From c42f2337e6e027d9c6c43d98868d947b25f756c6 Mon Sep 17 00:00:00 2001 From: evanbiederstedt Date: Fri, 3 Dec 2021 22:54:37 -0500 Subject: [PATCH 01/20] testthat (>= 3.1.0) --- DESCRIPTION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DESCRIPTION b/DESCRIPTION index 5ed5b1b..5a0290e 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -19,7 +19,7 @@ Imports: stats Suggests: pbapply, - testthat + testthat (>= 3.1.0) LinkingTo: Rcpp, RcppArmadillo, RcppEigen SystemRequirements: GNU make RoxygenNote: 7.1.2 From 32a0074bcef99471554b6e83292a84d15ef260ae Mon Sep 17 00:00:00 2001 From: evanbiederstedt Date: Tue, 5 Apr 2022 19:57:03 -0400 Subject: [PATCH 02/20] fix Makefile.win --- src/Makevars.win | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Makevars.win b/src/Makevars.win index aa9433b..435a73c 100644 --- a/src/Makevars.win +++ b/src/Makevars.win @@ -33,7 +33,7 @@ sublibs: sublibraries sublibraries: @for d in $(SUBDIRS); do \ - (cd $${d} && CFLAGS="$(CFLAGS)" CXXFLAGS="$(CXXFLAGS)" MAKE="$(MAKE) -f \"$(MkInclude)\" -f Makefile" $(MAKE) -f "$(MkInclude)" -f Makefile lib) || exit 1; \ + (cd $${d} && CFLAGS="$(CFLAGS)" CXXFLAGS="$(CXXFLAGS)" MAKE="$(MAKE) -f \"$(MkInclude)\" -f Makefile.win" $(MAKE) -f "$(MkInclude)" -f Makefile.win lib) || exit 1; \ done clean: subclean From 6fa451aa28525ff0b3970d899bff8f3dde26b045 Mon Sep 17 00:00:00 2001 From: evanbiederstedt Date: Tue, 5 Apr 2022 20:01:53 -0400 Subject: [PATCH 03/20] cannot find DLL, add #include --- src/leiden.cpp | 1 + src/leidenalg/CPMVertexPartition.cpp | 1 + src/leidenalg/GraphHelper.cpp | 1 + src/leidenalg/LinearResolutionParameterVertexPartition.cpp | 1 + src/leidenalg/ModularityVertexPartition.cpp | 1 + src/leidenalg/MutableVertexPartition.cpp | 1 + src/leidenalg/Optimiser.cpp | 1 + src/leidenalg/RBConfigurationVertexPartition.cpp | 1 + src/leidenalg/RBERVertexPartition.cpp | 1 + src/leidenalg/ResolutionParameterVertexPartition.cpp | 1 + src/leidenalg/SignificanceVertexPartition.cpp | 1 + src/leidenalg/SurpriseVertexPartition.cpp | 1 + src/leidenalg/include/GraphHelper.h | 2 ++ .../include/LinearResolutionParameterVertexPartition.h | 1 + src/leidenalg/include/ModularityVertexPartition.h | 1 + src/leidenalg/include/Optimiser.h | 1 + src/leidenalg/include/RBConfigurationVertexPartition.h | 1 + src/leidenalg/include/RBERVertexPartition.h | 2 +- src/leidenalg/include/ResolutionParameterVertexPartition.h | 1 + src/leidenalg/include/SignificanceVertexPartition.h | 2 +- src/leidenalg/include/SurpriseVertexPartition.h | 1 + 21 files changed, 22 insertions(+), 2 deletions(-) diff --git a/src/leiden.cpp b/src/leiden.cpp index 1440bf3..073f59c 100644 --- a/src/leiden.cpp +++ b/src/leiden.cpp @@ -7,6 +7,7 @@ #include "leidenalg/include/Optimiser.h" #include "leidenalg/include/RBERVertexPartition.h" #include "leidenalg/include/RBConfigurationVertexPartition.h" +#include using namespace std; using namespace Rcpp; diff --git a/src/leidenalg/CPMVertexPartition.cpp b/src/leidenalg/CPMVertexPartition.cpp index 57f08f7..a743d5d 100644 --- a/src/leidenalg/CPMVertexPartition.cpp +++ b/src/leidenalg/CPMVertexPartition.cpp @@ -1,4 +1,5 @@ #include "CPMVertexPartition.h" +#include CPMVertexPartition::CPMVertexPartition(Graph* graph, vector membership, double resolution_parameter) : diff --git a/src/leidenalg/GraphHelper.cpp b/src/leidenalg/GraphHelper.cpp index 4a9e45f..c48b428 100644 --- a/src/leidenalg/GraphHelper.cpp +++ b/src/leidenalg/GraphHelper.cpp @@ -1,4 +1,5 @@ #include "GraphHelper.h" +#include #ifdef DEBUG using std::cerr; diff --git a/src/leidenalg/LinearResolutionParameterVertexPartition.cpp b/src/leidenalg/LinearResolutionParameterVertexPartition.cpp index 12d5925..545326b 100644 --- a/src/leidenalg/LinearResolutionParameterVertexPartition.cpp +++ b/src/leidenalg/LinearResolutionParameterVertexPartition.cpp @@ -1,4 +1,5 @@ #include "LinearResolutionParameterVertexPartition.h" +#include LinearResolutionParameterVertexPartition::LinearResolutionParameterVertexPartition(Graph* graph, vector membership, double resolution_parameter) : diff --git a/src/leidenalg/ModularityVertexPartition.cpp b/src/leidenalg/ModularityVertexPartition.cpp index 26b32e8..7f817f5 100644 --- a/src/leidenalg/ModularityVertexPartition.cpp +++ b/src/leidenalg/ModularityVertexPartition.cpp @@ -1,4 +1,5 @@ #include "ModularityVertexPartition.h" +#include #ifdef DEBUG #include diff --git a/src/leidenalg/MutableVertexPartition.cpp b/src/leidenalg/MutableVertexPartition.cpp index 0cb69ce..c7fe617 100644 --- a/src/leidenalg/MutableVertexPartition.cpp +++ b/src/leidenalg/MutableVertexPartition.cpp @@ -1,4 +1,5 @@ #include "MutableVertexPartition.h" +#include #ifdef DEBUG using std::cerr; diff --git a/src/leidenalg/Optimiser.cpp b/src/leidenalg/Optimiser.cpp index d8dd0dc..1dea537 100644 --- a/src/leidenalg/Optimiser.cpp +++ b/src/leidenalg/Optimiser.cpp @@ -1,4 +1,5 @@ #include "Optimiser.h" +#include /**************************************************************************** Create a new Optimiser object diff --git a/src/leidenalg/RBConfigurationVertexPartition.cpp b/src/leidenalg/RBConfigurationVertexPartition.cpp index e2d6f2d..49f8f18 100644 --- a/src/leidenalg/RBConfigurationVertexPartition.cpp +++ b/src/leidenalg/RBConfigurationVertexPartition.cpp @@ -1,4 +1,5 @@ #include "RBConfigurationVertexPartition.h" +#include RBConfigurationVertexPartition::RBConfigurationVertexPartition(Graph* graph, vector const& membership, double resolution_parameter) : diff --git a/src/leidenalg/RBERVertexPartition.cpp b/src/leidenalg/RBERVertexPartition.cpp index 9350a08..6eb2cb5 100644 --- a/src/leidenalg/RBERVertexPartition.cpp +++ b/src/leidenalg/RBERVertexPartition.cpp @@ -1,4 +1,5 @@ #include "RBERVertexPartition.h" +#include RBERVertexPartition::RBERVertexPartition(Graph* graph, vector const& membership, double resolution_parameter) : diff --git a/src/leidenalg/ResolutionParameterVertexPartition.cpp b/src/leidenalg/ResolutionParameterVertexPartition.cpp index 56bb1e5..1abf0e2 100644 --- a/src/leidenalg/ResolutionParameterVertexPartition.cpp +++ b/src/leidenalg/ResolutionParameterVertexPartition.cpp @@ -1,4 +1,5 @@ #include "ResolutionParameterVertexPartition.h" +#include ResolutionParameterVertexPartition::ResolutionParameterVertexPartition(Graph* graph, vector membership, double resolution_parameter) : diff --git a/src/leidenalg/SignificanceVertexPartition.cpp b/src/leidenalg/SignificanceVertexPartition.cpp index 1dbd9fb..3287047 100644 --- a/src/leidenalg/SignificanceVertexPartition.cpp +++ b/src/leidenalg/SignificanceVertexPartition.cpp @@ -1,4 +1,5 @@ #include "SignificanceVertexPartition.h" +#include #ifdef DEBUG #include diff --git a/src/leidenalg/SurpriseVertexPartition.cpp b/src/leidenalg/SurpriseVertexPartition.cpp index 90a9ab8..7fbea14 100644 --- a/src/leidenalg/SurpriseVertexPartition.cpp +++ b/src/leidenalg/SurpriseVertexPartition.cpp @@ -1,4 +1,5 @@ #include "SurpriseVertexPartition.h" +#include SurpriseVertexPartition::SurpriseVertexPartition(Graph* graph, vector const& membership) : diff --git a/src/leidenalg/include/GraphHelper.h b/src/leidenalg/include/GraphHelper.h index fd44b62..5f56cc4 100644 --- a/src/leidenalg/include/GraphHelper.h +++ b/src/leidenalg/include/GraphHelper.h @@ -13,6 +13,8 @@ using std::endl; //#endif +#include + class MutableVertexPartition; using std::vector; diff --git a/src/leidenalg/include/LinearResolutionParameterVertexPartition.h b/src/leidenalg/include/LinearResolutionParameterVertexPartition.h index 751cf5b..13ce65c 100644 --- a/src/leidenalg/include/LinearResolutionParameterVertexPartition.h +++ b/src/leidenalg/include/LinearResolutionParameterVertexPartition.h @@ -2,6 +2,7 @@ #define LINEARRESOLUTIONPARAMETERVERTEXPARTITION_H #include +#include class LinearResolutionParameterVertexPartition : public ResolutionParameterVertexPartition { diff --git a/src/leidenalg/include/ModularityVertexPartition.h b/src/leidenalg/include/ModularityVertexPartition.h index 0157536..e975cd9 100644 --- a/src/leidenalg/include/ModularityVertexPartition.h +++ b/src/leidenalg/include/ModularityVertexPartition.h @@ -2,6 +2,7 @@ #define MODULARITYVERTEXPARTITION_H #include +#include class ModularityVertexPartition : public MutableVertexPartition { diff --git a/src/leidenalg/include/Optimiser.h b/src/leidenalg/include/Optimiser.h index b77fbe1..4d36e93 100644 --- a/src/leidenalg/include/Optimiser.h +++ b/src/leidenalg/include/Optimiser.h @@ -4,6 +4,7 @@ #include "MutableVertexPartition.h" #include #include +#include #include using std::cerr; diff --git a/src/leidenalg/include/RBConfigurationVertexPartition.h b/src/leidenalg/include/RBConfigurationVertexPartition.h index 4220f54..42abf75 100644 --- a/src/leidenalg/include/RBConfigurationVertexPartition.h +++ b/src/leidenalg/include/RBConfigurationVertexPartition.h @@ -2,6 +2,7 @@ #define RBCONFIGURATIONVERTEXPARTITION_H #include "LinearResolutionParameterVertexPartition.h" +#include class RBConfigurationVertexPartition : public LinearResolutionParameterVertexPartition { diff --git a/src/leidenalg/include/RBERVertexPartition.h b/src/leidenalg/include/RBERVertexPartition.h index e79c313..06c778e 100644 --- a/src/leidenalg/include/RBERVertexPartition.h +++ b/src/leidenalg/include/RBERVertexPartition.h @@ -2,7 +2,7 @@ #define RBERVERTEXPARTITION_H #include - +#include class RBERVertexPartition : public LinearResolutionParameterVertexPartition { diff --git a/src/leidenalg/include/ResolutionParameterVertexPartition.h b/src/leidenalg/include/ResolutionParameterVertexPartition.h index df886c7..8bd6f36 100644 --- a/src/leidenalg/include/ResolutionParameterVertexPartition.h +++ b/src/leidenalg/include/ResolutionParameterVertexPartition.h @@ -2,6 +2,7 @@ #define RESOLUTIONPARAMETERVERTEXPARTITION_H #include +#include class ResolutionParameterVertexPartition : public MutableVertexPartition { diff --git a/src/leidenalg/include/SignificanceVertexPartition.h b/src/leidenalg/include/SignificanceVertexPartition.h index dd93849..59c06f2 100644 --- a/src/leidenalg/include/SignificanceVertexPartition.h +++ b/src/leidenalg/include/SignificanceVertexPartition.h @@ -2,7 +2,7 @@ #define SIGNIFICANCEVERTEXPARTITION_H #include - +#include class SignificanceVertexPartition : public MutableVertexPartition { diff --git a/src/leidenalg/include/SurpriseVertexPartition.h b/src/leidenalg/include/SurpriseVertexPartition.h index 535caa4..ea1c14b 100644 --- a/src/leidenalg/include/SurpriseVertexPartition.h +++ b/src/leidenalg/include/SurpriseVertexPartition.h @@ -2,6 +2,7 @@ #define SURPRISEVERTEXPARTITION_H #include "MutableVertexPartition.h" +#include #include using std::cerr; using std::endl; From d64607a8f382230d11b03722af5046c9198981de Mon Sep 17 00:00:00 2001 From: evanbiederstedt Date: Tue, 5 Apr 2022 20:10:23 -0400 Subject: [PATCH 04/20] try explicit linkage --- src/leidenalg/Optimiser.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/leidenalg/Optimiser.cpp b/src/leidenalg/Optimiser.cpp index 1dea537..98cbd72 100644 --- a/src/leidenalg/Optimiser.cpp +++ b/src/leidenalg/Optimiser.cpp @@ -1,5 +1,5 @@ #include "Optimiser.h" -#include +#include "igraph-R/igraph.h" /**************************************************************************** Create a new Optimiser object From dc8f9770ab3fa5b2884153289440ff073b7815a2 Mon Sep 17 00:00:00 2001 From: evanbiederstedt Date: Tue, 5 Apr 2022 20:38:16 -0400 Subject: [PATCH 05/20] another linking --- src/leidenalg/Optimiser.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/leidenalg/Optimiser.cpp b/src/leidenalg/Optimiser.cpp index 98cbd72..17cf308 100644 --- a/src/leidenalg/Optimiser.cpp +++ b/src/leidenalg/Optimiser.cpp @@ -1,5 +1,6 @@ #include "Optimiser.h" #include "igraph-R/igraph.h" +#include /**************************************************************************** Create a new Optimiser object From 93931ce087c04bf4b325596192cf94e757e49043 Mon Sep 17 00:00:00 2001 From: evanbiederstedt Date: Tue, 5 Apr 2022 22:03:32 -0400 Subject: [PATCH 06/20] configure --- DESCRIPTION | 4 +- cleanup | 9 + cleanup.win | 9 + configure | 5660 ++++++ configure.ac | 181 + configure.win | 0 src/Makevars | 2 +- src/leidenalg/CPMVertexPartition.cpp | 2 +- src/leidenalg/GraphHelper.cpp | 4 +- ...nearResolutionParameterVertexPartition.cpp | 3 +- src/leidenalg/Makefile | 2 +- src/leidenalg/Makefile.win | 2 +- src/leidenalg/ModularityVertexPartition.cpp | 3 +- src/leidenalg/MutableVertexPartition.cpp | 3 +- src/leidenalg/Optimiser.cpp | 4 +- .../RBConfigurationVertexPartition.cpp | 3 +- src/leidenalg/RBERVertexPartition.cpp | 3 +- .../ResolutionParameterVertexPartition.cpp | 3 +- src/leidenalg/SignificanceVertexPartition.cpp | 3 +- src/leidenalg/SurpriseVertexPartition.cpp | 3 +- src/leidenalg/igraph-R/igraph_adjlist.h | 232 - src/leidenalg/igraph-R/igraph_array_pmt.h | 51 - src/leidenalg/igraph-R/igraph_attributes.h | 717 - src/leidenalg/igraph-R/igraph_bipartite.h | 91 - src/leidenalg/igraph-R/igraph_centrality.h | 183 - src/leidenalg/igraph-R/igraph_cliques.h | 66 - src/leidenalg/igraph-R/igraph_cocitation.h | 75 - src/leidenalg/igraph-R/igraph_community.h | 235 - src/leidenalg/igraph-R/igraph_complex.h | 111 - src/leidenalg/igraph-R/igraph_components.h | 69 - src/leidenalg/igraph-R/igraph_constants.h | 157 - src/leidenalg/igraph-R/igraph_constructors.h | 85 - src/leidenalg/igraph-R/igraph_dqueue_pmt.h | 49 - src/leidenalg/igraph-R/igraph_eigen.h | 118 - src/leidenalg/igraph-R/igraph_flow.h | 150 - src/leidenalg/igraph-R/igraph_foreign.h | 93 - src/leidenalg/igraph-R/igraph_games.h | 185 - src/leidenalg/igraph-R/igraph_heap_pmt.h | 45 - src/leidenalg/igraph-R/igraph_interface.h | 95 - src/leidenalg/igraph-R/igraph_lapack.h | 118 - src/leidenalg/igraph-R/igraph_layout.h | 248 - src/leidenalg/igraph-R/igraph_matching.h | 65 - src/leidenalg/igraph-R/igraph_matrix_pmt.h | 232 - .../igraph-R/igraph_microscopic_update.h | 69 - src/leidenalg/igraph-R/igraph_nongraph.h | 66 - src/leidenalg/igraph-R/igraph_operators.h | 65 - src/leidenalg/igraph-R/igraph_paths.h | 131 - src/leidenalg/igraph-R/igraph_pmt.h | 141 - src/leidenalg/igraph-R/igraph_random.h | 129 - src/leidenalg/igraph-R/igraph_revolver.h | 1200 -- src/leidenalg/igraph-R/igraph_scg.h | 135 - src/leidenalg/igraph-R/igraph_sparsemat.h | 264 - src/leidenalg/igraph-R/igraph_spmatrix.h | 123 - src/leidenalg/igraph-R/igraph_structural.h | 157 - src/leidenalg/igraph-R/igraph_strvector.h | 106 - src/leidenalg/igraph-R/igraph_topology.h | 268 - src/leidenalg/igraph-R/igraph_transitivity.h | 69 - src/leidenalg/igraph-R/igraph_vector.h | 108 - src/leidenalg/igraph-R/igraph_vector_pmt.h | 283 - src/leidenalg/igraph-R/igraph_vector_ptr.h | 105 - src/leidenalg/include/CPMVertexPartition.h | 1 + src/leidenalg/include/GraphHelper.h | 4 +- ...LinearResolutionParameterVertexPartition.h | 2 +- .../include/ModularityVertexPartition.h | 3 +- .../include/MutableVertexPartition.h | 2 + src/leidenalg/include/Optimiser.h | 4 +- .../include/RBConfigurationVertexPartition.h | 4 +- src/leidenalg/include/RBERVertexPartition.h | 4 +- .../ResolutionParameterVertexPartition.h | 4 +- .../include/SignificanceVertexPartition.h | 4 +- .../include/SurpriseVertexPartition.h | 5 +- src/rigraph/Makefile | 15 + src/rigraph/Makevars.in | 15 + src/rigraph/Makevars.ucrt | 11 + src/rigraph/Makevars.win | 10 + src/rigraph/config.h | 0 src/rigraph/config.h.in | 140 + src/rigraph/core/centrality/betweenness.c | 1016 + .../core/centrality/centrality_other.c | 1584 ++ src/rigraph/core/centrality/centralization.c | 658 + src/rigraph/core/centrality/closeness.c | 877 + src/rigraph/core/centrality/coreness.c | 164 + src/rigraph/core/centrality/prpack.cpp | 124 + src/rigraph/core/centrality/prpack/prpack.h | 11 + .../centrality/prpack/prpack_base_graph.cpp | 341 + .../centrality/prpack/prpack_base_graph.h | 42 + .../core/centrality/prpack/prpack_csc.h | 30 + .../core/centrality/prpack/prpack_csr.h | 16 + .../core/centrality/prpack/prpack_edge_list.h | 16 + .../centrality/prpack/prpack_igraph_graph.cpp | 147 + .../centrality/prpack/prpack_igraph_graph.h | 28 + .../prpack/prpack_preprocessed_ge_graph.cpp | 65 + .../prpack/prpack_preprocessed_ge_graph.h | 26 + .../prpack/prpack_preprocessed_graph.h | 17 + .../prpack/prpack_preprocessed_gs_graph.cpp | 80 + .../prpack/prpack_preprocessed_gs_graph.h | 30 + .../prpack/prpack_preprocessed_scc_graph.cpp | 201 + .../prpack/prpack_preprocessed_scc_graph.h | 39 + .../prpack_preprocessed_schur_graph.cpp | 120 + .../prpack/prpack_preprocessed_schur_graph.h | 33 + .../core/centrality/prpack/prpack_result.cpp | 11 + .../core/centrality/prpack/prpack_result.h | 29 + .../core/centrality/prpack/prpack_solver.cpp | 884 + .../core/centrality/prpack/prpack_solver.h | 178 + .../core/centrality/prpack/prpack_utils.cpp | 59 + .../core/centrality/prpack/prpack_utils.h | 33 + src/rigraph/core/centrality/prpack_internal.h | 44 + src/rigraph/core/cliques/cliquer/cliquer.c | 1768 ++ src/rigraph/core/cliques/cliquer/cliquer.h | 67 + .../core/cliques/cliquer/cliquer_graph.c | 765 + .../core/cliques/cliquer/cliquerconf.h | 68 + src/rigraph/core/cliques/cliquer/graph.h | 75 + src/rigraph/core/cliques/cliquer/misc.h | 59 + src/rigraph/core/cliques/cliquer/reorder.c | 424 + src/rigraph/core/cliques/cliquer/reorder.h | 26 + src/rigraph/core/cliques/cliquer/set.h | 386 + src/rigraph/core/cliques/cliquer_internal.h | 27 + src/rigraph/core/cliques/cliquer_wrapper.c | 407 + src/rigraph/core/cliques/cliques.c | 1139 ++ src/rigraph/core/cliques/glet.c | 867 + src/rigraph/core/cliques/maximal_cliques.c | 565 + .../core/cliques/maximal_cliques_template.h | 415 + src/rigraph/core/community/community_misc.c | 854 + src/rigraph/core/community/edge_betweenness.c | 750 + src/rigraph/core/community/fast_modularity.c | 1078 + src/rigraph/core/community/fluid.c | 268 + src/rigraph/core/community/infomap/infomap.cc | 325 + .../community/infomap/infomap_FlowGraph.cc | 420 + .../community/infomap/infomap_FlowGraph.h | 78 + .../core/community/infomap/infomap_Greedy.cc | 612 + .../core/community/infomap/infomap_Greedy.h | 85 + .../core/community/infomap/infomap_Node.cc | 71 + .../core/community/infomap/infomap_Node.h | 52 + .../core/community/label_propagation.c | 435 + .../core/community/leading_eigenvector.c | 1063 + src/rigraph/core/community/leiden.c | 1064 + src/rigraph/core/community/louvain.c | 723 + src/rigraph/core/community/modularity.c | 376 + .../core/community/optimal_modularity.c | 281 + .../core/community/spinglass/NetDataTypes.cpp | 218 + .../core/community/spinglass/NetDataTypes.h | 950 + .../core/community/spinglass/NetRoutines.cpp | 271 + .../core/community/spinglass/NetRoutines.h | 60 + .../core/community/spinglass/clustertool.cpp | 664 + .../core/community/spinglass/pottsmodel_2.cpp | 2236 +++ .../core/community/spinglass/pottsmodel_2.h | 179 + .../core/community/walktrap/walktrap.cpp | 177 + .../walktrap/walktrap_communities.cpp | 946 + .../community/walktrap/walktrap_communities.h | 175 + .../community/walktrap/walktrap_graph.cpp | 240 + .../core/community/walktrap/walktrap_graph.h | 104 + .../core/community/walktrap/walktrap_heap.cpp | 241 + .../core/community/walktrap/walktrap_heap.h | 133 + .../core/connectivity/cohesive_blocks.c | 602 + src/rigraph/core/connectivity/components.c | 1423 ++ src/rigraph/core/connectivity/separators.c | 853 + src/rigraph/core/constructors/adjacency.c | 681 + src/rigraph/core/constructors/atlas-edges.h | 1296 ++ src/rigraph/core/constructors/atlas.c | 82 + .../core/constructors/basic_constructors.c | 149 + src/rigraph/core/constructors/de_bruijn.c | 99 + src/rigraph/core/constructors/famous.c | 498 + src/rigraph/core/constructors/full.c | 154 + src/rigraph/core/constructors/kautz.c | 186 + src/rigraph/core/constructors/lcf.c | 141 + src/rigraph/core/constructors/linegraph.c | 164 + src/rigraph/core/constructors/prufer.c | 119 + src/rigraph/core/constructors/regular.c | 511 + src/rigraph/core/core/array.c | 50 + src/rigraph/core/core/array.pmt | 90 + src/rigraph/core/core/buckets.c | 196 + src/rigraph/core/core/buckets.h | 81 + src/rigraph/core/core/cutheap.c | 170 + src/rigraph/core/core/cutheap.h | 63 + src/rigraph/core/core/dqueue.c | 55 + src/rigraph/core/core/dqueue.pmt | 383 + src/rigraph/core/core/error.c | 385 + src/rigraph/core/core/estack.c | 67 + src/rigraph/core/core/estack.h | 47 + src/rigraph/core/core/exceptions.h | 19 + src/rigraph/core/core/fixed_vectorlist.c | 81 + .../core/core/fixed_vectorlist.h} | 51 +- src/rigraph/core/core/grid.c | 348 + src/rigraph/core/core/grid.h | 80 + src/rigraph/core/core/heap.c | 64 + src/rigraph/core/core/heap.pmt | 355 + src/rigraph/core/core/indheap.c | 953 + src/rigraph/core/core/indheap.h | 140 + src/rigraph/core/core/interruption.c | 42 + src/rigraph/core/core/interruption.h | 59 + src/rigraph/core/core/marked_queue.c | 115 + src/rigraph/core/core/marked_queue.h | 70 + src/rigraph/core/core/math.h | 88 + src/rigraph/core/core/matrix.c | 158 + src/rigraph/core/core/matrix.pmt | 1641 ++ src/rigraph/core/core/memory.c | 73 + src/rigraph/core/core/printing.c | 146 + src/rigraph/core/core/progress.c | 154 + src/rigraph/core/core/psumtree.c | 253 + src/rigraph/core/core/set.c | 320 + src/rigraph/core/core/set.h | 65 + src/rigraph/core/core/sparsemat.c | 3273 +++ src/rigraph/core/core/spmatrix.c | 1066 + src/rigraph/core/core/stack.c | 88 + src/rigraph/core/core/stack.pmt | 293 + src/rigraph/core/core/statusbar.c | 130 + src/rigraph/core/core/strvector.c | 603 + src/rigraph/core/core/trie.c | 395 + src/rigraph/core/core/trie.h | 72 + src/rigraph/core/core/vector.c | 534 + src/rigraph/core/core/vector.pmt | 2946 +++ src/rigraph/core/core/vector_ptr.c | 639 + src/rigraph/core/f2c.h | 238 + src/rigraph/core/flow/flow.c | 2544 +++ src/rigraph/core/flow/flow_internal.h | 41 + src/rigraph/core/flow/st-cuts.c | 1592 ++ src/rigraph/core/games/barabasi.c | 764 + src/rigraph/core/games/callaway_traits.c | 200 + src/rigraph/core/games/citations.c | 480 + src/rigraph/core/games/correlated.c | 326 + src/rigraph/core/games/degree_sequence.c | 775 + .../degree_sequence_vl/gengraph_box_list.cpp | 110 + .../degree_sequence_vl/gengraph_box_list.h | 83 + .../degree_sequence_vl/gengraph_definitions.h | 201 + .../gengraph_degree_sequence.cpp | 421 + .../gengraph_degree_sequence.h | 100 + .../gengraph_graph_molloy_hash.cpp | 1172 ++ .../gengraph_graph_molloy_hash.h | 218 + .../gengraph_graph_molloy_optimized.cpp | 2231 ++ .../gengraph_graph_molloy_optimized.h | 286 + .../games/degree_sequence_vl/gengraph_hash.h | 308 + .../degree_sequence_vl/gengraph_header.h | 109 + .../gengraph_mr-connected.cpp | 192 + .../degree_sequence_vl/gengraph_powerlaw.cpp | 272 + .../degree_sequence_vl/gengraph_powerlaw.h | 86 + .../games/degree_sequence_vl/gengraph_qsort.h | 564 + .../degree_sequence_vl/gengraph_random.cpp | 277 + .../degree_sequence_vl/gengraph_random.h | 213 + .../gengraph_vertex_cover.h | 72 + src/rigraph/core/games/dotproduct.c | 280 + src/rigraph/core/games/erdos_renyi.c | 285 + src/rigraph/core/games/establishment.c | 186 + src/rigraph/core/games/forestfire.c | 263 + src/rigraph/core/games/grg.c | 166 + src/rigraph/core/games/growing_random.c | 103 + src/rigraph/core/games/islands.c | 158 + src/rigraph/core/games/k_regular.c | 85 + src/rigraph/core/games/preference.c | 631 + src/rigraph/core/games/recent_degree.c | 372 + src/rigraph/core/games/sbm.c | 621 + src/rigraph/core/games/static_fitness.c | 436 + src/rigraph/core/games/tree.c | 196 + src/rigraph/core/games/watts_strogatz.c | 102 + src/rigraph/core/graph/adjlist.c | 1192 ++ src/rigraph/core/graph/attributes.c | 498 + src/rigraph/core/graph/attributes.h | 112 + src/rigraph/core/graph/basic_query.c | 63 + src/rigraph/core/graph/cattributes.c | 4241 ++++ src/rigraph/core/graph/iterators.c | 1932 ++ src/rigraph/core/graph/neighbors.h | 42 + src/rigraph/core/graph/type_indexededgelist.c | 1964 ++ src/rigraph/core/graph/visitors.c | 648 + src/rigraph/core/hrg/dendro.h | 313 + src/rigraph/core/hrg/graph.h | 167 + src/rigraph/core/hrg/graph_simp.h | 160 + src/rigraph/core/hrg/hrg.cc | 1071 + src/rigraph/core/hrg/hrg_types.cc | 3725 ++++ src/rigraph/core/hrg/rbtree.h | 160 + src/rigraph/core/hrg/splittree_eq.h | 183 + src/rigraph/core/internal/glpk_support.c | 170 + src/rigraph/core/internal/glpk_support.h | 141 + src/rigraph/core/internal/gmp_internal.h | 34 + src/rigraph/core/internal/hacks.c | 53 + .../core/internal/hacks.h} | 48 +- src/rigraph/core/internal/lsap.c | 672 + src/rigraph/core/internal/pstdint.h | 817 + src/rigraph/core/internal/qsort.c | 262 + src/rigraph/core/internal/qsort_r.c | 8 + src/rigraph/core/internal/zeroin.c | 204 + src/rigraph/core/io/dimacs.c | 318 + src/rigraph/core/io/dl-header.h | 44 + src/rigraph/core/io/dl-lexer.c | 2478 +++ src/rigraph/core/io/dl-lexer.h | 737 + src/rigraph/core/io/dl-lexer.l | 137 + src/rigraph/core/io/dl-parser.c | 2237 +++ src/rigraph/core/io/dl-parser.h | 104 + src/rigraph/core/io/dl-parser.y | 306 + src/rigraph/core/io/dl.c | 175 + src/rigraph/core/io/dot.c | 319 + src/rigraph/core/io/edgelist.c | 159 + src/rigraph/core/io/gml-header.h | 44 + src/rigraph/core/io/gml-lexer.c | 2319 +++ src/rigraph/core/io/gml-lexer.h | 736 + src/rigraph/core/io/gml-lexer.l | 112 + src/rigraph/core/io/gml-parser.c | 1987 ++ src/rigraph/core/io/gml-parser.h | 101 + src/rigraph/core/io/gml-parser.y | 297 + src/rigraph/core/io/gml-tree.c | 260 + src/rigraph/core/io/gml-tree.h | 82 + src/rigraph/core/io/gml.c | 794 + src/rigraph/core/io/graphdb.c | 118 + src/rigraph/core/io/graphml.c | 1861 ++ src/rigraph/core/io/leda.c | 258 + src/rigraph/core/io/lgl-header.h | 37 + src/rigraph/core/io/lgl-lexer.c | 2265 +++ src/rigraph/core/io/lgl-lexer.h | 736 + src/rigraph/core/io/lgl-lexer.l | 100 + src/rigraph/core/io/lgl-parser.c | 1773 ++ src/rigraph/core/io/lgl-parser.h | 94 + src/rigraph/core/io/lgl-parser.y | 145 + src/rigraph/core/io/lgl.c | 413 + src/rigraph/core/io/ncol-header.h | 36 + src/rigraph/core/io/ncol-lexer.c | 2260 +++ src/rigraph/core/io/ncol-lexer.h | 736 + src/rigraph/core/io/ncol-lexer.l | 98 + src/rigraph/core/io/ncol-parser.c | 1764 ++ src/rigraph/core/io/ncol-parser.h | 93 + src/rigraph/core/io/ncol-parser.y | 139 + src/rigraph/core/io/ncol.c | 383 + src/rigraph/core/io/pajek-header.h | 46 + src/rigraph/core/io/pajek-lexer.c | 2752 +++ src/rigraph/core/io/pajek-lexer.h | 736 + src/rigraph/core/io/pajek-lexer.l | 147 + src/rigraph/core/io/pajek-parser.c | 2882 +++ src/rigraph/core/io/pajek-parser.h | 143 + src/rigraph/core/io/pajek-parser.y | 752 + src/rigraph/core/io/pajek.c | 771 + src/rigraph/core/isomorphism/bliss.cc | 608 + src/rigraph/core/isomorphism/bliss/bignum.hh | 103 + src/rigraph/core/isomorphism/bliss/defs.cc | 32 + src/rigraph/core/isomorphism/bliss/defs.hh | 90 + src/rigraph/core/isomorphism/bliss/graph.cc | 5035 +++++ src/rigraph/core/isomorphism/bliss/graph.hh | 872 + src/rigraph/core/isomorphism/bliss/heap.cc | 111 + src/rigraph/core/isomorphism/bliss/heap.hh | 89 + .../core/isomorphism/bliss/igraph-changes.md | 34 + src/rigraph/core/isomorphism/bliss/kqueue.hh | 168 + src/rigraph/core/isomorphism/bliss/kstack.hh | 145 + src/rigraph/core/isomorphism/bliss/orbit.cc | 151 + src/rigraph/core/isomorphism/bliss/orbit.hh | 112 + .../core/isomorphism/bliss/partition.cc | 1127 ++ .../core/isomorphism/bliss/partition.hh | 299 + src/rigraph/core/isomorphism/bliss/stats.hh | 87 + .../core/isomorphism/bliss/uintseqhash.cc | 117 + .../core/isomorphism/bliss/uintseqhash.hh | 63 + src/rigraph/core/isomorphism/bliss/utils.cc | 60 + src/rigraph/core/isomorphism/bliss/utils.hh | 46 + src/rigraph/core/isomorphism/isoclasses.c | 2876 +++ .../core/isomorphism/isoclasses.h} | 49 +- .../core/isomorphism/isomorphism_misc.c | 115 + src/rigraph/core/isomorphism/lad.c | 1674 ++ src/rigraph/core/isomorphism/queries.c | 184 + src/rigraph/core/isomorphism/vf2.c | 1757 ++ src/rigraph/core/layout/circular.c | 185 + src/rigraph/core/layout/davidson_harel.c | 464 + src/rigraph/core/layout/drl/DensityGrid.cpp | 281 + src/rigraph/core/layout/drl/DensityGrid.h | 84 + .../core/layout/drl/DensityGrid_3d.cpp | 305 + src/rigraph/core/layout/drl/DensityGrid_3d.h | 84 + src/rigraph/core/layout/drl/drl_Node.h | 68 + src/rigraph/core/layout/drl/drl_Node_3d.h | 68 + src/rigraph/core/layout/drl/drl_graph.cpp | 1306 ++ src/rigraph/core/layout/drl/drl_graph.h | 132 + src/rigraph/core/layout/drl/drl_graph_3d.cpp | 873 + src/rigraph/core/layout/drl/drl_graph_3d.h | 124 + src/rigraph/core/layout/drl/drl_layout.cpp | 495 + src/rigraph/core/layout/drl/drl_layout.h | 65 + src/rigraph/core/layout/drl/drl_layout_3d.cpp | 121 + src/rigraph/core/layout/drl/drl_layout_3d.h | 65 + src/rigraph/core/layout/drl/drl_parse.cpp | 197 + src/rigraph/core/layout/drl/drl_parse.h | 72 + .../core/layout/fruchterman_reingold.c | 709 + src/rigraph/core/layout/gem.c | 250 + src/rigraph/core/layout/graphopt.c | 444 + src/rigraph/core/layout/kamada_kawai.c | 718 + src/rigraph/core/layout/large_graph.c | 349 + src/rigraph/core/layout/layout_bipartite.c | 81 + src/rigraph/core/layout/layout_grid.c | 113 + src/rigraph/core/layout/layout_internal.h | 55 + src/rigraph/core/layout/layout_random.c | 98 + src/rigraph/core/layout/mds.c | 305 + src/rigraph/core/layout/merge_dla.c | 295 + src/rigraph/core/layout/merge_grid.c | 217 + src/rigraph/core/layout/merge_grid.h | 58 + src/rigraph/core/layout/reingold_tilford.c | 965 + src/rigraph/core/layout/sugiyama.c | 1342 ++ src/rigraph/core/linalg/arpack.c | 1438 ++ src/rigraph/core/linalg/arpack_internal.h | 229 + src/rigraph/core/linalg/blas.c | 157 + src/rigraph/core/linalg/blas_internal.h | 90 + src/rigraph/core/linalg/eigen.c | 1550 ++ src/rigraph/core/linalg/lapack.c | 954 + src/rigraph/core/linalg/lapack_internal.h | 183 + src/rigraph/core/math/bfgs.c | 221 + src/rigraph/core/math/complex.c | 391 + src/rigraph/core/math/utils.c | 339 + src/rigraph/core/misc/bipartite.c | 1148 ++ src/rigraph/core/misc/chordality.c | 487 + src/rigraph/core/misc/cocitation.c | 779 + src/rigraph/core/misc/coloring.c | 161 + src/rigraph/core/misc/conversion.c | 993 + src/rigraph/core/misc/conversion_internal.h | 28 + src/rigraph/core/misc/degree_sequence.cpp | 785 + src/rigraph/core/misc/embedding.c | 1176 ++ src/rigraph/core/misc/feedback_arc_set.c | 661 + src/rigraph/core/misc/feedback_arc_set.h | 39 + src/rigraph/core/misc/graphicality.c | 959 + src/rigraph/core/misc/matching.c | 1033 + src/rigraph/core/misc/microscopic_update.c | 1209 ++ src/rigraph/core/misc/mixing.c | 307 + src/rigraph/core/misc/motifs.c | 1218 ++ src/rigraph/core/misc/other.c | 434 + src/rigraph/core/misc/scan.c | 879 + src/rigraph/core/misc/sir.c | 262 + src/rigraph/core/misc/spanning_trees.c | 513 + src/rigraph/core/operators/add_edge.c | 64 + src/rigraph/core/operators/complementer.c | 104 + src/rigraph/core/operators/compose.c | 135 + .../core/operators/connect_neighborhood.c | 163 + src/rigraph/core/operators/contract.c | 167 + src/rigraph/core/operators/difference.c | 183 + src/rigraph/core/operators/disjoint_union.c | 176 + src/rigraph/core/operators/intersection.c | 296 + src/rigraph/core/operators/misc_internal.c | 259 + src/rigraph/core/operators/misc_internal.h | 46 + src/rigraph/core/operators/permute.c | 96 + src/rigraph/core/operators/rewire.c | 268 + src/rigraph/core/operators/rewire_edges.c | 394 + src/rigraph/core/operators/rewire_internal.h | 8 + src/rigraph/core/operators/simplify.c | 179 + src/rigraph/core/operators/subgraph.c | 477 + src/rigraph/core/operators/subgraph.h | 35 + src/rigraph/core/operators/union.c | 261 + src/rigraph/core/paths/all_shortest_paths.c | 318 + src/rigraph/core/paths/bellman_ford.c | 570 + src/rigraph/core/paths/dijkstra.c | 1068 + src/rigraph/core/paths/distances.c | 214 + src/rigraph/core/paths/eulerian.c | 676 + src/rigraph/core/paths/histogram.c | 149 + src/rigraph/core/paths/johnson.c | 207 + src/rigraph/core/paths/random_walk.c | 294 + src/rigraph/core/paths/shortest_paths.c | 1270 ++ src/rigraph/core/paths/simple_paths.c | 179 + src/rigraph/core/paths/unweighted.c | 540 + .../core/properties/basic_properties.c | 331 + src/rigraph/core/properties/constraint.c | 306 + .../core/properties/convergence_degree.c | 208 + src/rigraph/core/properties/dag.c | 296 + src/rigraph/core/properties/degrees.c | 500 + src/rigraph/core/properties/girth.c | 208 + src/rigraph/core/properties/loops.c | 95 + src/rigraph/core/properties/multiplicity.c | 356 + src/rigraph/core/properties/neighborhood.c | 454 + .../core/properties/properties_internal.h | 31 + src/rigraph/core/properties/spectral.c | 436 + src/rigraph/core/properties/trees.c | 351 + src/rigraph/core/properties/triangles.c | 925 + .../core/properties/triangles_template.h | 143 + .../core/properties/triangles_template1.h | 95 + src/rigraph/core/random/random.c | 2284 +++ src/rigraph/core/scg/scg.c | 2303 +++ .../core/scg/scg_approximate_methods.c | 173 + src/rigraph/core/scg/scg_exact_scg.c | 69 + src/rigraph/core/scg/scg_headers.h | 128 + src/rigraph/core/scg/scg_kmeans.c | 102 + src/rigraph/core/scg/scg_optimal_method.c | 241 + src/rigraph/core/scg/scg_utils.c | 94 + src/rigraph/core/version.c | 67 + src/rigraph/igraph-win.def | 3 + .../igraph-R => rigraph/include}/igraph.h | 21 +- src/rigraph/include/igraph_adjlist.h | 189 + .../include}/igraph_arpack.h | 139 +- .../include}/igraph_array.h | 20 +- src/rigraph/include/igraph_array_pmt.h | 51 + src/rigraph/include/igraph_attributes.h | 826 + src/rigraph/include/igraph_bipartite.h | 97 + .../include}/igraph_blas.h | 44 +- src/rigraph/include/igraph_centrality.h | 204 + src/rigraph/include/igraph_cliques.h | 114 + src/rigraph/include/igraph_cocitation.h | 66 + .../include}/igraph_cohesive_blocks.h | 25 +- src/rigraph/include/igraph_coloring.h | 46 + src/rigraph/include/igraph_community.h | 253 + src/rigraph/include/igraph_complex.h | 104 + src/rigraph/include/igraph_components.h | 61 + src/rigraph/include/igraph_constants.h | 203 + src/rigraph/include/igraph_constructors.h | 80 + .../include}/igraph_conversion.h | 54 +- .../include}/igraph_datatype.h | 62 +- src/rigraph/include/igraph_decls.h | 19 + .../include}/igraph_dqueue.h | 31 +- src/rigraph/include/igraph_dqueue_pmt.h | 49 + src/rigraph/include/igraph_eigen.h | 111 + src/rigraph/include/igraph_embedding.h | 68 + src/rigraph/include/igraph_epidemics.h | 67 + .../include}/igraph_error.h | 730 +- src/rigraph/include/igraph_eulerian.h | 38 + src/rigraph/include/igraph_export.h | 11 + src/rigraph/include/igraph_flow.h | 158 + src/rigraph/include/igraph_foreign.h | 85 + src/rigraph/include/igraph_games.h | 223 + src/rigraph/include/igraph_graphicality.h | 65 + src/rigraph/include/igraph_graphlets.h | 52 + .../include}/igraph_heap.h | 20 +- src/rigraph/include/igraph_heap_pmt.h | 39 + .../igraph-R => rigraph/include}/igraph_hrg.h | 112 +- src/rigraph/include/igraph_interface.h | 124 + .../include}/igraph_interrupt.h | 34 +- .../include}/igraph_iterators.h | 292 +- src/rigraph/include/igraph_lapack.h | 111 + src/rigraph/include/igraph_layout.h | 258 + src/rigraph/include/igraph_lsap.h | 17 + src/rigraph/include/igraph_matching.h | 56 + .../include}/igraph_matrix.h | 37 +- src/rigraph/include/igraph_matrix_pmt.h | 244 + src/rigraph/include/igraph_memory.h | 45 + .../include/igraph_microscopic_update.h | 60 + src/rigraph/include/igraph_mixing.h | 51 + .../include}/igraph_motifs.h | 81 +- src/rigraph/include/igraph_neighborhood.h | 47 + src/rigraph/include/igraph_nongraph.h | 92 + src/rigraph/include/igraph_operators.h | 87 + src/rigraph/include/igraph_paths.h | 176 + src/rigraph/include/igraph_pmt.h | 144 + .../include}/igraph_pmt_off.h | 80 +- .../include}/igraph_progress.h | 134 +- .../include}/igraph_psumtree.h | 47 +- .../include}/igraph_qsort.h | 30 +- src/rigraph/include/igraph_random.h | 134 + src/rigraph/include/igraph_scan.h | 69 + src/rigraph/include/igraph_scg.h | 143 + .../include}/igraph_separators.h | 41 +- src/rigraph/include/igraph_sparsemat.h | 288 + src/rigraph/include/igraph_spmatrix.h | 109 + .../include}/igraph_stack.h | 31 +- .../include}/igraph_stack_pmt.h | 38 +- .../include}/igraph_statusbar.h | 107 +- src/rigraph/include/igraph_structural.h | 128 + src/rigraph/include/igraph_strvector.h | 97 + .../include}/igraph_threading.h | 36 +- src/rigraph/include/igraph_threading.h.in | 47 + src/rigraph/include/igraph_topology.h | 285 + src/rigraph/include/igraph_transitivity.h | 63 + .../include}/igraph_types.h | 81 +- src/rigraph/include/igraph_vector.h | 183 + src/rigraph/include/igraph_vector_pmt.h | 290 + src/rigraph/include/igraph_vector_ptr.h | 100 + .../include/igraph_vector_type.h} | 33 +- src/rigraph/include/igraph_version.h | 44 + src/rigraph/include/igraph_version.h.in | 44 + .../include}/igraph_visitor.h | 95 +- src/rigraph/init.c | 803 + src/rigraph/lazyeval.c | 172 + src/rigraph/rinterface.c | 16754 ++++++++++++++++ src/rigraph/rinterface.h | 29 + src/rigraph/rinterface_extra.c | 334 + src/rigraph/rrandom.c | 111 + src/rigraph/rrandom.h | 28 + src/rigraph/vendor/arpack/debug.h | 16 + src/rigraph/vendor/arpack/dgetv0.f | 419 + src/rigraph/vendor/arpack/dlaqrb.f | 521 + src/rigraph/vendor/arpack/dmout.f | 167 + src/rigraph/vendor/arpack/dnaitr.f | 840 + src/rigraph/vendor/arpack/dnapps.f | 647 + src/rigraph/vendor/arpack/dnaup2.f | 838 + src/rigraph/vendor/arpack/dnaupd.f | 655 + src/rigraph/vendor/arpack/dnconv.f | 146 + src/rigraph/vendor/arpack/dneigh.f | 315 + src/rigraph/vendor/arpack/dneupd.f | 1044 + src/rigraph/vendor/arpack/dngets.f | 231 + src/rigraph/vendor/arpack/dsaitr.f | 854 + src/rigraph/vendor/arpack/dsapps.f | 516 + src/rigraph/vendor/arpack/dsaup2.f | 853 + src/rigraph/vendor/arpack/dsaupd.f | 653 + src/rigraph/vendor/arpack/dsconv.f | 138 + src/rigraph/vendor/arpack/dseigt.f | 181 + src/rigraph/vendor/arpack/dsesrt.f | 217 + src/rigraph/vendor/arpack/dseupd.f | 905 + src/rigraph/vendor/arpack/dsgets.f | 220 + src/rigraph/vendor/arpack/dsortc.f | 344 + src/rigraph/vendor/arpack/dsortr.f | 218 + src/rigraph/vendor/arpack/dstatn.f | 61 + src/rigraph/vendor/arpack/dstats.f | 47 + src/rigraph/vendor/arpack/dstqrb.f | 594 + src/rigraph/vendor/arpack/dvout.f | 122 + src/rigraph/vendor/arpack/ivout.f | 120 + src/rigraph/vendor/arpack/second.f | 35 + src/rigraph/vendor/arpack/stat.h | 21 + src/rigraph/vendor/arpack/wrap.f | 151 + src/rigraph/vendor/cs/SuiteSparse_config.h | 221 + src/rigraph/vendor/cs/cs.h | 758 + src/rigraph/vendor/cs/cs_add.c | 28 + src/rigraph/vendor/cs/cs_amd.c | 364 + src/rigraph/vendor/cs/cs_chol.c | 59 + src/rigraph/vendor/cs/cs_cholsol.c | 26 + src/rigraph/vendor/cs/cs_compress.c | 22 + src/rigraph/vendor/cs/cs_counts.c | 61 + src/rigraph/vendor/cs/cs_cumsum.c | 17 + src/rigraph/vendor/cs/cs_dfs.c | 36 + src/rigraph/vendor/cs/cs_dmperm.c | 144 + src/rigraph/vendor/cs/cs_droptol.c | 9 + src/rigraph/vendor/cs/cs_dropzeros.c | 9 + src/rigraph/vendor/cs/cs_dupl.c | 34 + src/rigraph/vendor/cs/cs_entry.c | 13 + src/rigraph/vendor/cs/cs_ereach.c | 23 + src/rigraph/vendor/cs/cs_etree.c | 30 + src/rigraph/vendor/cs/cs_fkeep.c | 25 + src/rigraph/vendor/cs/cs_gaxpy.c | 17 + src/rigraph/vendor/cs/cs_happly.c | 19 + src/rigraph/vendor/cs/cs_house.c | 30 + src/rigraph/vendor/cs/cs_ipvec.c | 9 + src/rigraph/vendor/cs/cs_leaf.c | 22 + src/rigraph/vendor/cs/cs_load.c | 26 + src/rigraph/vendor/cs/cs_lsolve.c | 18 + src/rigraph/vendor/cs/cs_ltsolve.c | 18 + src/rigraph/vendor/cs/cs_lu.c | 88 + src/rigraph/vendor/cs/cs_lusol.c | 26 + src/rigraph/vendor/cs/cs_malloc.c | 35 + src/rigraph/vendor/cs/cs_maxtrans.c | 92 + src/rigraph/vendor/cs/cs_multiply.c | 35 + src/rigraph/vendor/cs/cs_norm.c | 16 + src/rigraph/vendor/cs/cs_permute.c | 25 + src/rigraph/vendor/cs/cs_pinv.c | 11 + src/rigraph/vendor/cs/cs_post.c | 24 + src/rigraph/vendor/cs/cs_print.c | 55 + src/rigraph/vendor/cs/cs_pvec.c | 9 + src/rigraph/vendor/cs/cs_qr.c | 74 + src/rigraph/vendor/cs/cs_qrsol.c | 53 + src/rigraph/vendor/cs/cs_randperm.c | 28 + src/rigraph/vendor/cs/cs_reach.c | 19 + src/rigraph/vendor/cs/cs_scatter.c | 22 + src/rigraph/vendor/cs/cs_scc.c | 41 + src/rigraph/vendor/cs/cs_schol.c | 26 + src/rigraph/vendor/cs/cs_spsolve.c | 28 + src/rigraph/vendor/cs/cs_sqr.c | 87 + src/rigraph/vendor/cs/cs_symperm.c | 39 + src/rigraph/vendor/cs/cs_tdfs.c | 24 + src/rigraph/vendor/cs/cs_transpose.c | 25 + src/rigraph/vendor/cs/cs_updown.c | 48 + src/rigraph/vendor/cs/cs_usolve.c | 18 + src/rigraph/vendor/cs/cs_util.c | 120 + src/rigraph/vendor/cs/cs_utsolve.c | 18 + src/rigraph/vendor/mini-gmp/mini-gmp.c | 4578 +++++ src/rigraph/vendor/mini-gmp/mini-gmp.h | 305 + src/rigraph/vendor/plfit/arithmetic_ansi.h | 133 + .../vendor/plfit/arithmetic_sse_double.h | 294 + .../vendor/plfit/arithmetic_sse_float.h | 291 + src/rigraph/vendor/plfit/gss.c | 153 + src/rigraph/vendor/plfit/gss.h | 146 + src/rigraph/vendor/plfit/hzeta.c | 651 + src/rigraph/vendor/plfit/hzeta.h | 96 + src/rigraph/vendor/plfit/kolmogorov.c | 66 + src/rigraph/vendor/plfit/kolmogorov.h | 43 + src/rigraph/vendor/plfit/lbfgs.c | 1378 ++ src/rigraph/vendor/plfit/lbfgs.h | 736 + src/rigraph/vendor/plfit/mt.c | 93 + src/rigraph/vendor/plfit/options.c | 52 + src/rigraph/vendor/plfit/platform.c | 36 + src/rigraph/vendor/plfit/platform.h | 69 + src/rigraph/vendor/plfit/plfit.c | 1342 ++ src/rigraph/vendor/plfit/plfit.h | 140 + src/rigraph/vendor/plfit/plfit_error.c | 66 + src/rigraph/vendor/plfit/plfit_error.h | 86 + src/rigraph/vendor/plfit/plfit_mt.h | 101 + src/rigraph/vendor/plfit/plfit_sampling.h | 177 + src/rigraph/vendor/plfit/plfit_version.h | 28 + src/rigraph/vendor/plfit/rbinom.c | 208 + src/rigraph/vendor/plfit/sampling.c | 312 + src/rigraph/vendor/simpleraytracer/Color.cpp | 96 + src/rigraph/vendor/simpleraytracer/Color.h | 40 + src/rigraph/vendor/simpleraytracer/Light.cpp | 46 + src/rigraph/vendor/simpleraytracer/Light.h | 39 + src/rigraph/vendor/simpleraytracer/Point.cpp | 106 + src/rigraph/vendor/simpleraytracer/Point.h | 45 + .../vendor/simpleraytracer/RIgraphRay.cpp | 93 + src/rigraph/vendor/simpleraytracer/Ray.cpp | 44 + src/rigraph/vendor/simpleraytracer/Ray.h | 33 + .../vendor/simpleraytracer/RayTracer.cpp | 266 + .../vendor/simpleraytracer/RayTracer.h | 63 + .../vendor/simpleraytracer/RayVector.cpp | 128 + .../vendor/simpleraytracer/RayVector.h | 49 + src/rigraph/vendor/simpleraytracer/Shape.cpp | 106 + src/rigraph/vendor/simpleraytracer/Shape.h | 65 + src/rigraph/vendor/simpleraytracer/Sphere.cpp | 70 + src/rigraph/vendor/simpleraytracer/Sphere.h | 31 + .../vendor/simpleraytracer/Triangle.cpp | 90 + src/rigraph/vendor/simpleraytracer/Triangle.h | 27 + .../vendor/simpleraytracer/unit_limiter.cpp | 15 + .../vendor/simpleraytracer/unit_limiter.h | 10 + src/rigraph/vendor/uuid/COPYING | 28 + src/rigraph/vendor/uuid/Makevars.in | 2 + src/rigraph/vendor/uuid/Makevars.win | 1 + src/rigraph/vendor/uuid/R.c | 25 + src/rigraph/vendor/uuid/clear.c | 43 + src/rigraph/vendor/uuid/compare.c | 55 + src/rigraph/vendor/uuid/config.h.in | 82 + src/rigraph/vendor/uuid/copy.c | 45 + src/rigraph/vendor/uuid/gen_uuid.c | 536 + src/rigraph/vendor/uuid/isnull.c | 48 + src/rigraph/vendor/uuid/pack.c | 69 + src/rigraph/vendor/uuid/parse.c | 79 + src/rigraph/vendor/uuid/unpack.c | 63 + src/rigraph/vendor/uuid/unparse.c | 76 + src/rigraph/vendor/uuid/uuid.h | 104 + src/rigraph/vendor/uuid/uuidP.h | 63 + src/rigraph/vendor/uuid/uuidd.h | 54 + src/rigraph/vendor/uuid/win32/config.h | 84 + 707 files changed, 251559 insertions(+), 8156 deletions(-) create mode 100755 cleanup create mode 100644 cleanup.win create mode 100755 configure create mode 100644 configure.ac create mode 100644 configure.win delete mode 100644 src/leidenalg/igraph-R/igraph_adjlist.h delete mode 100644 src/leidenalg/igraph-R/igraph_array_pmt.h delete mode 100644 src/leidenalg/igraph-R/igraph_attributes.h delete mode 100644 src/leidenalg/igraph-R/igraph_bipartite.h delete mode 100644 src/leidenalg/igraph-R/igraph_centrality.h delete mode 100644 src/leidenalg/igraph-R/igraph_cliques.h delete mode 100644 src/leidenalg/igraph-R/igraph_cocitation.h delete mode 100644 src/leidenalg/igraph-R/igraph_community.h delete mode 100644 src/leidenalg/igraph-R/igraph_complex.h delete mode 100644 src/leidenalg/igraph-R/igraph_components.h delete mode 100644 src/leidenalg/igraph-R/igraph_constants.h delete mode 100644 src/leidenalg/igraph-R/igraph_constructors.h delete mode 100644 src/leidenalg/igraph-R/igraph_dqueue_pmt.h delete mode 100644 src/leidenalg/igraph-R/igraph_eigen.h delete mode 100644 src/leidenalg/igraph-R/igraph_flow.h delete mode 100644 src/leidenalg/igraph-R/igraph_foreign.h delete mode 100644 src/leidenalg/igraph-R/igraph_games.h delete mode 100644 src/leidenalg/igraph-R/igraph_heap_pmt.h delete mode 100644 src/leidenalg/igraph-R/igraph_interface.h delete mode 100644 src/leidenalg/igraph-R/igraph_lapack.h delete mode 100644 src/leidenalg/igraph-R/igraph_layout.h delete mode 100644 src/leidenalg/igraph-R/igraph_matching.h delete mode 100644 src/leidenalg/igraph-R/igraph_matrix_pmt.h delete mode 100644 src/leidenalg/igraph-R/igraph_microscopic_update.h delete mode 100644 src/leidenalg/igraph-R/igraph_nongraph.h delete mode 100644 src/leidenalg/igraph-R/igraph_operators.h delete mode 100644 src/leidenalg/igraph-R/igraph_paths.h delete mode 100644 src/leidenalg/igraph-R/igraph_pmt.h delete mode 100644 src/leidenalg/igraph-R/igraph_random.h delete mode 100644 src/leidenalg/igraph-R/igraph_revolver.h delete mode 100644 src/leidenalg/igraph-R/igraph_scg.h delete mode 100644 src/leidenalg/igraph-R/igraph_sparsemat.h delete mode 100644 src/leidenalg/igraph-R/igraph_spmatrix.h delete mode 100644 src/leidenalg/igraph-R/igraph_structural.h delete mode 100644 src/leidenalg/igraph-R/igraph_strvector.h delete mode 100644 src/leidenalg/igraph-R/igraph_topology.h delete mode 100644 src/leidenalg/igraph-R/igraph_transitivity.h delete mode 100644 src/leidenalg/igraph-R/igraph_vector.h delete mode 100644 src/leidenalg/igraph-R/igraph_vector_pmt.h delete mode 100644 src/leidenalg/igraph-R/igraph_vector_ptr.h create mode 100644 src/rigraph/Makefile create mode 100644 src/rigraph/Makevars.in create mode 100644 src/rigraph/Makevars.ucrt create mode 100644 src/rigraph/Makevars.win create mode 100644 src/rigraph/config.h create mode 100644 src/rigraph/config.h.in create mode 100644 src/rigraph/core/centrality/betweenness.c create mode 100644 src/rigraph/core/centrality/centrality_other.c create mode 100644 src/rigraph/core/centrality/centralization.c create mode 100644 src/rigraph/core/centrality/closeness.c create mode 100644 src/rigraph/core/centrality/coreness.c create mode 100644 src/rigraph/core/centrality/prpack.cpp create mode 100644 src/rigraph/core/centrality/prpack/prpack.h create mode 100644 src/rigraph/core/centrality/prpack/prpack_base_graph.cpp create mode 100644 src/rigraph/core/centrality/prpack/prpack_base_graph.h create mode 100644 src/rigraph/core/centrality/prpack/prpack_csc.h create mode 100644 src/rigraph/core/centrality/prpack/prpack_csr.h create mode 100644 src/rigraph/core/centrality/prpack/prpack_edge_list.h create mode 100644 src/rigraph/core/centrality/prpack/prpack_igraph_graph.cpp create mode 100644 src/rigraph/core/centrality/prpack/prpack_igraph_graph.h create mode 100644 src/rigraph/core/centrality/prpack/prpack_preprocessed_ge_graph.cpp create mode 100644 src/rigraph/core/centrality/prpack/prpack_preprocessed_ge_graph.h create mode 100644 src/rigraph/core/centrality/prpack/prpack_preprocessed_graph.h create mode 100644 src/rigraph/core/centrality/prpack/prpack_preprocessed_gs_graph.cpp create mode 100644 src/rigraph/core/centrality/prpack/prpack_preprocessed_gs_graph.h create mode 100644 src/rigraph/core/centrality/prpack/prpack_preprocessed_scc_graph.cpp create mode 100644 src/rigraph/core/centrality/prpack/prpack_preprocessed_scc_graph.h create mode 100644 src/rigraph/core/centrality/prpack/prpack_preprocessed_schur_graph.cpp create mode 100644 src/rigraph/core/centrality/prpack/prpack_preprocessed_schur_graph.h create mode 100644 src/rigraph/core/centrality/prpack/prpack_result.cpp create mode 100644 src/rigraph/core/centrality/prpack/prpack_result.h create mode 100644 src/rigraph/core/centrality/prpack/prpack_solver.cpp create mode 100644 src/rigraph/core/centrality/prpack/prpack_solver.h create mode 100644 src/rigraph/core/centrality/prpack/prpack_utils.cpp create mode 100644 src/rigraph/core/centrality/prpack/prpack_utils.h create mode 100644 src/rigraph/core/centrality/prpack_internal.h create mode 100644 src/rigraph/core/cliques/cliquer/cliquer.c create mode 100644 src/rigraph/core/cliques/cliquer/cliquer.h create mode 100644 src/rigraph/core/cliques/cliquer/cliquer_graph.c create mode 100644 src/rigraph/core/cliques/cliquer/cliquerconf.h create mode 100644 src/rigraph/core/cliques/cliquer/graph.h create mode 100644 src/rigraph/core/cliques/cliquer/misc.h create mode 100644 src/rigraph/core/cliques/cliquer/reorder.c create mode 100644 src/rigraph/core/cliques/cliquer/reorder.h create mode 100644 src/rigraph/core/cliques/cliquer/set.h create mode 100644 src/rigraph/core/cliques/cliquer_internal.h create mode 100644 src/rigraph/core/cliques/cliquer_wrapper.c create mode 100644 src/rigraph/core/cliques/cliques.c create mode 100644 src/rigraph/core/cliques/glet.c create mode 100644 src/rigraph/core/cliques/maximal_cliques.c create mode 100644 src/rigraph/core/cliques/maximal_cliques_template.h create mode 100644 src/rigraph/core/community/community_misc.c create mode 100644 src/rigraph/core/community/edge_betweenness.c create mode 100644 src/rigraph/core/community/fast_modularity.c create mode 100644 src/rigraph/core/community/fluid.c create mode 100644 src/rigraph/core/community/infomap/infomap.cc create mode 100644 src/rigraph/core/community/infomap/infomap_FlowGraph.cc create mode 100644 src/rigraph/core/community/infomap/infomap_FlowGraph.h create mode 100644 src/rigraph/core/community/infomap/infomap_Greedy.cc create mode 100644 src/rigraph/core/community/infomap/infomap_Greedy.h create mode 100644 src/rigraph/core/community/infomap/infomap_Node.cc create mode 100644 src/rigraph/core/community/infomap/infomap_Node.h create mode 100644 src/rigraph/core/community/label_propagation.c create mode 100644 src/rigraph/core/community/leading_eigenvector.c create mode 100644 src/rigraph/core/community/leiden.c create mode 100644 src/rigraph/core/community/louvain.c create mode 100644 src/rigraph/core/community/modularity.c create mode 100644 src/rigraph/core/community/optimal_modularity.c create mode 100644 src/rigraph/core/community/spinglass/NetDataTypes.cpp create mode 100644 src/rigraph/core/community/spinglass/NetDataTypes.h create mode 100644 src/rigraph/core/community/spinglass/NetRoutines.cpp create mode 100644 src/rigraph/core/community/spinglass/NetRoutines.h create mode 100644 src/rigraph/core/community/spinglass/clustertool.cpp create mode 100644 src/rigraph/core/community/spinglass/pottsmodel_2.cpp create mode 100644 src/rigraph/core/community/spinglass/pottsmodel_2.h create mode 100644 src/rigraph/core/community/walktrap/walktrap.cpp create mode 100644 src/rigraph/core/community/walktrap/walktrap_communities.cpp create mode 100644 src/rigraph/core/community/walktrap/walktrap_communities.h create mode 100644 src/rigraph/core/community/walktrap/walktrap_graph.cpp create mode 100644 src/rigraph/core/community/walktrap/walktrap_graph.h create mode 100644 src/rigraph/core/community/walktrap/walktrap_heap.cpp create mode 100644 src/rigraph/core/community/walktrap/walktrap_heap.h create mode 100644 src/rigraph/core/connectivity/cohesive_blocks.c create mode 100644 src/rigraph/core/connectivity/components.c create mode 100644 src/rigraph/core/connectivity/separators.c create mode 100644 src/rigraph/core/constructors/adjacency.c create mode 100644 src/rigraph/core/constructors/atlas-edges.h create mode 100644 src/rigraph/core/constructors/atlas.c create mode 100644 src/rigraph/core/constructors/basic_constructors.c create mode 100644 src/rigraph/core/constructors/de_bruijn.c create mode 100644 src/rigraph/core/constructors/famous.c create mode 100644 src/rigraph/core/constructors/full.c create mode 100644 src/rigraph/core/constructors/kautz.c create mode 100644 src/rigraph/core/constructors/lcf.c create mode 100644 src/rigraph/core/constructors/linegraph.c create mode 100644 src/rigraph/core/constructors/prufer.c create mode 100644 src/rigraph/core/constructors/regular.c create mode 100644 src/rigraph/core/core/array.c create mode 100644 src/rigraph/core/core/array.pmt create mode 100644 src/rigraph/core/core/buckets.c create mode 100644 src/rigraph/core/core/buckets.h create mode 100644 src/rigraph/core/core/cutheap.c create mode 100644 src/rigraph/core/core/cutheap.h create mode 100644 src/rigraph/core/core/dqueue.c create mode 100644 src/rigraph/core/core/dqueue.pmt create mode 100644 src/rigraph/core/core/error.c create mode 100644 src/rigraph/core/core/estack.c create mode 100644 src/rigraph/core/core/estack.h create mode 100644 src/rigraph/core/core/exceptions.h create mode 100644 src/rigraph/core/core/fixed_vectorlist.c rename src/{leidenalg/igraph-R/igraph_mixing.h => rigraph/core/core/fixed_vectorlist.h} (57%) create mode 100644 src/rigraph/core/core/grid.c create mode 100644 src/rigraph/core/core/grid.h create mode 100644 src/rigraph/core/core/heap.c create mode 100644 src/rigraph/core/core/heap.pmt create mode 100644 src/rigraph/core/core/indheap.c create mode 100644 src/rigraph/core/core/indheap.h create mode 100644 src/rigraph/core/core/interruption.c create mode 100644 src/rigraph/core/core/interruption.h create mode 100644 src/rigraph/core/core/marked_queue.c create mode 100644 src/rigraph/core/core/marked_queue.h create mode 100644 src/rigraph/core/core/math.h create mode 100644 src/rigraph/core/core/matrix.c create mode 100644 src/rigraph/core/core/matrix.pmt create mode 100644 src/rigraph/core/core/memory.c create mode 100644 src/rigraph/core/core/printing.c create mode 100644 src/rigraph/core/core/progress.c create mode 100644 src/rigraph/core/core/psumtree.c create mode 100644 src/rigraph/core/core/set.c create mode 100644 src/rigraph/core/core/set.h create mode 100644 src/rigraph/core/core/sparsemat.c create mode 100644 src/rigraph/core/core/spmatrix.c create mode 100644 src/rigraph/core/core/stack.c create mode 100644 src/rigraph/core/core/stack.pmt create mode 100644 src/rigraph/core/core/statusbar.c create mode 100644 src/rigraph/core/core/strvector.c create mode 100644 src/rigraph/core/core/trie.c create mode 100644 src/rigraph/core/core/trie.h create mode 100644 src/rigraph/core/core/vector.c create mode 100644 src/rigraph/core/core/vector.pmt create mode 100644 src/rigraph/core/core/vector_ptr.c create mode 100644 src/rigraph/core/f2c.h create mode 100644 src/rigraph/core/flow/flow.c create mode 100644 src/rigraph/core/flow/flow_internal.h create mode 100644 src/rigraph/core/flow/st-cuts.c create mode 100644 src/rigraph/core/games/barabasi.c create mode 100644 src/rigraph/core/games/callaway_traits.c create mode 100644 src/rigraph/core/games/citations.c create mode 100644 src/rigraph/core/games/correlated.c create mode 100644 src/rigraph/core/games/degree_sequence.c create mode 100644 src/rigraph/core/games/degree_sequence_vl/gengraph_box_list.cpp create mode 100644 src/rigraph/core/games/degree_sequence_vl/gengraph_box_list.h create mode 100644 src/rigraph/core/games/degree_sequence_vl/gengraph_definitions.h create mode 100644 src/rigraph/core/games/degree_sequence_vl/gengraph_degree_sequence.cpp create mode 100644 src/rigraph/core/games/degree_sequence_vl/gengraph_degree_sequence.h create mode 100644 src/rigraph/core/games/degree_sequence_vl/gengraph_graph_molloy_hash.cpp create mode 100644 src/rigraph/core/games/degree_sequence_vl/gengraph_graph_molloy_hash.h create mode 100644 src/rigraph/core/games/degree_sequence_vl/gengraph_graph_molloy_optimized.cpp create mode 100644 src/rigraph/core/games/degree_sequence_vl/gengraph_graph_molloy_optimized.h create mode 100644 src/rigraph/core/games/degree_sequence_vl/gengraph_hash.h create mode 100644 src/rigraph/core/games/degree_sequence_vl/gengraph_header.h create mode 100644 src/rigraph/core/games/degree_sequence_vl/gengraph_mr-connected.cpp create mode 100644 src/rigraph/core/games/degree_sequence_vl/gengraph_powerlaw.cpp create mode 100644 src/rigraph/core/games/degree_sequence_vl/gengraph_powerlaw.h create mode 100644 src/rigraph/core/games/degree_sequence_vl/gengraph_qsort.h create mode 100644 src/rigraph/core/games/degree_sequence_vl/gengraph_random.cpp create mode 100644 src/rigraph/core/games/degree_sequence_vl/gengraph_random.h create mode 100644 src/rigraph/core/games/degree_sequence_vl/gengraph_vertex_cover.h create mode 100644 src/rigraph/core/games/dotproduct.c create mode 100644 src/rigraph/core/games/erdos_renyi.c create mode 100644 src/rigraph/core/games/establishment.c create mode 100644 src/rigraph/core/games/forestfire.c create mode 100644 src/rigraph/core/games/grg.c create mode 100644 src/rigraph/core/games/growing_random.c create mode 100644 src/rigraph/core/games/islands.c create mode 100644 src/rigraph/core/games/k_regular.c create mode 100644 src/rigraph/core/games/preference.c create mode 100644 src/rigraph/core/games/recent_degree.c create mode 100644 src/rigraph/core/games/sbm.c create mode 100644 src/rigraph/core/games/static_fitness.c create mode 100644 src/rigraph/core/games/tree.c create mode 100644 src/rigraph/core/games/watts_strogatz.c create mode 100644 src/rigraph/core/graph/adjlist.c create mode 100644 src/rigraph/core/graph/attributes.c create mode 100644 src/rigraph/core/graph/attributes.h create mode 100644 src/rigraph/core/graph/basic_query.c create mode 100644 src/rigraph/core/graph/cattributes.c create mode 100644 src/rigraph/core/graph/iterators.c create mode 100644 src/rigraph/core/graph/neighbors.h create mode 100644 src/rigraph/core/graph/type_indexededgelist.c create mode 100644 src/rigraph/core/graph/visitors.c create mode 100644 src/rigraph/core/hrg/dendro.h create mode 100644 src/rigraph/core/hrg/graph.h create mode 100644 src/rigraph/core/hrg/graph_simp.h create mode 100644 src/rigraph/core/hrg/hrg.cc create mode 100644 src/rigraph/core/hrg/hrg_types.cc create mode 100644 src/rigraph/core/hrg/rbtree.h create mode 100644 src/rigraph/core/hrg/splittree_eq.h create mode 100644 src/rigraph/core/internal/glpk_support.c create mode 100644 src/rigraph/core/internal/glpk_support.h create mode 100644 src/rigraph/core/internal/gmp_internal.h create mode 100644 src/rigraph/core/internal/hacks.c rename src/{leidenalg/igraph-R/igraph_memory.h => rigraph/core/internal/hacks.h} (61%) create mode 100644 src/rigraph/core/internal/lsap.c create mode 100644 src/rigraph/core/internal/pstdint.h create mode 100644 src/rigraph/core/internal/qsort.c create mode 100644 src/rigraph/core/internal/qsort_r.c create mode 100644 src/rigraph/core/internal/zeroin.c create mode 100644 src/rigraph/core/io/dimacs.c create mode 100644 src/rigraph/core/io/dl-header.h create mode 100644 src/rigraph/core/io/dl-lexer.c create mode 100644 src/rigraph/core/io/dl-lexer.h create mode 100644 src/rigraph/core/io/dl-lexer.l create mode 100644 src/rigraph/core/io/dl-parser.c create mode 100644 src/rigraph/core/io/dl-parser.h create mode 100644 src/rigraph/core/io/dl-parser.y create mode 100644 src/rigraph/core/io/dl.c create mode 100644 src/rigraph/core/io/dot.c create mode 100644 src/rigraph/core/io/edgelist.c create mode 100644 src/rigraph/core/io/gml-header.h create mode 100644 src/rigraph/core/io/gml-lexer.c create mode 100644 src/rigraph/core/io/gml-lexer.h create mode 100644 src/rigraph/core/io/gml-lexer.l create mode 100644 src/rigraph/core/io/gml-parser.c create mode 100644 src/rigraph/core/io/gml-parser.h create mode 100644 src/rigraph/core/io/gml-parser.y create mode 100644 src/rigraph/core/io/gml-tree.c create mode 100644 src/rigraph/core/io/gml-tree.h create mode 100644 src/rigraph/core/io/gml.c create mode 100644 src/rigraph/core/io/graphdb.c create mode 100644 src/rigraph/core/io/graphml.c create mode 100644 src/rigraph/core/io/leda.c create mode 100644 src/rigraph/core/io/lgl-header.h create mode 100644 src/rigraph/core/io/lgl-lexer.c create mode 100644 src/rigraph/core/io/lgl-lexer.h create mode 100644 src/rigraph/core/io/lgl-lexer.l create mode 100644 src/rigraph/core/io/lgl-parser.c create mode 100644 src/rigraph/core/io/lgl-parser.h create mode 100644 src/rigraph/core/io/lgl-parser.y create mode 100644 src/rigraph/core/io/lgl.c create mode 100644 src/rigraph/core/io/ncol-header.h create mode 100644 src/rigraph/core/io/ncol-lexer.c create mode 100644 src/rigraph/core/io/ncol-lexer.h create mode 100644 src/rigraph/core/io/ncol-lexer.l create mode 100644 src/rigraph/core/io/ncol-parser.c create mode 100644 src/rigraph/core/io/ncol-parser.h create mode 100644 src/rigraph/core/io/ncol-parser.y create mode 100644 src/rigraph/core/io/ncol.c create mode 100644 src/rigraph/core/io/pajek-header.h create mode 100644 src/rigraph/core/io/pajek-lexer.c create mode 100644 src/rigraph/core/io/pajek-lexer.h create mode 100644 src/rigraph/core/io/pajek-lexer.l create mode 100644 src/rigraph/core/io/pajek-parser.c create mode 100644 src/rigraph/core/io/pajek-parser.h create mode 100644 src/rigraph/core/io/pajek-parser.y create mode 100644 src/rigraph/core/io/pajek.c create mode 100644 src/rigraph/core/isomorphism/bliss.cc create mode 100644 src/rigraph/core/isomorphism/bliss/bignum.hh create mode 100644 src/rigraph/core/isomorphism/bliss/defs.cc create mode 100644 src/rigraph/core/isomorphism/bliss/defs.hh create mode 100644 src/rigraph/core/isomorphism/bliss/graph.cc create mode 100644 src/rigraph/core/isomorphism/bliss/graph.hh create mode 100644 src/rigraph/core/isomorphism/bliss/heap.cc create mode 100644 src/rigraph/core/isomorphism/bliss/heap.hh create mode 100644 src/rigraph/core/isomorphism/bliss/igraph-changes.md create mode 100644 src/rigraph/core/isomorphism/bliss/kqueue.hh create mode 100644 src/rigraph/core/isomorphism/bliss/kstack.hh create mode 100644 src/rigraph/core/isomorphism/bliss/orbit.cc create mode 100644 src/rigraph/core/isomorphism/bliss/orbit.hh create mode 100644 src/rigraph/core/isomorphism/bliss/partition.cc create mode 100644 src/rigraph/core/isomorphism/bliss/partition.hh create mode 100644 src/rigraph/core/isomorphism/bliss/stats.hh create mode 100644 src/rigraph/core/isomorphism/bliss/uintseqhash.cc create mode 100644 src/rigraph/core/isomorphism/bliss/uintseqhash.hh create mode 100644 src/rigraph/core/isomorphism/bliss/utils.cc create mode 100644 src/rigraph/core/isomorphism/bliss/utils.hh create mode 100644 src/rigraph/core/isomorphism/isoclasses.c rename src/{leidenalg/igraph-R/igraph_neighborhood.h => rigraph/core/isomorphism/isoclasses.h} (50%) create mode 100644 src/rigraph/core/isomorphism/isomorphism_misc.c create mode 100644 src/rigraph/core/isomorphism/lad.c create mode 100644 src/rigraph/core/isomorphism/queries.c create mode 100644 src/rigraph/core/isomorphism/vf2.c create mode 100644 src/rigraph/core/layout/circular.c create mode 100644 src/rigraph/core/layout/davidson_harel.c create mode 100644 src/rigraph/core/layout/drl/DensityGrid.cpp create mode 100644 src/rigraph/core/layout/drl/DensityGrid.h create mode 100644 src/rigraph/core/layout/drl/DensityGrid_3d.cpp create mode 100644 src/rigraph/core/layout/drl/DensityGrid_3d.h create mode 100644 src/rigraph/core/layout/drl/drl_Node.h create mode 100644 src/rigraph/core/layout/drl/drl_Node_3d.h create mode 100644 src/rigraph/core/layout/drl/drl_graph.cpp create mode 100644 src/rigraph/core/layout/drl/drl_graph.h create mode 100644 src/rigraph/core/layout/drl/drl_graph_3d.cpp create mode 100644 src/rigraph/core/layout/drl/drl_graph_3d.h create mode 100644 src/rigraph/core/layout/drl/drl_layout.cpp create mode 100644 src/rigraph/core/layout/drl/drl_layout.h create mode 100644 src/rigraph/core/layout/drl/drl_layout_3d.cpp create mode 100644 src/rigraph/core/layout/drl/drl_layout_3d.h create mode 100644 src/rigraph/core/layout/drl/drl_parse.cpp create mode 100644 src/rigraph/core/layout/drl/drl_parse.h create mode 100644 src/rigraph/core/layout/fruchterman_reingold.c create mode 100644 src/rigraph/core/layout/gem.c create mode 100644 src/rigraph/core/layout/graphopt.c create mode 100644 src/rigraph/core/layout/kamada_kawai.c create mode 100644 src/rigraph/core/layout/large_graph.c create mode 100644 src/rigraph/core/layout/layout_bipartite.c create mode 100644 src/rigraph/core/layout/layout_grid.c create mode 100644 src/rigraph/core/layout/layout_internal.h create mode 100644 src/rigraph/core/layout/layout_random.c create mode 100644 src/rigraph/core/layout/mds.c create mode 100644 src/rigraph/core/layout/merge_dla.c create mode 100644 src/rigraph/core/layout/merge_grid.c create mode 100644 src/rigraph/core/layout/merge_grid.h create mode 100644 src/rigraph/core/layout/reingold_tilford.c create mode 100644 src/rigraph/core/layout/sugiyama.c create mode 100644 src/rigraph/core/linalg/arpack.c create mode 100644 src/rigraph/core/linalg/arpack_internal.h create mode 100644 src/rigraph/core/linalg/blas.c create mode 100644 src/rigraph/core/linalg/blas_internal.h create mode 100644 src/rigraph/core/linalg/eigen.c create mode 100644 src/rigraph/core/linalg/lapack.c create mode 100644 src/rigraph/core/linalg/lapack_internal.h create mode 100644 src/rigraph/core/math/bfgs.c create mode 100644 src/rigraph/core/math/complex.c create mode 100644 src/rigraph/core/math/utils.c create mode 100644 src/rigraph/core/misc/bipartite.c create mode 100644 src/rigraph/core/misc/chordality.c create mode 100644 src/rigraph/core/misc/cocitation.c create mode 100644 src/rigraph/core/misc/coloring.c create mode 100644 src/rigraph/core/misc/conversion.c create mode 100644 src/rigraph/core/misc/conversion_internal.h create mode 100644 src/rigraph/core/misc/degree_sequence.cpp create mode 100644 src/rigraph/core/misc/embedding.c create mode 100644 src/rigraph/core/misc/feedback_arc_set.c create mode 100644 src/rigraph/core/misc/feedback_arc_set.h create mode 100644 src/rigraph/core/misc/graphicality.c create mode 100644 src/rigraph/core/misc/matching.c create mode 100644 src/rigraph/core/misc/microscopic_update.c create mode 100644 src/rigraph/core/misc/mixing.c create mode 100644 src/rigraph/core/misc/motifs.c create mode 100644 src/rigraph/core/misc/other.c create mode 100644 src/rigraph/core/misc/scan.c create mode 100644 src/rigraph/core/misc/sir.c create mode 100644 src/rigraph/core/misc/spanning_trees.c create mode 100644 src/rigraph/core/operators/add_edge.c create mode 100644 src/rigraph/core/operators/complementer.c create mode 100644 src/rigraph/core/operators/compose.c create mode 100644 src/rigraph/core/operators/connect_neighborhood.c create mode 100644 src/rigraph/core/operators/contract.c create mode 100644 src/rigraph/core/operators/difference.c create mode 100644 src/rigraph/core/operators/disjoint_union.c create mode 100644 src/rigraph/core/operators/intersection.c create mode 100644 src/rigraph/core/operators/misc_internal.c create mode 100644 src/rigraph/core/operators/misc_internal.h create mode 100644 src/rigraph/core/operators/permute.c create mode 100644 src/rigraph/core/operators/rewire.c create mode 100644 src/rigraph/core/operators/rewire_edges.c create mode 100644 src/rigraph/core/operators/rewire_internal.h create mode 100644 src/rigraph/core/operators/simplify.c create mode 100644 src/rigraph/core/operators/subgraph.c create mode 100644 src/rigraph/core/operators/subgraph.h create mode 100644 src/rigraph/core/operators/union.c create mode 100644 src/rigraph/core/paths/all_shortest_paths.c create mode 100644 src/rigraph/core/paths/bellman_ford.c create mode 100644 src/rigraph/core/paths/dijkstra.c create mode 100644 src/rigraph/core/paths/distances.c create mode 100644 src/rigraph/core/paths/eulerian.c create mode 100644 src/rigraph/core/paths/histogram.c create mode 100644 src/rigraph/core/paths/johnson.c create mode 100644 src/rigraph/core/paths/random_walk.c create mode 100644 src/rigraph/core/paths/shortest_paths.c create mode 100644 src/rigraph/core/paths/simple_paths.c create mode 100644 src/rigraph/core/paths/unweighted.c create mode 100644 src/rigraph/core/properties/basic_properties.c create mode 100644 src/rigraph/core/properties/constraint.c create mode 100644 src/rigraph/core/properties/convergence_degree.c create mode 100644 src/rigraph/core/properties/dag.c create mode 100644 src/rigraph/core/properties/degrees.c create mode 100644 src/rigraph/core/properties/girth.c create mode 100644 src/rigraph/core/properties/loops.c create mode 100644 src/rigraph/core/properties/multiplicity.c create mode 100644 src/rigraph/core/properties/neighborhood.c create mode 100644 src/rigraph/core/properties/properties_internal.h create mode 100644 src/rigraph/core/properties/spectral.c create mode 100644 src/rigraph/core/properties/trees.c create mode 100644 src/rigraph/core/properties/triangles.c create mode 100644 src/rigraph/core/properties/triangles_template.h create mode 100644 src/rigraph/core/properties/triangles_template1.h create mode 100644 src/rigraph/core/random/random.c create mode 100644 src/rigraph/core/scg/scg.c create mode 100644 src/rigraph/core/scg/scg_approximate_methods.c create mode 100644 src/rigraph/core/scg/scg_exact_scg.c create mode 100644 src/rigraph/core/scg/scg_headers.h create mode 100644 src/rigraph/core/scg/scg_kmeans.c create mode 100644 src/rigraph/core/scg/scg_optimal_method.c create mode 100644 src/rigraph/core/scg/scg_utils.c create mode 100644 src/rigraph/core/version.c create mode 100644 src/rigraph/igraph-win.def rename src/{leidenalg/igraph-R => rigraph/include}/igraph.h (90%) create mode 100644 src/rigraph/include/igraph_adjlist.h rename src/{leidenalg/igraph-R => rigraph/include}/igraph_arpack.h (76%) rename src/{leidenalg/igraph-R => rigraph/include}/igraph_array.h (88%) create mode 100644 src/rigraph/include/igraph_array_pmt.h create mode 100644 src/rigraph/include/igraph_attributes.h create mode 100644 src/rigraph/include/igraph_bipartite.h rename src/{leidenalg/igraph-R => rigraph/include}/igraph_blas.h (70%) create mode 100644 src/rigraph/include/igraph_centrality.h create mode 100644 src/rigraph/include/igraph_cliques.h create mode 100644 src/rigraph/include/igraph_cocitation.h rename src/{leidenalg/igraph-R => rigraph/include}/igraph_cohesive_blocks.h (72%) create mode 100644 src/rigraph/include/igraph_coloring.h create mode 100644 src/rigraph/include/igraph_community.h create mode 100644 src/rigraph/include/igraph_complex.h create mode 100644 src/rigraph/include/igraph_components.h create mode 100644 src/rigraph/include/igraph_constants.h create mode 100644 src/rigraph/include/igraph_constructors.h rename src/{leidenalg/igraph-R => rigraph/include}/igraph_conversion.h (50%) rename src/{leidenalg/igraph-R => rigraph/include}/igraph_datatype.h (69%) create mode 100644 src/rigraph/include/igraph_decls.h rename src/{leidenalg/igraph-R => rigraph/include}/igraph_dqueue.h (84%) create mode 100644 src/rigraph/include/igraph_dqueue_pmt.h create mode 100644 src/rigraph/include/igraph_eigen.h create mode 100644 src/rigraph/include/igraph_embedding.h create mode 100644 src/rigraph/include/igraph_epidemics.h rename src/{leidenalg/igraph-R => rigraph/include}/igraph_error.h (52%) create mode 100644 src/rigraph/include/igraph_eulerian.h create mode 100644 src/rigraph/include/igraph_export.h create mode 100644 src/rigraph/include/igraph_flow.h create mode 100644 src/rigraph/include/igraph_foreign.h create mode 100644 src/rigraph/include/igraph_games.h create mode 100644 src/rigraph/include/igraph_graphicality.h create mode 100644 src/rigraph/include/igraph_graphlets.h rename src/{leidenalg/igraph-R => rigraph/include}/igraph_heap.h (90%) create mode 100644 src/rigraph/include/igraph_heap_pmt.h rename src/{leidenalg/igraph-R => rigraph/include}/igraph_hrg.h (54%) create mode 100644 src/rigraph/include/igraph_interface.h rename src/{leidenalg/igraph-R => rigraph/include}/igraph_interrupt.h (90%) rename src/{leidenalg/igraph-R => rigraph/include}/igraph_iterators.h (54%) create mode 100644 src/rigraph/include/igraph_lapack.h create mode 100644 src/rigraph/include/igraph_layout.h create mode 100644 src/rigraph/include/igraph_lsap.h create mode 100644 src/rigraph/include/igraph_matching.h rename src/{leidenalg/igraph-R => rigraph/include}/igraph_matrix.h (79%) create mode 100644 src/rigraph/include/igraph_matrix_pmt.h create mode 100644 src/rigraph/include/igraph_memory.h create mode 100644 src/rigraph/include/igraph_microscopic_update.h create mode 100644 src/rigraph/include/igraph_mixing.h rename src/{leidenalg/igraph-R => rigraph/include}/igraph_motifs.h (56%) create mode 100644 src/rigraph/include/igraph_neighborhood.h create mode 100644 src/rigraph/include/igraph_nongraph.h create mode 100644 src/rigraph/include/igraph_operators.h create mode 100644 src/rigraph/include/igraph_paths.h create mode 100644 src/rigraph/include/igraph_pmt.h rename src/{leidenalg/igraph-R => rigraph/include}/igraph_pmt_off.h (70%) rename src/{leidenalg/igraph-R => rigraph/include}/igraph_progress.h (73%) rename src/{leidenalg/igraph-R => rigraph/include}/igraph_psumtree.h (55%) rename src/{leidenalg/igraph-R => rigraph/include}/igraph_qsort.h (67%) create mode 100644 src/rigraph/include/igraph_random.h create mode 100644 src/rigraph/include/igraph_scan.h create mode 100644 src/rigraph/include/igraph_scg.h rename src/{leidenalg/igraph-R => rigraph/include}/igraph_separators.h (60%) create mode 100644 src/rigraph/include/igraph_sparsemat.h create mode 100644 src/rigraph/include/igraph_spmatrix.h rename src/{leidenalg/igraph-R => rigraph/include}/igraph_stack.h (84%) rename src/{leidenalg/igraph-R => rigraph/include}/igraph_stack_pmt.h (51%) rename src/{leidenalg/igraph-R => rigraph/include}/igraph_statusbar.h (65%) create mode 100644 src/rigraph/include/igraph_structural.h create mode 100644 src/rigraph/include/igraph_strvector.h rename src/{leidenalg/igraph-R => rigraph/include}/igraph_threading.h (68%) create mode 100644 src/rigraph/include/igraph_threading.h.in create mode 100644 src/rigraph/include/igraph_topology.h create mode 100644 src/rigraph/include/igraph_transitivity.h rename src/{leidenalg/igraph-R => rigraph/include}/igraph_types.h (50%) create mode 100644 src/rigraph/include/igraph_vector.h create mode 100644 src/rigraph/include/igraph_vector_pmt.h create mode 100644 src/rigraph/include/igraph_vector_ptr.h rename src/{leidenalg/igraph-R/igraph_version.h => rigraph/include/igraph_vector_type.h} (74%) create mode 100644 src/rigraph/include/igraph_version.h create mode 100644 src/rigraph/include/igraph_version.h.in rename src/{leidenalg/igraph-R => rigraph/include}/igraph_visitor.h (68%) create mode 100644 src/rigraph/init.c create mode 100644 src/rigraph/lazyeval.c create mode 100644 src/rigraph/rinterface.c create mode 100644 src/rigraph/rinterface.h create mode 100644 src/rigraph/rinterface_extra.c create mode 100644 src/rigraph/rrandom.c create mode 100644 src/rigraph/rrandom.h create mode 100644 src/rigraph/vendor/arpack/debug.h create mode 100644 src/rigraph/vendor/arpack/dgetv0.f create mode 100644 src/rigraph/vendor/arpack/dlaqrb.f create mode 100644 src/rigraph/vendor/arpack/dmout.f create mode 100644 src/rigraph/vendor/arpack/dnaitr.f create mode 100644 src/rigraph/vendor/arpack/dnapps.f create mode 100644 src/rigraph/vendor/arpack/dnaup2.f create mode 100644 src/rigraph/vendor/arpack/dnaupd.f create mode 100644 src/rigraph/vendor/arpack/dnconv.f create mode 100644 src/rigraph/vendor/arpack/dneigh.f create mode 100644 src/rigraph/vendor/arpack/dneupd.f create mode 100644 src/rigraph/vendor/arpack/dngets.f create mode 100644 src/rigraph/vendor/arpack/dsaitr.f create mode 100644 src/rigraph/vendor/arpack/dsapps.f create mode 100644 src/rigraph/vendor/arpack/dsaup2.f create mode 100644 src/rigraph/vendor/arpack/dsaupd.f create mode 100644 src/rigraph/vendor/arpack/dsconv.f create mode 100644 src/rigraph/vendor/arpack/dseigt.f create mode 100644 src/rigraph/vendor/arpack/dsesrt.f create mode 100644 src/rigraph/vendor/arpack/dseupd.f create mode 100644 src/rigraph/vendor/arpack/dsgets.f create mode 100644 src/rigraph/vendor/arpack/dsortc.f create mode 100644 src/rigraph/vendor/arpack/dsortr.f create mode 100644 src/rigraph/vendor/arpack/dstatn.f create mode 100644 src/rigraph/vendor/arpack/dstats.f create mode 100644 src/rigraph/vendor/arpack/dstqrb.f create mode 100644 src/rigraph/vendor/arpack/dvout.f create mode 100644 src/rigraph/vendor/arpack/ivout.f create mode 100644 src/rigraph/vendor/arpack/second.f create mode 100644 src/rigraph/vendor/arpack/stat.h create mode 100644 src/rigraph/vendor/arpack/wrap.f create mode 100644 src/rigraph/vendor/cs/SuiteSparse_config.h create mode 100644 src/rigraph/vendor/cs/cs.h create mode 100644 src/rigraph/vendor/cs/cs_add.c create mode 100644 src/rigraph/vendor/cs/cs_amd.c create mode 100644 src/rigraph/vendor/cs/cs_chol.c create mode 100644 src/rigraph/vendor/cs/cs_cholsol.c create mode 100644 src/rigraph/vendor/cs/cs_compress.c create mode 100644 src/rigraph/vendor/cs/cs_counts.c create mode 100644 src/rigraph/vendor/cs/cs_cumsum.c create mode 100644 src/rigraph/vendor/cs/cs_dfs.c create mode 100644 src/rigraph/vendor/cs/cs_dmperm.c create mode 100644 src/rigraph/vendor/cs/cs_droptol.c create mode 100644 src/rigraph/vendor/cs/cs_dropzeros.c create mode 100644 src/rigraph/vendor/cs/cs_dupl.c create mode 100644 src/rigraph/vendor/cs/cs_entry.c create mode 100644 src/rigraph/vendor/cs/cs_ereach.c create mode 100644 src/rigraph/vendor/cs/cs_etree.c create mode 100644 src/rigraph/vendor/cs/cs_fkeep.c create mode 100644 src/rigraph/vendor/cs/cs_gaxpy.c create mode 100644 src/rigraph/vendor/cs/cs_happly.c create mode 100644 src/rigraph/vendor/cs/cs_house.c create mode 100644 src/rigraph/vendor/cs/cs_ipvec.c create mode 100644 src/rigraph/vendor/cs/cs_leaf.c create mode 100644 src/rigraph/vendor/cs/cs_load.c create mode 100644 src/rigraph/vendor/cs/cs_lsolve.c create mode 100644 src/rigraph/vendor/cs/cs_ltsolve.c create mode 100644 src/rigraph/vendor/cs/cs_lu.c create mode 100644 src/rigraph/vendor/cs/cs_lusol.c create mode 100644 src/rigraph/vendor/cs/cs_malloc.c create mode 100644 src/rigraph/vendor/cs/cs_maxtrans.c create mode 100644 src/rigraph/vendor/cs/cs_multiply.c create mode 100644 src/rigraph/vendor/cs/cs_norm.c create mode 100644 src/rigraph/vendor/cs/cs_permute.c create mode 100644 src/rigraph/vendor/cs/cs_pinv.c create mode 100644 src/rigraph/vendor/cs/cs_post.c create mode 100644 src/rigraph/vendor/cs/cs_print.c create mode 100644 src/rigraph/vendor/cs/cs_pvec.c create mode 100644 src/rigraph/vendor/cs/cs_qr.c create mode 100644 src/rigraph/vendor/cs/cs_qrsol.c create mode 100644 src/rigraph/vendor/cs/cs_randperm.c create mode 100644 src/rigraph/vendor/cs/cs_reach.c create mode 100644 src/rigraph/vendor/cs/cs_scatter.c create mode 100644 src/rigraph/vendor/cs/cs_scc.c create mode 100644 src/rigraph/vendor/cs/cs_schol.c create mode 100644 src/rigraph/vendor/cs/cs_spsolve.c create mode 100644 src/rigraph/vendor/cs/cs_sqr.c create mode 100644 src/rigraph/vendor/cs/cs_symperm.c create mode 100644 src/rigraph/vendor/cs/cs_tdfs.c create mode 100644 src/rigraph/vendor/cs/cs_transpose.c create mode 100644 src/rigraph/vendor/cs/cs_updown.c create mode 100644 src/rigraph/vendor/cs/cs_usolve.c create mode 100644 src/rigraph/vendor/cs/cs_util.c create mode 100644 src/rigraph/vendor/cs/cs_utsolve.c create mode 100644 src/rigraph/vendor/mini-gmp/mini-gmp.c create mode 100644 src/rigraph/vendor/mini-gmp/mini-gmp.h create mode 100644 src/rigraph/vendor/plfit/arithmetic_ansi.h create mode 100644 src/rigraph/vendor/plfit/arithmetic_sse_double.h create mode 100644 src/rigraph/vendor/plfit/arithmetic_sse_float.h create mode 100644 src/rigraph/vendor/plfit/gss.c create mode 100644 src/rigraph/vendor/plfit/gss.h create mode 100644 src/rigraph/vendor/plfit/hzeta.c create mode 100644 src/rigraph/vendor/plfit/hzeta.h create mode 100644 src/rigraph/vendor/plfit/kolmogorov.c create mode 100644 src/rigraph/vendor/plfit/kolmogorov.h create mode 100644 src/rigraph/vendor/plfit/lbfgs.c create mode 100644 src/rigraph/vendor/plfit/lbfgs.h create mode 100644 src/rigraph/vendor/plfit/mt.c create mode 100644 src/rigraph/vendor/plfit/options.c create mode 100644 src/rigraph/vendor/plfit/platform.c create mode 100644 src/rigraph/vendor/plfit/platform.h create mode 100644 src/rigraph/vendor/plfit/plfit.c create mode 100644 src/rigraph/vendor/plfit/plfit.h create mode 100644 src/rigraph/vendor/plfit/plfit_error.c create mode 100644 src/rigraph/vendor/plfit/plfit_error.h create mode 100644 src/rigraph/vendor/plfit/plfit_mt.h create mode 100644 src/rigraph/vendor/plfit/plfit_sampling.h create mode 100644 src/rigraph/vendor/plfit/plfit_version.h create mode 100644 src/rigraph/vendor/plfit/rbinom.c create mode 100644 src/rigraph/vendor/plfit/sampling.c create mode 100644 src/rigraph/vendor/simpleraytracer/Color.cpp create mode 100644 src/rigraph/vendor/simpleraytracer/Color.h create mode 100644 src/rigraph/vendor/simpleraytracer/Light.cpp create mode 100644 src/rigraph/vendor/simpleraytracer/Light.h create mode 100644 src/rigraph/vendor/simpleraytracer/Point.cpp create mode 100644 src/rigraph/vendor/simpleraytracer/Point.h create mode 100644 src/rigraph/vendor/simpleraytracer/RIgraphRay.cpp create mode 100644 src/rigraph/vendor/simpleraytracer/Ray.cpp create mode 100644 src/rigraph/vendor/simpleraytracer/Ray.h create mode 100644 src/rigraph/vendor/simpleraytracer/RayTracer.cpp create mode 100644 src/rigraph/vendor/simpleraytracer/RayTracer.h create mode 100644 src/rigraph/vendor/simpleraytracer/RayVector.cpp create mode 100644 src/rigraph/vendor/simpleraytracer/RayVector.h create mode 100644 src/rigraph/vendor/simpleraytracer/Shape.cpp create mode 100644 src/rigraph/vendor/simpleraytracer/Shape.h create mode 100644 src/rigraph/vendor/simpleraytracer/Sphere.cpp create mode 100644 src/rigraph/vendor/simpleraytracer/Sphere.h create mode 100644 src/rigraph/vendor/simpleraytracer/Triangle.cpp create mode 100644 src/rigraph/vendor/simpleraytracer/Triangle.h create mode 100644 src/rigraph/vendor/simpleraytracer/unit_limiter.cpp create mode 100644 src/rigraph/vendor/simpleraytracer/unit_limiter.h create mode 100644 src/rigraph/vendor/uuid/COPYING create mode 100644 src/rigraph/vendor/uuid/Makevars.in create mode 100644 src/rigraph/vendor/uuid/Makevars.win create mode 100644 src/rigraph/vendor/uuid/R.c create mode 100644 src/rigraph/vendor/uuid/clear.c create mode 100644 src/rigraph/vendor/uuid/compare.c create mode 100644 src/rigraph/vendor/uuid/config.h.in create mode 100644 src/rigraph/vendor/uuid/copy.c create mode 100644 src/rigraph/vendor/uuid/gen_uuid.c create mode 100644 src/rigraph/vendor/uuid/isnull.c create mode 100644 src/rigraph/vendor/uuid/pack.c create mode 100644 src/rigraph/vendor/uuid/parse.c create mode 100644 src/rigraph/vendor/uuid/unpack.c create mode 100644 src/rigraph/vendor/uuid/unparse.c create mode 100644 src/rigraph/vendor/uuid/uuid.h create mode 100644 src/rigraph/vendor/uuid/uuidP.h create mode 100644 src/rigraph/vendor/uuid/uuidd.h create mode 100644 src/rigraph/vendor/uuid/win32/config.h diff --git a/DESCRIPTION b/DESCRIPTION index 5a0290e..c663717 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -8,7 +8,7 @@ License: GPL-3 Copyright: See the file COPYRIGHTS for various leidenAlg copyright details Encoding: UTF-8 LazyData: true -Depends: R (>= 3.5.0), Matrix, igraph +Depends: R (>= 3.5.0), Matrix Imports: graphics, grDevices, @@ -21,7 +21,7 @@ Suggests: pbapply, testthat (>= 3.1.0) LinkingTo: Rcpp, RcppArmadillo, RcppEigen -SystemRequirements: GNU make +SystemRequirements: C++11, GNU make (optional), libxml2 (optional), glpk (>= 4.57, optional) RoxygenNote: 7.1.2 URL: https://github.com/kharchenkolab/leidenAlg BugReports: https://github.com/kharchenkolab/leidenAlg/issues diff --git a/cleanup b/cleanup new file mode 100755 index 0000000..8de4092 --- /dev/null +++ b/cleanup @@ -0,0 +1,9 @@ +#! /bin/sh + +rm -f src/rigraph/config.h src/rigraph/Makevars +touch src/rigraph/config.h + +# Object files cause problems on Github Actions where they get included +# in the source package that is re-generated from the original source, so +# we remove them here in the cleanup step +find src -name *.o | xargs rm diff --git a/cleanup.win b/cleanup.win new file mode 100644 index 0000000..8de4092 --- /dev/null +++ b/cleanup.win @@ -0,0 +1,9 @@ +#! /bin/sh + +rm -f src/rigraph/config.h src/rigraph/Makevars +touch src/rigraph/config.h + +# Object files cause problems on Github Actions where they get included +# in the source package that is re-generated from the original source, so +# we remove them here in the cleanup step +find src -name *.o | xargs rm diff --git a/configure b/configure new file mode 100755 index 0000000..5ebacec --- /dev/null +++ b/configure @@ -0,0 +1,5660 @@ +#! /bin/sh +# Guess values for system-dependent variables and create Makefiles. +# Generated by GNU Autoconf 2.69 for igraph 1.3.0.9016. +# +# Report bugs to . +# +# +# Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc. +# +# +# This configure script is free software; the Free Software Foundation +# gives unlimited permission to copy, distribute and modify it. +## -------------------- ## +## M4sh Initialization. ## +## -------------------- ## + +# Be more Bourne compatible +DUALCASE=1; export DUALCASE # for MKS sh +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in #( + *posix*) : + set -o posix ;; #( + *) : + ;; +esac +fi + + +as_nl=' +' +export as_nl +# Printing a long string crashes Solaris 7 /usr/bin/printf. +as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo +# Prefer a ksh shell builtin over an external printf program on Solaris, +# but without wasting forks for bash or zsh. +if test -z "$BASH_VERSION$ZSH_VERSION" \ + && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='print -r --' + as_echo_n='print -rn --' +elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='printf %s\n' + as_echo_n='printf %s' +else + if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then + as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' + as_echo_n='/usr/ucb/echo -n' + else + as_echo_body='eval expr "X$1" : "X\\(.*\\)"' + as_echo_n_body='eval + arg=$1; + case $arg in #( + *"$as_nl"*) + expr "X$arg" : "X\\(.*\\)$as_nl"; + arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; + esac; + expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" + ' + export as_echo_n_body + as_echo_n='sh -c $as_echo_n_body as_echo' + fi + export as_echo_body + as_echo='sh -c $as_echo_body as_echo' +fi + +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + PATH_SEPARATOR=: + (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { + (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || + PATH_SEPARATOR=';' + } +fi + + +# IFS +# We need space, tab and new line, in precisely that order. Quoting is +# there to prevent editors from complaining about space-tab. +# (If _AS_PATH_WALK were called with IFS unset, it would disable word +# splitting by setting IFS to empty value.) +IFS=" "" $as_nl" + +# Find who we are. Look in the path if we contain no directory separator. +as_myself= +case $0 in #(( + *[\\/]* ) as_myself=$0 ;; + *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break + done +IFS=$as_save_IFS + + ;; +esac +# We did not find ourselves, most probably we were run as `sh COMMAND' +# in which case we are not to be found in the path. +if test "x$as_myself" = x; then + as_myself=$0 +fi +if test ! -f "$as_myself"; then + $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 + exit 1 +fi + +# Unset variables that we do not need and which cause bugs (e.g. in +# pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" +# suppresses any "Segmentation fault" message there. '((' could +# trigger a bug in pdksh 5.2.14. +for as_var in BASH_ENV ENV MAIL MAILPATH +do eval test x\${$as_var+set} = xset \ + && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : +done +PS1='$ ' +PS2='> ' +PS4='+ ' + +# NLS nuisances. +LC_ALL=C +export LC_ALL +LANGUAGE=C +export LANGUAGE + +# CDPATH. +(unset CDPATH) >/dev/null 2>&1 && unset CDPATH + +# Use a proper internal environment variable to ensure we don't fall + # into an infinite loop, continuously re-executing ourselves. + if test x"${_as_can_reexec}" != xno && test "x$CONFIG_SHELL" != x; then + _as_can_reexec=no; export _as_can_reexec; + # We cannot yet assume a decent shell, so we have to provide a +# neutralization value for shells without unset; and this also +# works around shells that cannot unset nonexistent variables. +# Preserve -v and -x to the replacement shell. +BASH_ENV=/dev/null +ENV=/dev/null +(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV +case $- in # (((( + *v*x* | *x*v* ) as_opts=-vx ;; + *v* ) as_opts=-v ;; + *x* ) as_opts=-x ;; + * ) as_opts= ;; +esac +exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} +# Admittedly, this is quite paranoid, since all the known shells bail +# out after a failed `exec'. +$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 +as_fn_exit 255 + fi + # We don't want this to propagate to other subprocesses. + { _as_can_reexec=; unset _as_can_reexec;} +if test "x$CONFIG_SHELL" = x; then + as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then : + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which + # is contrary to our usage. Disable this feature. + alias -g '\${1+\"\$@\"}'='\"\$@\"' + setopt NO_GLOB_SUBST +else + case \`(set -o) 2>/dev/null\` in #( + *posix*) : + set -o posix ;; #( + *) : + ;; +esac +fi +" + as_required="as_fn_return () { (exit \$1); } +as_fn_success () { as_fn_return 0; } +as_fn_failure () { as_fn_return 1; } +as_fn_ret_success () { return 0; } +as_fn_ret_failure () { return 1; } + +exitcode=0 +as_fn_success || { exitcode=1; echo as_fn_success failed.; } +as_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; } +as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; } +as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; } +if ( set x; as_fn_ret_success y && test x = \"\$1\" ); then : + +else + exitcode=1; echo positional parameters were not saved. +fi +test x\$exitcode = x0 || exit 1 +test -x / || exit 1" + as_suggested=" as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO + as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO + eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" && + test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1 +test \$(( 1 + 1 )) = 2 || exit 1" + if (eval "$as_required") 2>/dev/null; then : + as_have_required=yes +else + as_have_required=no +fi + if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null; then : + +else + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +as_found=false +for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + as_found=: + case $as_dir in #( + /*) + for as_base in sh bash ksh sh5; do + # Try only shells that exist, to save several forks. + as_shell=$as_dir/$as_base + if { test -f "$as_shell" || test -f "$as_shell.exe"; } && + { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$as_shell"; } 2>/dev/null; then : + CONFIG_SHELL=$as_shell as_have_required=yes + if { $as_echo "$as_bourne_compatible""$as_suggested" | as_run=a "$as_shell"; } 2>/dev/null; then : + break 2 +fi +fi + done;; + esac + as_found=false +done +$as_found || { if { test -f "$SHELL" || test -f "$SHELL.exe"; } && + { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$SHELL"; } 2>/dev/null; then : + CONFIG_SHELL=$SHELL as_have_required=yes +fi; } +IFS=$as_save_IFS + + + if test "x$CONFIG_SHELL" != x; then : + export CONFIG_SHELL + # We cannot yet assume a decent shell, so we have to provide a +# neutralization value for shells without unset; and this also +# works around shells that cannot unset nonexistent variables. +# Preserve -v and -x to the replacement shell. +BASH_ENV=/dev/null +ENV=/dev/null +(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV +case $- in # (((( + *v*x* | *x*v* ) as_opts=-vx ;; + *v* ) as_opts=-v ;; + *x* ) as_opts=-x ;; + * ) as_opts= ;; +esac +exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} +# Admittedly, this is quite paranoid, since all the known shells bail +# out after a failed `exec'. +$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 +exit 255 +fi + + if test x$as_have_required = xno; then : + $as_echo "$0: This script requires a shell more modern than all" + $as_echo "$0: the shells that I found on your system." + if test x${ZSH_VERSION+set} = xset ; then + $as_echo "$0: In particular, zsh $ZSH_VERSION has bugs and should" + $as_echo "$0: be upgraded to zsh 4.3.4 or later." + else + $as_echo "$0: Please tell bug-autoconf@gnu.org and ntamas@gmail.com +$0: about your system, including any error possibly output +$0: before this message. Then install a modern shell, or +$0: manually run the script under such a shell if you do +$0: have one." + fi + exit 1 +fi +fi +fi +SHELL=${CONFIG_SHELL-/bin/sh} +export SHELL +# Unset more variables known to interfere with behavior of common tools. +CLICOLOR_FORCE= GREP_OPTIONS= +unset CLICOLOR_FORCE GREP_OPTIONS + +## --------------------- ## +## M4sh Shell Functions. ## +## --------------------- ## +# as_fn_unset VAR +# --------------- +# Portably unset VAR. +as_fn_unset () +{ + { eval $1=; unset $1;} +} +as_unset=as_fn_unset + +# as_fn_set_status STATUS +# ----------------------- +# Set $? to STATUS, without forking. +as_fn_set_status () +{ + return $1 +} # as_fn_set_status + +# as_fn_exit STATUS +# ----------------- +# Exit the shell with STATUS, even in a "trap 0" or "set -e" context. +as_fn_exit () +{ + set +e + as_fn_set_status $1 + exit $1 +} # as_fn_exit + +# as_fn_mkdir_p +# ------------- +# Create "$as_dir" as a directory, including parents if necessary. +as_fn_mkdir_p () +{ + + case $as_dir in #( + -*) as_dir=./$as_dir;; + esac + test -d "$as_dir" || eval $as_mkdir_p || { + as_dirs= + while :; do + case $as_dir in #( + *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( + *) as_qdir=$as_dir;; + esac + as_dirs="'$as_qdir' $as_dirs" + as_dir=`$as_dirname -- "$as_dir" || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + test -d "$as_dir" && break + done + test -z "$as_dirs" || eval "mkdir $as_dirs" + } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" + + +} # as_fn_mkdir_p + +# as_fn_executable_p FILE +# ----------------------- +# Test if FILE is an executable regular file. +as_fn_executable_p () +{ + test -f "$1" && test -x "$1" +} # as_fn_executable_p +# as_fn_append VAR VALUE +# ---------------------- +# Append the text in VALUE to the end of the definition contained in VAR. Take +# advantage of any shell optimizations that allow amortized linear growth over +# repeated appends, instead of the typical quadratic growth present in naive +# implementations. +if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : + eval 'as_fn_append () + { + eval $1+=\$2 + }' +else + as_fn_append () + { + eval $1=\$$1\$2 + } +fi # as_fn_append + +# as_fn_arith ARG... +# ------------------ +# Perform arithmetic evaluation on the ARGs, and store the result in the +# global $as_val. Take advantage of shells that can avoid forks. The arguments +# must be portable across $(()) and expr. +if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : + eval 'as_fn_arith () + { + as_val=$(( $* )) + }' +else + as_fn_arith () + { + as_val=`expr "$@" || test $? -eq 1` + } +fi # as_fn_arith + + +# as_fn_error STATUS ERROR [LINENO LOG_FD] +# ---------------------------------------- +# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are +# provided, also output the error to LOG_FD, referencing LINENO. Then exit the +# script with STATUS, using 1 if that was 0. +as_fn_error () +{ + as_status=$1; test $as_status -eq 0 && as_status=1 + if test "$4"; then + as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 + fi + $as_echo "$as_me: error: $2" >&2 + as_fn_exit $as_status +} # as_fn_error + +if expr a : '\(a\)' >/dev/null 2>&1 && + test "X`expr 00001 : '.*\(...\)'`" = X001; then + as_expr=expr +else + as_expr=false +fi + +if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then + as_basename=basename +else + as_basename=false +fi + +if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then + as_dirname=dirname +else + as_dirname=false +fi + +as_me=`$as_basename -- "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ + s//\1/ + q + } + /^X\/\(\/\/\)$/{ + s//\1/ + q + } + /^X\/\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits + + + as_lineno_1=$LINENO as_lineno_1a=$LINENO + as_lineno_2=$LINENO as_lineno_2a=$LINENO + eval 'test "x$as_lineno_1'$as_run'" != "x$as_lineno_2'$as_run'" && + test "x`expr $as_lineno_1'$as_run' + 1`" = "x$as_lineno_2'$as_run'"' || { + # Blame Lee E. McMahon (1931-1989) for sed's syntax. :-) + sed -n ' + p + /[$]LINENO/= + ' <$as_myself | + sed ' + s/[$]LINENO.*/&-/ + t lineno + b + :lineno + N + :loop + s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/ + t loop + s/-\n.*// + ' >$as_me.lineno && + chmod +x "$as_me.lineno" || + { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; } + + # If we had to re-execute with $CONFIG_SHELL, we're ensured to have + # already done that, so ensure we don't try to do so again and fall + # in an infinite loop. This has already happened in practice. + _as_can_reexec=no; export _as_can_reexec + # Don't try to exec as it changes $[0], causing all sort of problems + # (the dirname of $[0] is not the place where we might find the + # original and so on. Autoconf is especially sensitive to this). + . "./$as_me.lineno" + # Exit status is that of the last command. + exit +} + +ECHO_C= ECHO_N= ECHO_T= +case `echo -n x` in #((((( +-n*) + case `echo 'xy\c'` in + *c*) ECHO_T=' ';; # ECHO_T is single tab character. + xy) ECHO_C='\c';; + *) echo `echo ksh88 bug on AIX 6.1` > /dev/null + ECHO_T=' ';; + esac;; +*) + ECHO_N='-n';; +esac + +rm -f conf$$ conf$$.exe conf$$.file +if test -d conf$$.dir; then + rm -f conf$$.dir/conf$$.file +else + rm -f conf$$.dir + mkdir conf$$.dir 2>/dev/null +fi +if (echo >conf$$.file) 2>/dev/null; then + if ln -s conf$$.file conf$$ 2>/dev/null; then + as_ln_s='ln -s' + # ... but there are two gotchas: + # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. + # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. + # In both cases, we have to default to `cp -pR'. + ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || + as_ln_s='cp -pR' + elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln + else + as_ln_s='cp -pR' + fi +else + as_ln_s='cp -pR' +fi +rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file +rmdir conf$$.dir 2>/dev/null + +if mkdir -p . 2>/dev/null; then + as_mkdir_p='mkdir -p "$as_dir"' +else + test -d ./-p && rmdir ./-p + as_mkdir_p=false +fi + +as_test_x='test -x' +as_executable_p=as_fn_executable_p + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" + +# Sed expression to map a string onto a valid variable name. +as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" + + +test -n "$DJDIR" || exec 7<&0 &1 + +# Name of the host. +# hostname on some systems (SVR3.2, old GNU/Linux) returns a bogus exit status, +# so uname gets run too. +ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` + +# +# Initializations. +# +ac_default_prefix=/usr/local +ac_clean_files= +ac_config_libobj_dir=. +LIBOBJS= +cross_compiling=no +subdirs= +MFLAGS= +MAKEFLAGS= + +# Identity of this package. +PACKAGE_NAME='igraph' +PACKAGE_TARNAME='igraph' +PACKAGE_VERSION='1.3.0.9016' +PACKAGE_STRING='igraph 1.3.0.9016' +PACKAGE_BUGREPORT='ntamas@gmail.com' +PACKAGE_URL='' + +ac_unique_file="src/rigraph/rinterface.c" +# Factoring default headers for most tests. +ac_includes_default="\ +#include +#ifdef HAVE_SYS_TYPES_H +# include +#endif +#ifdef HAVE_SYS_STAT_H +# include +#endif +#ifdef STDC_HEADERS +# include +# include +#else +# ifdef HAVE_STDLIB_H +# include +# endif +#endif +#ifdef HAVE_STRING_H +# if !defined STDC_HEADERS && defined HAVE_MEMORY_H +# include +# endif +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif +#ifdef HAVE_INTTYPES_H +# include +#endif +#ifdef HAVE_STDINT_H +# include +#endif +#ifdef HAVE_UNISTD_H +# include +#endif" + +ac_subst_vars='LTLIBOBJS +LIBOBJS +GLPK_LIBS +HAVE_GLPK +GMP_LIBS +INTERNAL_GMP +HAVE_GMP +XML2_CPPFLAGS +XML2_LIBS +HAVE_LIBXML +XML2CONFIG +EGREP +GREP +CXXCPP +ac_ct_FC +FCFLAGS +FC +ac_ct_CXX +CXXFLAGS +CXX +OBJEXT +EXEEXT +ac_ct_CC +CPPFLAGS +LDFLAGS +CFLAGS +CC +target_alias +host_alias +build_alias +LIBS +ECHO_T +ECHO_N +ECHO_C +DEFS +mandir +localedir +libdir +psdir +pdfdir +dvidir +htmldir +infodir +docdir +oldincludedir +includedir +runstatedir +localstatedir +sharedstatedir +sysconfdir +datadir +datarootdir +libexecdir +sbindir +bindir +program_transform_name +prefix +exec_prefix +PACKAGE_URL +PACKAGE_BUGREPORT +PACKAGE_STRING +PACKAGE_VERSION +PACKAGE_TARNAME +PACKAGE_NAME +PATH_SEPARATOR +SHELL' +ac_subst_files='' +ac_user_opts=' +enable_option_checking +enable_graphml +enable_glpk +' + ac_precious_vars='build_alias +host_alias +target_alias +CC +CFLAGS +LDFLAGS +LIBS +CPPFLAGS +CXX +CXXFLAGS +CCC +FC +FCFLAGS +CXXCPP' + + +# Initialize some variables set by options. +ac_init_help= +ac_init_version=false +ac_unrecognized_opts= +ac_unrecognized_sep= +# The variables have the same names as the options, with +# dashes changed to underlines. +cache_file=/dev/null +exec_prefix=NONE +no_create= +no_recursion= +prefix=NONE +program_prefix=NONE +program_suffix=NONE +program_transform_name=s,x,x, +silent= +site= +srcdir= +verbose= +x_includes=NONE +x_libraries=NONE + +# Installation directory options. +# These are left unexpanded so users can "make install exec_prefix=/foo" +# and all the variables that are supposed to be based on exec_prefix +# by default will actually change. +# Use braces instead of parens because sh, perl, etc. also accept them. +# (The list follows the same order as the GNU Coding Standards.) +bindir='${exec_prefix}/bin' +sbindir='${exec_prefix}/sbin' +libexecdir='${exec_prefix}/libexec' +datarootdir='${prefix}/share' +datadir='${datarootdir}' +sysconfdir='${prefix}/etc' +sharedstatedir='${prefix}/com' +localstatedir='${prefix}/var' +runstatedir='${localstatedir}/run' +includedir='${prefix}/include' +oldincludedir='/usr/include' +docdir='${datarootdir}/doc/${PACKAGE_TARNAME}' +infodir='${datarootdir}/info' +htmldir='${docdir}' +dvidir='${docdir}' +pdfdir='${docdir}' +psdir='${docdir}' +libdir='${exec_prefix}/lib' +localedir='${datarootdir}/locale' +mandir='${datarootdir}/man' + +ac_prev= +ac_dashdash= +for ac_option +do + # If the previous option needs an argument, assign it. + if test -n "$ac_prev"; then + eval $ac_prev=\$ac_option + ac_prev= + continue + fi + + case $ac_option in + *=?*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;; + *=) ac_optarg= ;; + *) ac_optarg=yes ;; + esac + + # Accept the important Cygnus configure options, so we can diagnose typos. + + case $ac_dashdash$ac_option in + --) + ac_dashdash=yes ;; + + -bindir | --bindir | --bindi | --bind | --bin | --bi) + ac_prev=bindir ;; + -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) + bindir=$ac_optarg ;; + + -build | --build | --buil | --bui | --bu) + ac_prev=build_alias ;; + -build=* | --build=* | --buil=* | --bui=* | --bu=*) + build_alias=$ac_optarg ;; + + -cache-file | --cache-file | --cache-fil | --cache-fi \ + | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) + ac_prev=cache_file ;; + -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ + | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) + cache_file=$ac_optarg ;; + + --config-cache | -C) + cache_file=config.cache ;; + + -datadir | --datadir | --datadi | --datad) + ac_prev=datadir ;; + -datadir=* | --datadir=* | --datadi=* | --datad=*) + datadir=$ac_optarg ;; + + -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \ + | --dataroo | --dataro | --datar) + ac_prev=datarootdir ;; + -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \ + | --dataroot=* | --dataroo=* | --dataro=* | --datar=*) + datarootdir=$ac_optarg ;; + + -disable-* | --disable-*) + ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error $? "invalid feature name: $ac_useropt" + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"enable_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval enable_$ac_useropt=no ;; + + -docdir | --docdir | --docdi | --doc | --do) + ac_prev=docdir ;; + -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*) + docdir=$ac_optarg ;; + + -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv) + ac_prev=dvidir ;; + -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*) + dvidir=$ac_optarg ;; + + -enable-* | --enable-*) + ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error $? "invalid feature name: $ac_useropt" + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"enable_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval enable_$ac_useropt=\$ac_optarg ;; + + -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ + | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ + | --exec | --exe | --ex) + ac_prev=exec_prefix ;; + -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ + | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ + | --exec=* | --exe=* | --ex=*) + exec_prefix=$ac_optarg ;; + + -gas | --gas | --ga | --g) + # Obsolete; use --with-gas. + with_gas=yes ;; + + -help | --help | --hel | --he | -h) + ac_init_help=long ;; + -help=r* | --help=r* | --hel=r* | --he=r* | -hr*) + ac_init_help=recursive ;; + -help=s* | --help=s* | --hel=s* | --he=s* | -hs*) + ac_init_help=short ;; + + -host | --host | --hos | --ho) + ac_prev=host_alias ;; + -host=* | --host=* | --hos=* | --ho=*) + host_alias=$ac_optarg ;; + + -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht) + ac_prev=htmldir ;; + -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \ + | --ht=*) + htmldir=$ac_optarg ;; + + -includedir | --includedir | --includedi | --included | --include \ + | --includ | --inclu | --incl | --inc) + ac_prev=includedir ;; + -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ + | --includ=* | --inclu=* | --incl=* | --inc=*) + includedir=$ac_optarg ;; + + -infodir | --infodir | --infodi | --infod | --info | --inf) + ac_prev=infodir ;; + -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) + infodir=$ac_optarg ;; + + -libdir | --libdir | --libdi | --libd) + ac_prev=libdir ;; + -libdir=* | --libdir=* | --libdi=* | --libd=*) + libdir=$ac_optarg ;; + + -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ + | --libexe | --libex | --libe) + ac_prev=libexecdir ;; + -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ + | --libexe=* | --libex=* | --libe=*) + libexecdir=$ac_optarg ;; + + -localedir | --localedir | --localedi | --localed | --locale) + ac_prev=localedir ;; + -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*) + localedir=$ac_optarg ;; + + -localstatedir | --localstatedir | --localstatedi | --localstated \ + | --localstate | --localstat | --localsta | --localst | --locals) + ac_prev=localstatedir ;; + -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ + | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*) + localstatedir=$ac_optarg ;; + + -mandir | --mandir | --mandi | --mand | --man | --ma | --m) + ac_prev=mandir ;; + -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) + mandir=$ac_optarg ;; + + -nfp | --nfp | --nf) + # Obsolete; use --without-fp. + with_fp=no ;; + + -no-create | --no-create | --no-creat | --no-crea | --no-cre \ + | --no-cr | --no-c | -n) + no_create=yes ;; + + -no-recursion | --no-recursion | --no-recursio | --no-recursi \ + | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) + no_recursion=yes ;; + + -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ + | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ + | --oldin | --oldi | --old | --ol | --o) + ac_prev=oldincludedir ;; + -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ + | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ + | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) + oldincludedir=$ac_optarg ;; + + -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) + ac_prev=prefix ;; + -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) + prefix=$ac_optarg ;; + + -program-prefix | --program-prefix | --program-prefi | --program-pref \ + | --program-pre | --program-pr | --program-p) + ac_prev=program_prefix ;; + -program-prefix=* | --program-prefix=* | --program-prefi=* \ + | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) + program_prefix=$ac_optarg ;; + + -program-suffix | --program-suffix | --program-suffi | --program-suff \ + | --program-suf | --program-su | --program-s) + ac_prev=program_suffix ;; + -program-suffix=* | --program-suffix=* | --program-suffi=* \ + | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) + program_suffix=$ac_optarg ;; + + -program-transform-name | --program-transform-name \ + | --program-transform-nam | --program-transform-na \ + | --program-transform-n | --program-transform- \ + | --program-transform | --program-transfor \ + | --program-transfo | --program-transf \ + | --program-trans | --program-tran \ + | --progr-tra | --program-tr | --program-t) + ac_prev=program_transform_name ;; + -program-transform-name=* | --program-transform-name=* \ + | --program-transform-nam=* | --program-transform-na=* \ + | --program-transform-n=* | --program-transform-=* \ + | --program-transform=* | --program-transfor=* \ + | --program-transfo=* | --program-transf=* \ + | --program-trans=* | --program-tran=* \ + | --progr-tra=* | --program-tr=* | --program-t=*) + program_transform_name=$ac_optarg ;; + + -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd) + ac_prev=pdfdir ;; + -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*) + pdfdir=$ac_optarg ;; + + -psdir | --psdir | --psdi | --psd | --ps) + ac_prev=psdir ;; + -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*) + psdir=$ac_optarg ;; + + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + silent=yes ;; + + -runstatedir | --runstatedir | --runstatedi | --runstated \ + | --runstate | --runstat | --runsta | --runst | --runs \ + | --run | --ru | --r) + ac_prev=runstatedir ;; + -runstatedir=* | --runstatedir=* | --runstatedi=* | --runstated=* \ + | --runstate=* | --runstat=* | --runsta=* | --runst=* | --runs=* \ + | --run=* | --ru=* | --r=*) + runstatedir=$ac_optarg ;; + + -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) + ac_prev=sbindir ;; + -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ + | --sbi=* | --sb=*) + sbindir=$ac_optarg ;; + + -sharedstatedir | --sharedstatedir | --sharedstatedi \ + | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ + | --sharedst | --shareds | --shared | --share | --shar \ + | --sha | --sh) + ac_prev=sharedstatedir ;; + -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ + | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ + | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ + | --sha=* | --sh=*) + sharedstatedir=$ac_optarg ;; + + -site | --site | --sit) + ac_prev=site ;; + -site=* | --site=* | --sit=*) + site=$ac_optarg ;; + + -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) + ac_prev=srcdir ;; + -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) + srcdir=$ac_optarg ;; + + -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ + | --syscon | --sysco | --sysc | --sys | --sy) + ac_prev=sysconfdir ;; + -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ + | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) + sysconfdir=$ac_optarg ;; + + -target | --target | --targe | --targ | --tar | --ta | --t) + ac_prev=target_alias ;; + -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) + target_alias=$ac_optarg ;; + + -v | -verbose | --verbose | --verbos | --verbo | --verb) + verbose=yes ;; + + -version | --version | --versio | --versi | --vers | -V) + ac_init_version=: ;; + + -with-* | --with-*) + ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error $? "invalid package name: $ac_useropt" + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"with_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval with_$ac_useropt=\$ac_optarg ;; + + -without-* | --without-*) + ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error $? "invalid package name: $ac_useropt" + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"with_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval with_$ac_useropt=no ;; + + --x) + # Obsolete; use --with-x. + with_x=yes ;; + + -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ + | --x-incl | --x-inc | --x-in | --x-i) + ac_prev=x_includes ;; + -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ + | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) + x_includes=$ac_optarg ;; + + -x-libraries | --x-libraries | --x-librarie | --x-librari \ + | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) + ac_prev=x_libraries ;; + -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ + | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) + x_libraries=$ac_optarg ;; + + -*) as_fn_error $? "unrecognized option: \`$ac_option' +Try \`$0 --help' for more information" + ;; + + *=*) + ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` + # Reject names that are not valid shell variable names. + case $ac_envvar in #( + '' | [0-9]* | *[!_$as_cr_alnum]* ) + as_fn_error $? "invalid variable name: \`$ac_envvar'" ;; + esac + eval $ac_envvar=\$ac_optarg + export $ac_envvar ;; + + *) + # FIXME: should be removed in autoconf 3.0. + $as_echo "$as_me: WARNING: you should use --build, --host, --target" >&2 + expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && + $as_echo "$as_me: WARNING: invalid host type: $ac_option" >&2 + : "${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}" + ;; + + esac +done + +if test -n "$ac_prev"; then + ac_option=--`echo $ac_prev | sed 's/_/-/g'` + as_fn_error $? "missing argument to $ac_option" +fi + +if test -n "$ac_unrecognized_opts"; then + case $enable_option_checking in + no) ;; + fatal) as_fn_error $? "unrecognized options: $ac_unrecognized_opts" ;; + *) $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;; + esac +fi + +# Check all directory arguments for consistency. +for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ + datadir sysconfdir sharedstatedir localstatedir includedir \ + oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ + libdir localedir mandir runstatedir +do + eval ac_val=\$$ac_var + # Remove trailing slashes. + case $ac_val in + */ ) + ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'` + eval $ac_var=\$ac_val;; + esac + # Be sure to have absolute directory names. + case $ac_val in + [\\/$]* | ?:[\\/]* ) continue;; + NONE | '' ) case $ac_var in *prefix ) continue;; esac;; + esac + as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val" +done + +# There might be people who depend on the old broken behavior: `$host' +# used to hold the argument of --host etc. +# FIXME: To remove some day. +build=$build_alias +host=$host_alias +target=$target_alias + +# FIXME: To remove some day. +if test "x$host_alias" != x; then + if test "x$build_alias" = x; then + cross_compiling=maybe + elif test "x$build_alias" != "x$host_alias"; then + cross_compiling=yes + fi +fi + +ac_tool_prefix= +test -n "$host_alias" && ac_tool_prefix=$host_alias- + +test "$silent" = yes && exec 6>/dev/null + + +ac_pwd=`pwd` && test -n "$ac_pwd" && +ac_ls_di=`ls -di .` && +ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` || + as_fn_error $? "working directory cannot be determined" +test "X$ac_ls_di" = "X$ac_pwd_ls_di" || + as_fn_error $? "pwd does not report name of working directory" + + +# Find the source files, if location was not specified. +if test -z "$srcdir"; then + ac_srcdir_defaulted=yes + # Try the directory containing this script, then the parent directory. + ac_confdir=`$as_dirname -- "$as_myself" || +$as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_myself" : 'X\(//\)[^/]' \| \ + X"$as_myself" : 'X\(//\)$' \| \ + X"$as_myself" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$as_myself" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + srcdir=$ac_confdir + if test ! -r "$srcdir/$ac_unique_file"; then + srcdir=.. + fi +else + ac_srcdir_defaulted=no +fi +if test ! -r "$srcdir/$ac_unique_file"; then + test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .." + as_fn_error $? "cannot find sources ($ac_unique_file) in $srcdir" +fi +ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work" +ac_abs_confdir=`( + cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error $? "$ac_msg" + pwd)` +# When building in place, set srcdir=. +if test "$ac_abs_confdir" = "$ac_pwd"; then + srcdir=. +fi +# Remove unnecessary trailing slashes from srcdir. +# Double slashes in file names in object file debugging info +# mess up M-x gdb in Emacs. +case $srcdir in +*/) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;; +esac +for ac_var in $ac_precious_vars; do + eval ac_env_${ac_var}_set=\${${ac_var}+set} + eval ac_env_${ac_var}_value=\$${ac_var} + eval ac_cv_env_${ac_var}_set=\${${ac_var}+set} + eval ac_cv_env_${ac_var}_value=\$${ac_var} +done + +# +# Report the --help message. +# +if test "$ac_init_help" = "long"; then + # Omit some internal or obsolete options to make the list less imposing. + # This message is too long to be a string in the A/UX 3.1 sh. + cat <<_ACEOF +\`configure' configures igraph 1.3.0.9016 to adapt to many kinds of systems. + +Usage: $0 [OPTION]... [VAR=VALUE]... + +To assign environment variables (e.g., CC, CFLAGS...), specify them as +VAR=VALUE. See below for descriptions of some of the useful variables. + +Defaults for the options are specified in brackets. + +Configuration: + -h, --help display this help and exit + --help=short display options specific to this package + --help=recursive display the short help of all the included packages + -V, --version display version information and exit + -q, --quiet, --silent do not print \`checking ...' messages + --cache-file=FILE cache test results in FILE [disabled] + -C, --config-cache alias for \`--cache-file=config.cache' + -n, --no-create do not create output files + --srcdir=DIR find the sources in DIR [configure dir or \`..'] + +Installation directories: + --prefix=PREFIX install architecture-independent files in PREFIX + [$ac_default_prefix] + --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX + [PREFIX] + +By default, \`make install' will install all the files in +\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify +an installation prefix other than \`$ac_default_prefix' using \`--prefix', +for instance \`--prefix=\$HOME'. + +For better control, use the options below. + +Fine tuning of the installation directories: + --bindir=DIR user executables [EPREFIX/bin] + --sbindir=DIR system admin executables [EPREFIX/sbin] + --libexecdir=DIR program executables [EPREFIX/libexec] + --sysconfdir=DIR read-only single-machine data [PREFIX/etc] + --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] + --localstatedir=DIR modifiable single-machine data [PREFIX/var] + --runstatedir=DIR modifiable per-process data [LOCALSTATEDIR/run] + --libdir=DIR object code libraries [EPREFIX/lib] + --includedir=DIR C header files [PREFIX/include] + --oldincludedir=DIR C header files for non-gcc [/usr/include] + --datarootdir=DIR read-only arch.-independent data root [PREFIX/share] + --datadir=DIR read-only architecture-independent data [DATAROOTDIR] + --infodir=DIR info documentation [DATAROOTDIR/info] + --localedir=DIR locale-dependent data [DATAROOTDIR/locale] + --mandir=DIR man documentation [DATAROOTDIR/man] + --docdir=DIR documentation root [DATAROOTDIR/doc/igraph] + --htmldir=DIR html documentation [DOCDIR] + --dvidir=DIR dvi documentation [DOCDIR] + --pdfdir=DIR pdf documentation [DOCDIR] + --psdir=DIR ps documentation [DOCDIR] +_ACEOF + + cat <<\_ACEOF +_ACEOF +fi + +if test -n "$ac_init_help"; then + case $ac_init_help in + short | recursive ) echo "Configuration of igraph 1.3.0.9016:";; + esac + cat <<\_ACEOF + +Optional Features: + --disable-option-checking ignore unrecognized --enable/--with options + --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) + --enable-FEATURE[=ARG] include FEATURE [ARG=yes] + --disable-graphml Disable support for GraphML format + --disable-glpk Compile without the GLPK library + +Some influential environment variables: + CC C compiler command + CFLAGS C compiler flags + LDFLAGS linker flags, e.g. -L if you have libraries in a + nonstandard directory + LIBS libraries to pass to the linker, e.g. -l + CPPFLAGS (Objective) C/C++ preprocessor flags, e.g. -I if + you have headers in a nonstandard directory + CXX C++ compiler command + CXXFLAGS C++ compiler flags + FC Fortran compiler command + FCFLAGS Fortran compiler flags + CXXCPP C++ preprocessor + +Use these variables to override the choices made by `configure' or to help +it to find libraries and programs with nonstandard names/locations. + +Report bugs to . +_ACEOF +ac_status=$? +fi + +if test "$ac_init_help" = "recursive"; then + # If there are subdirs, report their specific --help. + for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue + test -d "$ac_dir" || + { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } || + continue + ac_builddir=. + +case "$ac_dir" in +.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; +*) + ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` + # A ".." for each directory in $ac_dir_suffix. + ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` + case $ac_top_builddir_sub in + "") ac_top_builddir_sub=. ac_top_build_prefix= ;; + *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; + esac ;; +esac +ac_abs_top_builddir=$ac_pwd +ac_abs_builddir=$ac_pwd$ac_dir_suffix +# for backward compatibility: +ac_top_builddir=$ac_top_build_prefix + +case $srcdir in + .) # We are building in place. + ac_srcdir=. + ac_top_srcdir=$ac_top_builddir_sub + ac_abs_top_srcdir=$ac_pwd ;; + [\\/]* | ?:[\\/]* ) # Absolute name. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir + ac_abs_top_srcdir=$srcdir ;; + *) # Relative name. + ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_build_prefix$srcdir + ac_abs_top_srcdir=$ac_pwd/$srcdir ;; +esac +ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix + + cd "$ac_dir" || { ac_status=$?; continue; } + # Check for guested configure. + if test -f "$ac_srcdir/configure.gnu"; then + echo && + $SHELL "$ac_srcdir/configure.gnu" --help=recursive + elif test -f "$ac_srcdir/configure"; then + echo && + $SHELL "$ac_srcdir/configure" --help=recursive + else + $as_echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2 + fi || ac_status=$? + cd "$ac_pwd" || { ac_status=$?; break; } + done +fi + +test -n "$ac_init_help" && exit $ac_status +if $ac_init_version; then + cat <<\_ACEOF +igraph configure 1.3.0.9016 +generated by GNU Autoconf 2.69 + +Copyright (C) 2012 Free Software Foundation, Inc. +This configure script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it. +_ACEOF + exit +fi + +## ------------------------ ## +## Autoconf initialization. ## +## ------------------------ ## + +# ac_fn_c_try_compile LINENO +# -------------------------- +# Try to compile conftest.$ac_ext, and return whether this succeeded. +ac_fn_c_try_compile () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + rm -f conftest.$ac_objext + if { { ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compile") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then : + ac_retval=0 +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_c_try_compile + +# ac_fn_cxx_try_compile LINENO +# ---------------------------- +# Try to compile conftest.$ac_ext, and return whether this succeeded. +ac_fn_cxx_try_compile () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + rm -f conftest.$ac_objext + if { { ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compile") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { + test -z "$ac_cxx_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then : + ac_retval=0 +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_cxx_try_compile + +# ac_fn_fc_try_compile LINENO +# --------------------------- +# Try to compile conftest.$ac_ext, and return whether this succeeded. +ac_fn_fc_try_compile () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + rm -f conftest.$ac_objext + if { { ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compile") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { + test -z "$ac_fc_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then : + ac_retval=0 +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_fc_try_compile + +# ac_fn_cxx_try_link LINENO +# ------------------------- +# Try to link conftest.$ac_ext, and return whether this succeeded. +ac_fn_cxx_try_link () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + rm -f conftest.$ac_objext conftest$ac_exeext + if { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { + test -z "$ac_cxx_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && { + test "$cross_compiling" = yes || + test -x conftest$ac_exeext + }; then : + ac_retval=0 +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information + # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would + # interfere with the next link command; also delete a directory that is + # left behind by Apple's compiler. We do this before executing the actions. + rm -rf conftest.dSYM conftest_ipa8_conftest.oo + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_cxx_try_link + +# ac_fn_cxx_check_func LINENO FUNC VAR +# ------------------------------------ +# Tests whether FUNC exists, setting the cache variable VAR accordingly +ac_fn_cxx_check_func () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +$as_echo_n "checking for $2... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +/* Define $2 to an innocuous variant, in case declares $2. + For example, HP-UX 11i declares gettimeofday. */ +#define $2 innocuous_$2 + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $2 (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef $2 + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char $2 (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined __stub_$2 || defined __stub___$2 +choke me +#endif + +int +main () +{ +return $2 (); + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_link "$LINENO"; then : + eval "$3=yes" +else + eval "$3=no" +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + +} # ac_fn_cxx_check_func + +# ac_fn_cxx_try_cpp LINENO +# ------------------------ +# Try to preprocess conftest.$ac_ext, and return whether this succeeded. +ac_fn_cxx_try_cpp () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + if { { ac_try="$ac_cpp conftest.$ac_ext" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_cpp conftest.$ac_ext") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } > conftest.i && { + test -z "$ac_cxx_preproc_warn_flag$ac_cxx_werror_flag" || + test ! -s conftest.err + }; then : + ac_retval=0 +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_cxx_try_cpp + +# ac_fn_cxx_check_header_mongrel LINENO HEADER VAR INCLUDES +# --------------------------------------------------------- +# Tests whether HEADER exists, giving a warning if it cannot be compiled using +# the include files in INCLUDES and setting the cache variable VAR +# accordingly. +ac_fn_cxx_check_header_mongrel () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + if eval \${$3+:} false; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +$as_echo_n "checking for $2... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +else + # Is the header compilable? +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 usability" >&5 +$as_echo_n "checking $2 usability... " >&6; } +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +#include <$2> +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + ac_header_compiler=yes +else + ac_header_compiler=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_compiler" >&5 +$as_echo "$ac_header_compiler" >&6; } + +# Is the header present? +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 presence" >&5 +$as_echo_n "checking $2 presence... " >&6; } +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <$2> +_ACEOF +if ac_fn_cxx_try_cpp "$LINENO"; then : + ac_header_preproc=yes +else + ac_header_preproc=no +fi +rm -f conftest.err conftest.i conftest.$ac_ext +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_preproc" >&5 +$as_echo "$ac_header_preproc" >&6; } + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_cxx_preproc_warn_flag in #(( + yes:no: ) + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&5 +$as_echo "$as_me: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 +$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} + ;; + no:yes:* ) + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: present but cannot be compiled" >&5 +$as_echo "$as_me: WARNING: $2: present but cannot be compiled" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: check for missing prerequisite headers?" >&5 +$as_echo "$as_me: WARNING: $2: check for missing prerequisite headers?" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: see the Autoconf documentation" >&5 +$as_echo "$as_me: WARNING: $2: see the Autoconf documentation" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&5 +$as_echo "$as_me: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 +$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} +( $as_echo "## ------------------------------- ## +## Report this to ntamas@gmail.com ## +## ------------------------------- ##" + ) | sed "s/^/$as_me: WARNING: /" >&2 + ;; +esac + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +$as_echo_n "checking for $2... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +else + eval "$3=\$ac_header_compiler" +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +fi + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + +} # ac_fn_cxx_check_header_mongrel + +# ac_fn_cxx_try_run LINENO +# ------------------------ +# Try to link conftest.$ac_ext, and return whether this succeeded. Assumes +# that executables *can* be run. +ac_fn_cxx_try_run () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + if { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { ac_try='./conftest$ac_exeext' + { { case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; }; then : + ac_retval=0 +else + $as_echo "$as_me: program exited with status $ac_status" >&5 + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=$ac_status +fi + rm -rf conftest.dSYM conftest_ipa8_conftest.oo + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_cxx_try_run + +# ac_fn_cxx_check_header_compile LINENO HEADER VAR INCLUDES +# --------------------------------------------------------- +# Tests whether HEADER exists and can be compiled using the include files in +# INCLUDES, setting the cache variable VAR accordingly. +ac_fn_cxx_check_header_compile () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +$as_echo_n "checking for $2... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +#include <$2> +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + eval "$3=yes" +else + eval "$3=no" +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + +} # ac_fn_cxx_check_header_compile + +# ac_fn_cxx_check_member LINENO AGGR MEMBER VAR INCLUDES +# ------------------------------------------------------ +# Tries to find if the field MEMBER exists in type AGGR, after including +# INCLUDES, setting cache variable VAR accordingly. +ac_fn_cxx_check_member () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2.$3" >&5 +$as_echo_n "checking for $2.$3... " >&6; } +if eval \${$4+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$5 +int +main () +{ +static $2 ac_aggr; +if (ac_aggr.$3) +return 0; + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + eval "$4=yes" +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$5 +int +main () +{ +static $2 ac_aggr; +if (sizeof ac_aggr.$3) +return 0; + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + eval "$4=yes" +else + eval "$4=no" +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +eval ac_res=\$$4 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + +} # ac_fn_cxx_check_member +cat >config.log <<_ACEOF +This file contains any messages produced by compilers while +running configure, to aid debugging if configure makes a mistake. + +It was created by igraph $as_me 1.3.0.9016, which was +generated by GNU Autoconf 2.69. Invocation command line was + + $ $0 $@ + +_ACEOF +exec 5>>config.log +{ +cat <<_ASUNAME +## --------- ## +## Platform. ## +## --------- ## + +hostname = `(hostname || uname -n) 2>/dev/null | sed 1q` +uname -m = `(uname -m) 2>/dev/null || echo unknown` +uname -r = `(uname -r) 2>/dev/null || echo unknown` +uname -s = `(uname -s) 2>/dev/null || echo unknown` +uname -v = `(uname -v) 2>/dev/null || echo unknown` + +/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown` +/bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` + +/bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` +/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` +/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` +/usr/bin/hostinfo = `(/usr/bin/hostinfo) 2>/dev/null || echo unknown` +/bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` +/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` +/bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` + +_ASUNAME + +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + $as_echo "PATH: $as_dir" + done +IFS=$as_save_IFS + +} >&5 + +cat >&5 <<_ACEOF + + +## ----------- ## +## Core tests. ## +## ----------- ## + +_ACEOF + + +# Keep a trace of the command line. +# Strip out --no-create and --no-recursion so they do not pile up. +# Strip out --silent because we don't want to record it for future runs. +# Also quote any args containing shell meta-characters. +# Make two passes to allow for proper duplicate-argument suppression. +ac_configure_args= +ac_configure_args0= +ac_configure_args1= +ac_must_keep_next=false +for ac_pass in 1 2 +do + for ac_arg + do + case $ac_arg in + -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;; + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + continue ;; + *\'*) + ac_arg=`$as_echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; + esac + case $ac_pass in + 1) as_fn_append ac_configure_args0 " '$ac_arg'" ;; + 2) + as_fn_append ac_configure_args1 " '$ac_arg'" + if test $ac_must_keep_next = true; then + ac_must_keep_next=false # Got value, back to normal. + else + case $ac_arg in + *=* | --config-cache | -C | -disable-* | --disable-* \ + | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \ + | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \ + | -with-* | --with-* | -without-* | --without-* | --x) + case "$ac_configure_args0 " in + "$ac_configure_args1"*" '$ac_arg' "* ) continue ;; + esac + ;; + -* ) ac_must_keep_next=true ;; + esac + fi + as_fn_append ac_configure_args " '$ac_arg'" + ;; + esac + done +done +{ ac_configure_args0=; unset ac_configure_args0;} +{ ac_configure_args1=; unset ac_configure_args1;} + +# When interrupted or exit'd, cleanup temporary files, and complete +# config.log. We remove comments because anyway the quotes in there +# would cause problems or look ugly. +# WARNING: Use '\'' to represent an apostrophe within the trap. +# WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug. +trap 'exit_status=$? + # Save into config.log some information that might help in debugging. + { + echo + + $as_echo "## ---------------- ## +## Cache variables. ## +## ---------------- ##" + echo + # The following way of writing the cache mishandles newlines in values, +( + for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do + eval ac_val=\$$ac_var + case $ac_val in #( + *${as_nl}*) + case $ac_var in #( + *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 +$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; + esac + case $ac_var in #( + _ | IFS | as_nl) ;; #( + BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( + *) { eval $ac_var=; unset $ac_var;} ;; + esac ;; + esac + done + (set) 2>&1 | + case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #( + *${as_nl}ac_space=\ *) + sed -n \ + "s/'\''/'\''\\\\'\'''\''/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p" + ;; #( + *) + sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" + ;; + esac | + sort +) + echo + + $as_echo "## ----------------- ## +## Output variables. ## +## ----------------- ##" + echo + for ac_var in $ac_subst_vars + do + eval ac_val=\$$ac_var + case $ac_val in + *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; + esac + $as_echo "$ac_var='\''$ac_val'\''" + done | sort + echo + + if test -n "$ac_subst_files"; then + $as_echo "## ------------------- ## +## File substitutions. ## +## ------------------- ##" + echo + for ac_var in $ac_subst_files + do + eval ac_val=\$$ac_var + case $ac_val in + *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; + esac + $as_echo "$ac_var='\''$ac_val'\''" + done | sort + echo + fi + + if test -s confdefs.h; then + $as_echo "## ----------- ## +## confdefs.h. ## +## ----------- ##" + echo + cat confdefs.h + echo + fi + test "$ac_signal" != 0 && + $as_echo "$as_me: caught signal $ac_signal" + $as_echo "$as_me: exit $exit_status" + } >&5 + rm -f core *.core core.conftest.* && + rm -f -r conftest* confdefs* conf$$* $ac_clean_files && + exit $exit_status +' 0 +for ac_signal in 1 2 13 15; do + trap 'ac_signal='$ac_signal'; as_fn_exit 1' $ac_signal +done +ac_signal=0 + +# confdefs.h avoids OS command line length limits that DEFS can exceed. +rm -f -r conftest* confdefs.h + +$as_echo "/* confdefs.h */" > confdefs.h + +# Predefined preprocessor variables. + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_NAME "$PACKAGE_NAME" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_TARNAME "$PACKAGE_TARNAME" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_VERSION "$PACKAGE_VERSION" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_STRING "$PACKAGE_STRING" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_URL "$PACKAGE_URL" +_ACEOF + + +# Let the site file select an alternate cache file if it wants to. +# Prefer an explicitly selected file to automatically selected ones. +ac_site_file1=NONE +ac_site_file2=NONE +if test -n "$CONFIG_SITE"; then + # We do not want a PATH search for config.site. + case $CONFIG_SITE in #(( + -*) ac_site_file1=./$CONFIG_SITE;; + */*) ac_site_file1=$CONFIG_SITE;; + *) ac_site_file1=./$CONFIG_SITE;; + esac +elif test "x$prefix" != xNONE; then + ac_site_file1=$prefix/share/config.site + ac_site_file2=$prefix/etc/config.site +else + ac_site_file1=$ac_default_prefix/share/config.site + ac_site_file2=$ac_default_prefix/etc/config.site +fi +for ac_site_file in "$ac_site_file1" "$ac_site_file2" +do + test "x$ac_site_file" = xNONE && continue + if test /dev/null != "$ac_site_file" && test -r "$ac_site_file"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5 +$as_echo "$as_me: loading site script $ac_site_file" >&6;} + sed 's/^/| /' "$ac_site_file" >&5 + . "$ac_site_file" \ + || { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "failed to load site script $ac_site_file +See \`config.log' for more details" "$LINENO" 5; } + fi +done + +if test -r "$cache_file"; then + # Some versions of bash will fail to source /dev/null (special files + # actually), so we avoid doing that. DJGPP emulates it as a regular file. + if test /dev/null != "$cache_file" && test -f "$cache_file"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5 +$as_echo "$as_me: loading cache $cache_file" >&6;} + case $cache_file in + [\\/]* | ?:[\\/]* ) . "$cache_file";; + *) . "./$cache_file";; + esac + fi +else + { $as_echo "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5 +$as_echo "$as_me: creating cache $cache_file" >&6;} + >$cache_file +fi + +# Check that the precious variables saved in the cache have kept the same +# value. +ac_cache_corrupted=false +for ac_var in $ac_precious_vars; do + eval ac_old_set=\$ac_cv_env_${ac_var}_set + eval ac_new_set=\$ac_env_${ac_var}_set + eval ac_old_val=\$ac_cv_env_${ac_var}_value + eval ac_new_val=\$ac_env_${ac_var}_value + case $ac_old_set,$ac_new_set in + set,) + { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 +$as_echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,set) + { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5 +$as_echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,);; + *) + if test "x$ac_old_val" != "x$ac_new_val"; then + # differences in whitespace do not lead to failure. + ac_old_val_w=`echo x $ac_old_val` + ac_new_val_w=`echo x $ac_new_val` + if test "$ac_old_val_w" != "$ac_new_val_w"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5 +$as_echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} + ac_cache_corrupted=: + else + { $as_echo "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5 +$as_echo "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;} + eval $ac_var=\$ac_old_val + fi + { $as_echo "$as_me:${as_lineno-$LINENO}: former value: \`$ac_old_val'" >&5 +$as_echo "$as_me: former value: \`$ac_old_val'" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: current value: \`$ac_new_val'" >&5 +$as_echo "$as_me: current value: \`$ac_new_val'" >&2;} + fi;; + esac + # Pass precious variables to config.status. + if test "$ac_new_set" = set; then + case $ac_new_val in + *\'*) ac_arg=$ac_var=`$as_echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; + *) ac_arg=$ac_var=$ac_new_val ;; + esac + case " $ac_configure_args " in + *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy. + *) as_fn_append ac_configure_args " '$ac_arg'" ;; + esac + fi +done +if $ac_cache_corrupted; then + { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5 +$as_echo "$as_me: error: changes in the environment can compromise the build" >&2;} + as_fn_error $? "run \`make distclean' and/or \`rm $cache_file' and start over" "$LINENO" 5 +fi +## -------------------- ## +## Main body of script. ## +## -------------------- ## + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + + +ac_config_headers="$ac_config_headers src/config.h" + + +: ${R_HOME=`R RHOME`} +if test -z "${R_HOME}"; then + echo "could not determine R_HOME" + exit 1 +fi +CC=`"${R_HOME}/bin/R" CMD config CC` +CXX=`"${R_HOME}/bin/R" CMD config CXX11` +if test -z "$CXX"; then + as_fn_error $? "No C++11 compiler is available" "$LINENO" 5 +fi +CXX11STD=`"${R_HOME}/bin/R" CMD config CXX11STD` +CXX="${CXX} ${CXX11STD}" +FC=`"${R_HOME}/bin/R" CMD config FC` +CFLAGS=`"${R_HOME}/bin/R" CMD config CFLAGS` +CXXFLAGS=`"${R_HOME}/bin/R" CMD config CXX11FLAGS` +CPPFLAGS=`"${R_HOME}/bin/R" CMD config CPPFLAGS` +FCFLAGS=`"${R_HOME}/bin/R" CMD config FCFLAGS` +FLIBS=`"${R_HOME}/bin/R" CMD config FLIBS` +LDFLAGS=`"${R_HOME}/bin/R" CMD config LDFLAGS` + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. +set dummy ${ac_tool_prefix}gcc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="${ac_tool_prefix}gcc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_CC"; then + ac_ct_CC=$CC + # Extract the first word of "gcc", so it can be a program name with args. +set dummy gcc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="gcc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 +$as_echo "$ac_ct_CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_CC" = x; then + CC="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + CC=$ac_ct_CC + fi +else + CC="$ac_cv_prog_CC" +fi + +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. +set dummy ${ac_tool_prefix}cc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="${ac_tool_prefix}cc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + fi +fi +if test -z "$CC"; then + # Extract the first word of "cc", so it can be a program name with args. +set dummy cc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + ac_prog_rejected=no +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then + ac_prog_rejected=yes + continue + fi + ac_cv_prog_CC="cc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +if test $ac_prog_rejected = yes; then + # We found a bogon in the path, so make sure we never use it. + set dummy $ac_cv_prog_CC + shift + if test $# != 0; then + # We chose a different compiler from the bogus one. + # However, it has the same basename, so the bogon will be chosen + # first if we set CC to just the basename; use the full file name. + shift + ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" + fi +fi +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + for ac_prog in cl.exe + do + # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. +set dummy $ac_tool_prefix$ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="$ac_tool_prefix$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$CC" && break + done +fi +if test -z "$CC"; then + ac_ct_CC=$CC + for ac_prog in cl.exe +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 +$as_echo "$ac_ct_CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$ac_ct_CC" && break +done + + if test "x$ac_ct_CC" = x; then + CC="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + CC=$ac_ct_CC + fi +fi + +fi + + +test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "no acceptable C compiler found in \$PATH +See \`config.log' for more details" "$LINENO" 5; } + +# Provide some information about the compiler. +$as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5 +set X $ac_compile +ac_compiler=$2 +for ac_option in --version -v -V -qversion; do + { { ac_try="$ac_compiler $ac_option >&5" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compiler $ac_option >&5") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + sed '10a\ +... rest of stderr output deleted ... + 10q' conftest.err >conftest.er1 + cat conftest.er1 >&5 + fi + rm -f conftest.er1 conftest.err + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } +done + +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out" +# Try to create an executable without -o first, disregard a.out. +# It will help us diagnose broken compilers, and finding out an intuition +# of exeext. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler works" >&5 +$as_echo_n "checking whether the C compiler works... " >&6; } +ac_link_default=`$as_echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'` + +# The possible output files: +ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*" + +ac_rmfiles= +for ac_file in $ac_files +do + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; + * ) ac_rmfiles="$ac_rmfiles $ac_file";; + esac +done +rm -f $ac_rmfiles + +if { { ac_try="$ac_link_default" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link_default") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then : + # Autoconf-2.13 could set the ac_cv_exeext variable to `no'. +# So ignore a value of `no', otherwise this would lead to `EXEEXT = no' +# in a Makefile. We should not override ac_cv_exeext if it was cached, +# so that the user can short-circuit this test for compilers unknown to +# Autoconf. +for ac_file in $ac_files '' +do + test -f "$ac_file" || continue + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) + ;; + [ab].out ) + # We found the default executable, but exeext='' is most + # certainly right. + break;; + *.* ) + if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no; + then :; else + ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + fi + # We set ac_cv_exeext here because the later test for it is not + # safe: cross compilers may not add the suffix if given an `-o' + # argument, so we may need to know it at that point already. + # Even if this section looks crufty: it has the advantage of + # actually working. + break;; + * ) + break;; + esac +done +test "$ac_cv_exeext" = no && ac_cv_exeext= + +else + ac_file='' +fi +if test -z "$ac_file"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +$as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error 77 "C compiler cannot create executables +See \`config.log' for more details" "$LINENO" 5; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5 +$as_echo_n "checking for C compiler default output file name... " >&6; } +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5 +$as_echo "$ac_file" >&6; } +ac_exeext=$ac_cv_exeext + +rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out +ac_clean_files=$ac_clean_files_save +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5 +$as_echo_n "checking for suffix of executables... " >&6; } +if { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then : + # If both `conftest.exe' and `conftest' are `present' (well, observable) +# catch `conftest.exe'. For instance with Cygwin, `ls conftest' will +# work properly (i.e., refer to `conftest.exe'), while it won't with +# `rm'. +for ac_file in conftest.exe conftest conftest.*; do + test -f "$ac_file" || continue + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; + *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + break;; + * ) break;; + esac +done +else + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "cannot compute suffix of executables: cannot compile and link +See \`config.log' for more details" "$LINENO" 5; } +fi +rm -f conftest conftest$ac_cv_exeext +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5 +$as_echo "$ac_cv_exeext" >&6; } + +rm -f conftest.$ac_ext +EXEEXT=$ac_cv_exeext +ac_exeext=$EXEEXT +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +int +main () +{ +FILE *f = fopen ("conftest.out", "w"); + return ferror (f) || fclose (f) != 0; + + ; + return 0; +} +_ACEOF +ac_clean_files="$ac_clean_files conftest.out" +# Check that the compiler produces executables we can run. If not, either +# the compiler is broken, or we cross compile. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5 +$as_echo_n "checking whether we are cross compiling... " >&6; } +if test "$cross_compiling" != yes; then + { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } + if { ac_try='./conftest$ac_cv_exeext' + { { case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; }; then + cross_compiling=no + else + if test "$cross_compiling" = maybe; then + cross_compiling=yes + else + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "cannot run C compiled programs. +If you meant to cross compile, use \`--host'. +See \`config.log' for more details" "$LINENO" 5; } + fi + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5 +$as_echo "$cross_compiling" >&6; } + +rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out +ac_clean_files=$ac_clean_files_save +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5 +$as_echo_n "checking for suffix of object files... " >&6; } +if ${ac_cv_objext+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.o conftest.obj +if { { ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compile") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then : + for ac_file in conftest.o conftest.obj conftest.*; do + test -f "$ac_file" || continue; + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;; + *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` + break;; + esac +done +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "cannot compute suffix of object files: cannot compile +See \`config.log' for more details" "$LINENO" 5; } +fi +rm -f conftest.$ac_cv_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5 +$as_echo "$ac_cv_objext" >&6; } +OBJEXT=$ac_cv_objext +ac_objext=$OBJEXT +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5 +$as_echo_n "checking whether we are using the GNU C compiler... " >&6; } +if ${ac_cv_c_compiler_gnu+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ +#ifndef __GNUC__ + choke me +#endif + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_compiler_gnu=yes +else + ac_compiler_gnu=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +ac_cv_c_compiler_gnu=$ac_compiler_gnu + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5 +$as_echo "$ac_cv_c_compiler_gnu" >&6; } +if test $ac_compiler_gnu = yes; then + GCC=yes +else + GCC= +fi +ac_test_CFLAGS=${CFLAGS+set} +ac_save_CFLAGS=$CFLAGS +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5 +$as_echo_n "checking whether $CC accepts -g... " >&6; } +if ${ac_cv_prog_cc_g+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_save_c_werror_flag=$ac_c_werror_flag + ac_c_werror_flag=yes + ac_cv_prog_cc_g=no + CFLAGS="-g" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_prog_cc_g=yes +else + CFLAGS="" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + +else + ac_c_werror_flag=$ac_save_c_werror_flag + CFLAGS="-g" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_prog_cc_g=yes +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_c_werror_flag=$ac_save_c_werror_flag +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5 +$as_echo "$ac_cv_prog_cc_g" >&6; } +if test "$ac_test_CFLAGS" = set; then + CFLAGS=$ac_save_CFLAGS +elif test $ac_cv_prog_cc_g = yes; then + if test "$GCC" = yes; then + CFLAGS="-g -O2" + else + CFLAGS="-g" + fi +else + if test "$GCC" = yes; then + CFLAGS="-O2" + else + CFLAGS= + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5 +$as_echo_n "checking for $CC option to accept ISO C89... " >&6; } +if ${ac_cv_prog_cc_c89+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_cv_prog_cc_c89=no +ac_save_CC=$CC +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +#include +struct stat; +/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ +struct buf { int x; }; +FILE * (*rcsopen) (struct buf *, struct stat *, int); +static char *e (p, i) + char **p; + int i; +{ + return p[i]; +} +static char *f (char * (*g) (char **, int), char **p, ...) +{ + char *s; + va_list v; + va_start (v,p); + s = g (p, va_arg (v,int)); + va_end (v); + return s; +} + +/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has + function prototypes and stuff, but not '\xHH' hex character constants. + These don't provoke an error unfortunately, instead are silently treated + as 'x'. The following induces an error, until -std is added to get + proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an + array size at least. It's necessary to write '\x00'==0 to get something + that's true only with -std. */ +int osf4_cc_array ['\x00' == 0 ? 1 : -1]; + +/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters + inside strings and character constants. */ +#define FOO(x) 'x' +int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1]; + +int test (int i, double x); +struct s1 {int (*f) (int a);}; +struct s2 {int (*f) (double a);}; +int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); +int argc; +char **argv; +int +main () +{ +return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; + ; + return 0; +} +_ACEOF +for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \ + -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" +do + CC="$ac_save_CC $ac_arg" + if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_prog_cc_c89=$ac_arg +fi +rm -f core conftest.err conftest.$ac_objext + test "x$ac_cv_prog_cc_c89" != "xno" && break +done +rm -f conftest.$ac_ext +CC=$ac_save_CC + +fi +# AC_CACHE_VAL +case "x$ac_cv_prog_cc_c89" in + x) + { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 +$as_echo "none needed" >&6; } ;; + xno) + { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 +$as_echo "unsupported" >&6; } ;; + *) + CC="$CC $ac_cv_prog_cc_c89" + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5 +$as_echo "$ac_cv_prog_cc_c89" >&6; } ;; +esac +if test "x$ac_cv_prog_cc_c89" != xno; then : + +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + +ac_ext=cpp +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_cxx_compiler_gnu + +ac_ext=cpp +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_cxx_compiler_gnu +if test -z "$CXX"; then + if test -n "$CCC"; then + CXX=$CCC + else + if test -n "$ac_tool_prefix"; then + for ac_prog in g++ c++ gpp aCC CC cxx cc++ cl.exe FCC KCC RCC xlC_r xlC + do + # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. +set dummy $ac_tool_prefix$ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CXX+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CXX"; then + ac_cv_prog_CXX="$CXX" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CXX="$ac_tool_prefix$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CXX=$ac_cv_prog_CXX +if test -n "$CXX"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CXX" >&5 +$as_echo "$CXX" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$CXX" && break + done +fi +if test -z "$CXX"; then + ac_ct_CXX=$CXX + for ac_prog in g++ c++ gpp aCC CC cxx cc++ cl.exe FCC KCC RCC xlC_r xlC +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_CXX+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_CXX"; then + ac_cv_prog_ac_ct_CXX="$ac_ct_CXX" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CXX="$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_CXX=$ac_cv_prog_ac_ct_CXX +if test -n "$ac_ct_CXX"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CXX" >&5 +$as_echo "$ac_ct_CXX" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$ac_ct_CXX" && break +done + + if test "x$ac_ct_CXX" = x; then + CXX="g++" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + CXX=$ac_ct_CXX + fi +fi + + fi +fi +# Provide some information about the compiler. +$as_echo "$as_me:${as_lineno-$LINENO}: checking for C++ compiler version" >&5 +set X $ac_compile +ac_compiler=$2 +for ac_option in --version -v -V -qversion; do + { { ac_try="$ac_compiler $ac_option >&5" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compiler $ac_option >&5") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + sed '10a\ +... rest of stderr output deleted ... + 10q' conftest.err >conftest.er1 + cat conftest.er1 >&5 + fi + rm -f conftest.er1 conftest.err + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } +done + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C++ compiler" >&5 +$as_echo_n "checking whether we are using the GNU C++ compiler... " >&6; } +if ${ac_cv_cxx_compiler_gnu+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ +#ifndef __GNUC__ + choke me +#endif + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + ac_compiler_gnu=yes +else + ac_compiler_gnu=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +ac_cv_cxx_compiler_gnu=$ac_compiler_gnu + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cxx_compiler_gnu" >&5 +$as_echo "$ac_cv_cxx_compiler_gnu" >&6; } +if test $ac_compiler_gnu = yes; then + GXX=yes +else + GXX= +fi +ac_test_CXXFLAGS=${CXXFLAGS+set} +ac_save_CXXFLAGS=$CXXFLAGS +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CXX accepts -g" >&5 +$as_echo_n "checking whether $CXX accepts -g... " >&6; } +if ${ac_cv_prog_cxx_g+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_save_cxx_werror_flag=$ac_cxx_werror_flag + ac_cxx_werror_flag=yes + ac_cv_prog_cxx_g=no + CXXFLAGS="-g" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + ac_cv_prog_cxx_g=yes +else + CXXFLAGS="" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + +else + ac_cxx_werror_flag=$ac_save_cxx_werror_flag + CXXFLAGS="-g" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + ac_cv_prog_cxx_g=yes +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_cxx_werror_flag=$ac_save_cxx_werror_flag +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cxx_g" >&5 +$as_echo "$ac_cv_prog_cxx_g" >&6; } +if test "$ac_test_CXXFLAGS" = set; then + CXXFLAGS=$ac_save_CXXFLAGS +elif test $ac_cv_prog_cxx_g = yes; then + if test "$GXX" = yes; then + CXXFLAGS="-g -O2" + else + CXXFLAGS="-g" + fi +else + if test "$GXX" = yes; then + CXXFLAGS="-O2" + else + CXXFLAGS= + fi +fi +ac_ext=cpp +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_cxx_compiler_gnu + + +# Fortran compiler, we need to check if it is the GNU compiler +ac_ext=${ac_fc_srcext-f} +ac_compile='$FC -c $FCFLAGS $ac_fcflags_srcext conftest.$ac_ext >&5' +ac_link='$FC -o conftest$ac_exeext $FCFLAGS $LDFLAGS $ac_fcflags_srcext conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_fc_compiler_gnu +if test -n "$ac_tool_prefix"; then + for ac_prog in gfortran g95 xlf95 f95 fort ifort ifc efc pgfortran pgf95 lf95 ftn nagfor xlf90 f90 pgf90 pghpf epcf90 g77 xlf f77 frt pgf77 cf77 fort77 fl32 af77 + do + # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. +set dummy $ac_tool_prefix$ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_FC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$FC"; then + ac_cv_prog_FC="$FC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_FC="$ac_tool_prefix$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +FC=$ac_cv_prog_FC +if test -n "$FC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $FC" >&5 +$as_echo "$FC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$FC" && break + done +fi +if test -z "$FC"; then + ac_ct_FC=$FC + for ac_prog in gfortran g95 xlf95 f95 fort ifort ifc efc pgfortran pgf95 lf95 ftn nagfor xlf90 f90 pgf90 pghpf epcf90 g77 xlf f77 frt pgf77 cf77 fort77 fl32 af77 +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_FC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_FC"; then + ac_cv_prog_ac_ct_FC="$ac_ct_FC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_FC="$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_FC=$ac_cv_prog_ac_ct_FC +if test -n "$ac_ct_FC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_FC" >&5 +$as_echo "$ac_ct_FC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$ac_ct_FC" && break +done + + if test "x$ac_ct_FC" = x; then + FC="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + FC=$ac_ct_FC + fi +fi + + +# Provide some information about the compiler. +$as_echo "$as_me:${as_lineno-$LINENO}: checking for Fortran compiler version" >&5 +set X $ac_compile +ac_compiler=$2 +for ac_option in --version -v -V -qversion; do + { { ac_try="$ac_compiler $ac_option >&5" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compiler $ac_option >&5") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + sed '10a\ +... rest of stderr output deleted ... + 10q' conftest.err >conftest.er1 + cat conftest.er1 >&5 + fi + rm -f conftest.er1 conftest.err + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } +done +rm -f a.out + +# If we don't use `.F' as extension, the preprocessor is not run on the +# input file. (Note that this only needs to work for GNU compilers.) +ac_save_ext=$ac_ext +ac_ext=F +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU Fortran compiler" >&5 +$as_echo_n "checking whether we are using the GNU Fortran compiler... " >&6; } +if ${ac_cv_fc_compiler_gnu+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat > conftest.$ac_ext <<_ACEOF + program main +#ifndef __GNUC__ + choke me +#endif + + end +_ACEOF +if ac_fn_fc_try_compile "$LINENO"; then : + ac_compiler_gnu=yes +else + ac_compiler_gnu=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +ac_cv_fc_compiler_gnu=$ac_compiler_gnu + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_fc_compiler_gnu" >&5 +$as_echo "$ac_cv_fc_compiler_gnu" >&6; } +ac_ext=$ac_save_ext +ac_test_FCFLAGS=${FCFLAGS+set} +ac_save_FCFLAGS=$FCFLAGS +FCFLAGS= +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $FC accepts -g" >&5 +$as_echo_n "checking whether $FC accepts -g... " >&6; } +if ${ac_cv_prog_fc_g+:} false; then : + $as_echo_n "(cached) " >&6 +else + FCFLAGS=-g +cat > conftest.$ac_ext <<_ACEOF + program main + + end +_ACEOF +if ac_fn_fc_try_compile "$LINENO"; then : + ac_cv_prog_fc_g=yes +else + ac_cv_prog_fc_g=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_fc_g" >&5 +$as_echo "$ac_cv_prog_fc_g" >&6; } +if test "$ac_test_FCFLAGS" = set; then + FCFLAGS=$ac_save_FCFLAGS +elif test $ac_cv_prog_fc_g = yes; then + if test "x$ac_cv_fc_compiler_gnu" = xyes; then + FCFLAGS="-g -O2" + else + FCFLAGS="-g" + fi +else + if test "x$ac_cv_fc_compiler_gnu" = xyes; then + FCFLAGS="-O2" + else + FCFLAGS= + fi +fi + +if test $ac_compiler_gnu = yes; then + GFC=yes +else + GFC= +fi +ac_ext=cpp +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_cxx_compiler_gnu + +if test "x$ac_cv_fc_compiler_gnu" = xyes; then + +$as_echo "#define HAVE_GFORTRAN 1" >>confdefs.h + +fi + +LIBS_SAVE=$LIBS +LIBS="$LIBS -lm" + +for ac_func in expm1 fmin finite log2 log1p rint rintf round stpcpy strcasecmp _stricmp strdup +do : + as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` +ac_fn_cxx_check_func "$LINENO" "$ac_func" "$as_ac_var" +if eval test \"x\$"$as_ac_var"\" = x"yes"; then : + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +fi +done + +for ac_func in isfinite +do : + ac_fn_cxx_check_func "$LINENO" "isfinite" "ac_cv_func_isfinite" +if test "x$ac_cv_func_isfinite" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_ISFINITE 1 +_ACEOF + $as_echo "#define HAVE_ISFINITE 1" >>confdefs.h + +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +int +main () +{ +double f = 0.0; isfinite(f) + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_link "$LINENO"; then : + $as_echo "#define HAVE_ISFINITE 1" >>confdefs.h + +else + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "isfinite() not available +See \`config.log' for more details" "$LINENO" 5; } + +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + +fi +done + +LIBS=$LIBS_SAVE + +ac_ext=cpp +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_cxx_compiler_gnu +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to run the C++ preprocessor" >&5 +$as_echo_n "checking how to run the C++ preprocessor... " >&6; } +if test -z "$CXXCPP"; then + if ${ac_cv_prog_CXXCPP+:} false; then : + $as_echo_n "(cached) " >&6 +else + # Double quotes because CXXCPP needs to be expanded + for CXXCPP in "$CXX -E" "/lib/cpp" + do + ac_preproc_ok=false +for ac_cxx_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer to if __STDC__ is defined, since + # exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#ifdef __STDC__ +# include +#else +# include +#endif + Syntax error +_ACEOF +if ac_fn_cxx_try_cpp "$LINENO"; then : + +else + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.i conftest.$ac_ext + + # OK, works on sane cases. Now check whether nonexistent headers + # can be detected and how. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +_ACEOF +if ac_fn_cxx_try_cpp "$LINENO"; then : + # Broken: success on invalid input. +continue +else + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.i conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.i conftest.err conftest.$ac_ext +if $ac_preproc_ok; then : + break +fi + + done + ac_cv_prog_CXXCPP=$CXXCPP + +fi + CXXCPP=$ac_cv_prog_CXXCPP +else + ac_cv_prog_CXXCPP=$CXXCPP +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $CXXCPP" >&5 +$as_echo "$CXXCPP" >&6; } +ac_preproc_ok=false +for ac_cxx_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer to if __STDC__ is defined, since + # exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#ifdef __STDC__ +# include +#else +# include +#endif + Syntax error +_ACEOF +if ac_fn_cxx_try_cpp "$LINENO"; then : + +else + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.i conftest.$ac_ext + + # OK, works on sane cases. Now check whether nonexistent headers + # can be detected and how. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +_ACEOF +if ac_fn_cxx_try_cpp "$LINENO"; then : + # Broken: success on invalid input. +continue +else + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.i conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.i conftest.err conftest.$ac_ext +if $ac_preproc_ok; then : + +else + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "C++ preprocessor \"$CXXCPP\" fails sanity check +See \`config.log' for more details" "$LINENO" 5; } +fi + +ac_ext=cpp +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_cxx_compiler_gnu + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for grep that handles long lines and -e" >&5 +$as_echo_n "checking for grep that handles long lines and -e... " >&6; } +if ${ac_cv_path_GREP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -z "$GREP"; then + ac_path_GREP_found=false + # Loop through the user's path and test for each of PROGNAME-LIST + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_prog in grep ggrep; do + for ac_exec_ext in '' $ac_executable_extensions; do + ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext" + as_fn_executable_p "$ac_path_GREP" || continue +# Check for GNU ac_path_GREP and select it if it is found. + # Check for GNU $ac_path_GREP +case `"$ac_path_GREP" --version 2>&1` in +*GNU*) + ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;; +*) + ac_count=0 + $as_echo_n 0123456789 >"conftest.in" + while : + do + cat "conftest.in" "conftest.in" >"conftest.tmp" + mv "conftest.tmp" "conftest.in" + cp "conftest.in" "conftest.nl" + $as_echo 'GREP' >> "conftest.nl" + "$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break + diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break + as_fn_arith $ac_count + 1 && ac_count=$as_val + if test $ac_count -gt ${ac_path_GREP_max-0}; then + # Best one so far, save it but keep looking for a better one + ac_cv_path_GREP="$ac_path_GREP" + ac_path_GREP_max=$ac_count + fi + # 10*(2^10) chars as input seems more than enough + test $ac_count -gt 10 && break + done + rm -f conftest.in conftest.tmp conftest.nl conftest.out;; +esac + + $ac_path_GREP_found && break 3 + done + done + done +IFS=$as_save_IFS + if test -z "$ac_cv_path_GREP"; then + as_fn_error $? "no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 + fi +else + ac_cv_path_GREP=$GREP +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_GREP" >&5 +$as_echo "$ac_cv_path_GREP" >&6; } + GREP="$ac_cv_path_GREP" + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for egrep" >&5 +$as_echo_n "checking for egrep... " >&6; } +if ${ac_cv_path_EGREP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if echo a | $GREP -E '(a|b)' >/dev/null 2>&1 + then ac_cv_path_EGREP="$GREP -E" + else + if test -z "$EGREP"; then + ac_path_EGREP_found=false + # Loop through the user's path and test for each of PROGNAME-LIST + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_prog in egrep; do + for ac_exec_ext in '' $ac_executable_extensions; do + ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext" + as_fn_executable_p "$ac_path_EGREP" || continue +# Check for GNU ac_path_EGREP and select it if it is found. + # Check for GNU $ac_path_EGREP +case `"$ac_path_EGREP" --version 2>&1` in +*GNU*) + ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;; +*) + ac_count=0 + $as_echo_n 0123456789 >"conftest.in" + while : + do + cat "conftest.in" "conftest.in" >"conftest.tmp" + mv "conftest.tmp" "conftest.in" + cp "conftest.in" "conftest.nl" + $as_echo 'EGREP' >> "conftest.nl" + "$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break + diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break + as_fn_arith $ac_count + 1 && ac_count=$as_val + if test $ac_count -gt ${ac_path_EGREP_max-0}; then + # Best one so far, save it but keep looking for a better one + ac_cv_path_EGREP="$ac_path_EGREP" + ac_path_EGREP_max=$ac_count + fi + # 10*(2^10) chars as input seems more than enough + test $ac_count -gt 10 && break + done + rm -f conftest.in conftest.tmp conftest.nl conftest.out;; +esac + + $ac_path_EGREP_found && break 3 + done + done + done +IFS=$as_save_IFS + if test -z "$ac_cv_path_EGREP"; then + as_fn_error $? "no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 + fi +else + ac_cv_path_EGREP=$EGREP +fi + + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_EGREP" >&5 +$as_echo "$ac_cv_path_EGREP" >&6; } + EGREP="$ac_cv_path_EGREP" + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ANSI C header files" >&5 +$as_echo_n "checking for ANSI C header files... " >&6; } +if ${ac_cv_header_stdc+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +#include +#include +#include + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + ac_cv_header_stdc=yes +else + ac_cv_header_stdc=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + +if test $ac_cv_header_stdc = yes; then + # SunOS 4.x string.h does not declare mem*, contrary to ANSI. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "memchr" >/dev/null 2>&1; then : + +else + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "free" >/dev/null 2>&1; then : + +else + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. + if test "$cross_compiling" = yes; then : + : +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +#include +#if ((' ' & 0x0FF) == 0x020) +# define ISLOWER(c) ('a' <= (c) && (c) <= 'z') +# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) +#else +# define ISLOWER(c) \ + (('a' <= (c) && (c) <= 'i') \ + || ('j' <= (c) && (c) <= 'r') \ + || ('s' <= (c) && (c) <= 'z')) +# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c)) +#endif + +#define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) +int +main () +{ + int i; + for (i = 0; i < 256; i++) + if (XOR (islower (i), ISLOWER (i)) + || toupper (i) != TOUPPER (i)) + return 2; + return 0; +} +_ACEOF +if ac_fn_cxx_try_run "$LINENO"; then : + +else + ac_cv_header_stdc=no +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + +fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdc" >&5 +$as_echo "$ac_cv_header_stdc" >&6; } +if test $ac_cv_header_stdc = yes; then + +$as_echo "#define STDC_HEADERS 1" >>confdefs.h + +fi + +# On IRIX 5.3, sys/types and inttypes.h are conflicting. +for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \ + inttypes.h stdint.h unistd.h +do : + as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` +ac_fn_cxx_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default +" +if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + +ac_fn_cxx_check_header_mongrel "$LINENO" "sys/times.h" "ac_cv_header_sys_times_h" "$ac_includes_default" +if test "x$ac_cv_header_sys_times_h" = xyes; then : + +$as_echo "#define HAVE_TIMES_H 1" >>confdefs.h + +fi + + + +for ac_header in \ + net/if.h \ + netinet/in.h \ + net/if_dl.h \ + sys/sockio.h \ + sys/un.h \ + sys/socket.h \ + sys/ioctl.h \ + sys/time.h \ + sys/file.h \ + +do : + as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` +ac_fn_cxx_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default" +if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + +ac_fn_cxx_check_member "$LINENO" "struct sockaddr" "sa_len" "ac_cv_member_struct_sockaddr_sa_len" "#include + #include +" +if test "x$ac_cv_member_struct_sockaddr_sa_len" = xyes; then : + +cat >>confdefs.h <<_ACEOF +#define HAVE_SA_LEN 1 +_ACEOF + +fi + + +graphml_support=yes +# Check whether --enable-graphml was given. +if test "${enable_graphml+set}" = set; then : + enableval=$enable_graphml; graphml_support=$enableval +else + graphml_support=yes +fi + + +HAVE_LIBXML=0 +if test $graphml_support = yes; then + # Extract the first word of "xml2-config", so it can be a program name with args. +set dummy xml2-config; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_path_XML2CONFIG+:} false; then : + $as_echo_n "(cached) " >&6 +else + case $XML2CONFIG in + [\\/]* | ?:[\\/]*) + ac_cv_path_XML2CONFIG="$XML2CONFIG" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_path_XML2CONFIG="$as_dir/$ac_word$ac_exec_ext" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + + test -z "$ac_cv_path_XML2CONFIG" && ac_cv_path_XML2CONFIG="none" + ;; +esac +fi +XML2CONFIG=$ac_cv_path_XML2CONFIG +if test -n "$XML2CONFIG"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $XML2CONFIG" >&5 +$as_echo "$XML2CONFIG" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + if test "$XML2CONFIG" = "none"; then + graphml_support=no + else + # xml2-config puts only preprocessor flags in --cflags (i.e. -D and -I) so + # we can put them in XML2_CPPFLAGS. This might not be true for other + # libraries, though. + XML2_CPPFLAGS=`$XML2CONFIG --cflags` + XML2_LIBS=`$XML2CONFIG --libs` + OLDLIBS=${LIBS} + LIBS=${XML2_LIBS} + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for xmlSAXUserParseFile in -lxml2" >&5 +$as_echo_n "checking for xmlSAXUserParseFile in -lxml2... " >&6; } +if ${ac_cv_lib_xml2_xmlSAXUserParseFile+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lxml2 $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char xmlSAXUserParseFile (); +int +main () +{ +return xmlSAXUserParseFile (); + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_link "$LINENO"; then : + ac_cv_lib_xml2_xmlSAXUserParseFile=yes +else + ac_cv_lib_xml2_xmlSAXUserParseFile=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_xml2_xmlSAXUserParseFile" >&5 +$as_echo "$ac_cv_lib_xml2_xmlSAXUserParseFile" >&6; } +if test "x$ac_cv_lib_xml2_xmlSAXUserParseFile" = xyes; then : + + OLDCPPFLAGS=${CPPFLAGS} + CPPFLAGS=${XML2_CPPFLAGS} + ac_fn_cxx_check_header_mongrel "$LINENO" "libxml/parser.h" "ac_cv_header_libxml_parser_h" "$ac_includes_default" +if test "x$ac_cv_header_libxml_parser_h" = xyes; then : + + HAVE_LIBXML=1 + +$as_echo "#define HAVE_LIBXML 1" >>confdefs.h + + +else + + graphml_support=no + +fi + + + CPPFLAGS=${OLDCPPFLAGS} + +else + + graphml_support=no + +fi + + LIBS=${OLDLIBS} + fi +fi + + + + + +$as_echo "#define INTERNAL_GMP 1" >>confdefs.h + + +ac_ext=cpp +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_cxx_compiler_gnu + +HAVE_GMP=0 +INTERNAL_GMP=1 +GMP_LIBS="" +gmp_support=no +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for __gmpz_add in -lgmp" >&5 +$as_echo_n "checking for __gmpz_add in -lgmp... " >&6; } +if ${ac_cv_lib_gmp___gmpz_add+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lgmp $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char __gmpz_add (); +int +main () +{ +return __gmpz_add (); + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_link "$LINENO"; then : + ac_cv_lib_gmp___gmpz_add=yes +else + ac_cv_lib_gmp___gmpz_add=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_gmp___gmpz_add" >&5 +$as_echo "$ac_cv_lib_gmp___gmpz_add" >&6; } +if test "x$ac_cv_lib_gmp___gmpz_add" = xyes; then : + + ac_fn_cxx_check_header_mongrel "$LINENO" "gmp.h" "ac_cv_header_gmp_h" "$ac_includes_default" +if test "x$ac_cv_header_gmp_h" = xyes; then : + + HAVE_GMP=1 + INTERNAL_GMP=0 + +$as_echo "#define HAVE_GMP 1" >>confdefs.h + + gmp_support=yes + GMP_LIBS="-lgmp" + +fi + + + +fi + + + + +ac_ext=cpp +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_cxx_compiler_gnu + + +HAVE_GLPK=0 +GLPK_LIBS="" +glpk_support=no +# Check whether --enable-glpk was given. +if test "${enable_glpk+set}" = set; then : + enableval=$enable_glpk; +fi + +if test "x$enable_gmp" != "xno"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for glp_read_mps in -lglpk" >&5 +$as_echo_n "checking for glp_read_mps in -lglpk... " >&6; } +if ${ac_cv_lib_glpk_glp_read_mps+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lglpk $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char glp_read_mps (); +int +main () +{ +return glp_read_mps (); + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_link "$LINENO"; then : + ac_cv_lib_glpk_glp_read_mps=yes +else + ac_cv_lib_glpk_glp_read_mps=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_glpk_glp_read_mps" >&5 +$as_echo "$ac_cv_lib_glpk_glp_read_mps" >&6; } +if test "x$ac_cv_lib_glpk_glp_read_mps" = xyes; then : + + ac_fn_cxx_check_header_mongrel "$LINENO" "glpk.h" "ac_cv_header_glpk_h" "$ac_includes_default" +if test "x$ac_cv_header_glpk_h" = xyes; then : + + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + + #include + #if GLP_MAJOR_VERSION > 4 || (GLP_MAJOR_VERSION == 4 && GLP_MINOR_VERSION >= 57) + yes + #endif + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "yes" >/dev/null 2>&1; then : + + HAVE_GLPK=1 + +$as_echo "#define HAVE_GLPK 1" >>confdefs.h + + glpk_support=yes + GLPK_LIBS="-lglpk" + +fi +rm -f conftest* + + +fi + + + +fi + +fi + + + + +$as_echo "#define IGRAPH_THREAD_LOCAL /**/" >>confdefs.h + + +ac_config_files="$ac_config_files src/Makevars.tmp:src/Makevars.in" + + +cat >confcache <<\_ACEOF +# This file is a shell script that caches the results of configure +# tests run on this system so they can be shared between configure +# scripts and configure runs, see configure's option --config-cache. +# It is not useful on other systems. If it contains results you don't +# want to keep, you may remove or edit it. +# +# config.status only pays attention to the cache file if you give it +# the --recheck option to rerun configure. +# +# `ac_cv_env_foo' variables (set or unset) will be overridden when +# loading this file, other *unset* `ac_cv_foo' will be assigned the +# following values. + +_ACEOF + +# The following way of writing the cache mishandles newlines in values, +# but we know of no workaround that is simple, portable, and efficient. +# So, we kill variables containing newlines. +# Ultrix sh set writes to stderr and can't be redirected directly, +# and sets the high bit in the cache file unless we assign to the vars. +( + for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do + eval ac_val=\$$ac_var + case $ac_val in #( + *${as_nl}*) + case $ac_var in #( + *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 +$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; + esac + case $ac_var in #( + _ | IFS | as_nl) ;; #( + BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( + *) { eval $ac_var=; unset $ac_var;} ;; + esac ;; + esac + done + + (set) 2>&1 | + case $as_nl`(ac_space=' '; set) 2>&1` in #( + *${as_nl}ac_space=\ *) + # `set' does not quote correctly, so add quotes: double-quote + # substitution turns \\\\ into \\, and sed turns \\ into \. + sed -n \ + "s/'/'\\\\''/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" + ;; #( + *) + # `set' quotes correctly as required by POSIX, so do not add quotes. + sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" + ;; + esac | + sort +) | + sed ' + /^ac_cv_env_/b end + t clear + :clear + s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/ + t end + s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ + :end' >>confcache +if diff "$cache_file" confcache >/dev/null 2>&1; then :; else + if test -w "$cache_file"; then + if test "x$cache_file" != "x/dev/null"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5 +$as_echo "$as_me: updating cache $cache_file" >&6;} + if test ! -f "$cache_file" || test -h "$cache_file"; then + cat confcache >"$cache_file" + else + case $cache_file in #( + */* | ?:*) + mv -f confcache "$cache_file"$$ && + mv -f "$cache_file"$$ "$cache_file" ;; #( + *) + mv -f confcache "$cache_file" ;; + esac + fi + fi + else + { $as_echo "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5 +$as_echo "$as_me: not updating unwritable cache $cache_file" >&6;} + fi +fi +rm -f confcache + +test "x$prefix" = xNONE && prefix=$ac_default_prefix +# Let make expand exec_prefix. +test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' + +DEFS=-DHAVE_CONFIG_H + +ac_libobjs= +ac_ltlibobjs= +U= +for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue + # 1. Remove the extension, and $U if already installed. + ac_script='s/\$U\././;s/\.o$//;s/\.obj$//' + ac_i=`$as_echo "$ac_i" | sed "$ac_script"` + # 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR + # will be set to the directory where LIBOBJS objects are built. + as_fn_append ac_libobjs " \${LIBOBJDIR}$ac_i\$U.$ac_objext" + as_fn_append ac_ltlibobjs " \${LIBOBJDIR}$ac_i"'$U.lo' +done +LIBOBJS=$ac_libobjs + +LTLIBOBJS=$ac_ltlibobjs + + + +: "${CONFIG_STATUS=./config.status}" +ac_write_fail=0 +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files $CONFIG_STATUS" +{ $as_echo "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5 +$as_echo "$as_me: creating $CONFIG_STATUS" >&6;} +as_write_fail=0 +cat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1 +#! $SHELL +# Generated by $as_me. +# Run this file to recreate the current configuration. +# Compiler output produced by configure, useful for debugging +# configure, is in config.log if it exists. + +debug=false +ac_cs_recheck=false +ac_cs_silent=false + +SHELL=\${CONFIG_SHELL-$SHELL} +export SHELL +_ASEOF +cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1 +## -------------------- ## +## M4sh Initialization. ## +## -------------------- ## + +# Be more Bourne compatible +DUALCASE=1; export DUALCASE # for MKS sh +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in #( + *posix*) : + set -o posix ;; #( + *) : + ;; +esac +fi + + +as_nl=' +' +export as_nl +# Printing a long string crashes Solaris 7 /usr/bin/printf. +as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo +# Prefer a ksh shell builtin over an external printf program on Solaris, +# but without wasting forks for bash or zsh. +if test -z "$BASH_VERSION$ZSH_VERSION" \ + && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='print -r --' + as_echo_n='print -rn --' +elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='printf %s\n' + as_echo_n='printf %s' +else + if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then + as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' + as_echo_n='/usr/ucb/echo -n' + else + as_echo_body='eval expr "X$1" : "X\\(.*\\)"' + as_echo_n_body='eval + arg=$1; + case $arg in #( + *"$as_nl"*) + expr "X$arg" : "X\\(.*\\)$as_nl"; + arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; + esac; + expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" + ' + export as_echo_n_body + as_echo_n='sh -c $as_echo_n_body as_echo' + fi + export as_echo_body + as_echo='sh -c $as_echo_body as_echo' +fi + +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + PATH_SEPARATOR=: + (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { + (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || + PATH_SEPARATOR=';' + } +fi + + +# IFS +# We need space, tab and new line, in precisely that order. Quoting is +# there to prevent editors from complaining about space-tab. +# (If _AS_PATH_WALK were called with IFS unset, it would disable word +# splitting by setting IFS to empty value.) +IFS=" "" $as_nl" + +# Find who we are. Look in the path if we contain no directory separator. +as_myself= +case $0 in #(( + *[\\/]* ) as_myself=$0 ;; + *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break + done +IFS=$as_save_IFS + + ;; +esac +# We did not find ourselves, most probably we were run as `sh COMMAND' +# in which case we are not to be found in the path. +if test "x$as_myself" = x; then + as_myself=$0 +fi +if test ! -f "$as_myself"; then + $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 + exit 1 +fi + +# Unset variables that we do not need and which cause bugs (e.g. in +# pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" +# suppresses any "Segmentation fault" message there. '((' could +# trigger a bug in pdksh 5.2.14. +for as_var in BASH_ENV ENV MAIL MAILPATH +do eval test x\${$as_var+set} = xset \ + && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : +done +PS1='$ ' +PS2='> ' +PS4='+ ' + +# NLS nuisances. +LC_ALL=C +export LC_ALL +LANGUAGE=C +export LANGUAGE + +# CDPATH. +(unset CDPATH) >/dev/null 2>&1 && unset CDPATH + + +# as_fn_error STATUS ERROR [LINENO LOG_FD] +# ---------------------------------------- +# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are +# provided, also output the error to LOG_FD, referencing LINENO. Then exit the +# script with STATUS, using 1 if that was 0. +as_fn_error () +{ + as_status=$1; test $as_status -eq 0 && as_status=1 + if test "$4"; then + as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 + fi + $as_echo "$as_me: error: $2" >&2 + as_fn_exit $as_status +} # as_fn_error + + +# as_fn_set_status STATUS +# ----------------------- +# Set $? to STATUS, without forking. +as_fn_set_status () +{ + return $1 +} # as_fn_set_status + +# as_fn_exit STATUS +# ----------------- +# Exit the shell with STATUS, even in a "trap 0" or "set -e" context. +as_fn_exit () +{ + set +e + as_fn_set_status $1 + exit $1 +} # as_fn_exit + +# as_fn_unset VAR +# --------------- +# Portably unset VAR. +as_fn_unset () +{ + { eval $1=; unset $1;} +} +as_unset=as_fn_unset +# as_fn_append VAR VALUE +# ---------------------- +# Append the text in VALUE to the end of the definition contained in VAR. Take +# advantage of any shell optimizations that allow amortized linear growth over +# repeated appends, instead of the typical quadratic growth present in naive +# implementations. +if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : + eval 'as_fn_append () + { + eval $1+=\$2 + }' +else + as_fn_append () + { + eval $1=\$$1\$2 + } +fi # as_fn_append + +# as_fn_arith ARG... +# ------------------ +# Perform arithmetic evaluation on the ARGs, and store the result in the +# global $as_val. Take advantage of shells that can avoid forks. The arguments +# must be portable across $(()) and expr. +if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : + eval 'as_fn_arith () + { + as_val=$(( $* )) + }' +else + as_fn_arith () + { + as_val=`expr "$@" || test $? -eq 1` + } +fi # as_fn_arith + + +if expr a : '\(a\)' >/dev/null 2>&1 && + test "X`expr 00001 : '.*\(...\)'`" = X001; then + as_expr=expr +else + as_expr=false +fi + +if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then + as_basename=basename +else + as_basename=false +fi + +if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then + as_dirname=dirname +else + as_dirname=false +fi + +as_me=`$as_basename -- "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ + s//\1/ + q + } + /^X\/\(\/\/\)$/{ + s//\1/ + q + } + /^X\/\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits + +ECHO_C= ECHO_N= ECHO_T= +case `echo -n x` in #((((( +-n*) + case `echo 'xy\c'` in + *c*) ECHO_T=' ';; # ECHO_T is single tab character. + xy) ECHO_C='\c';; + *) echo `echo ksh88 bug on AIX 6.1` > /dev/null + ECHO_T=' ';; + esac;; +*) + ECHO_N='-n';; +esac + +rm -f conf$$ conf$$.exe conf$$.file +if test -d conf$$.dir; then + rm -f conf$$.dir/conf$$.file +else + rm -f conf$$.dir + mkdir conf$$.dir 2>/dev/null +fi +if (echo >conf$$.file) 2>/dev/null; then + if ln -s conf$$.file conf$$ 2>/dev/null; then + as_ln_s='ln -s' + # ... but there are two gotchas: + # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. + # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. + # In both cases, we have to default to `cp -pR'. + ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || + as_ln_s='cp -pR' + elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln + else + as_ln_s='cp -pR' + fi +else + as_ln_s='cp -pR' +fi +rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file +rmdir conf$$.dir 2>/dev/null + + +# as_fn_mkdir_p +# ------------- +# Create "$as_dir" as a directory, including parents if necessary. +as_fn_mkdir_p () +{ + + case $as_dir in #( + -*) as_dir=./$as_dir;; + esac + test -d "$as_dir" || eval $as_mkdir_p || { + as_dirs= + while :; do + case $as_dir in #( + *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( + *) as_qdir=$as_dir;; + esac + as_dirs="'$as_qdir' $as_dirs" + as_dir=`$as_dirname -- "$as_dir" || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + test -d "$as_dir" && break + done + test -z "$as_dirs" || eval "mkdir $as_dirs" + } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" + + +} # as_fn_mkdir_p +if mkdir -p . 2>/dev/null; then + as_mkdir_p='mkdir -p "$as_dir"' +else + test -d ./-p && rmdir ./-p + as_mkdir_p=false +fi + + +# as_fn_executable_p FILE +# ----------------------- +# Test if FILE is an executable regular file. +as_fn_executable_p () +{ + test -f "$1" && test -x "$1" +} # as_fn_executable_p +as_test_x='test -x' +as_executable_p=as_fn_executable_p + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" + +# Sed expression to map a string onto a valid variable name. +as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" + + +exec 6>&1 +## ----------------------------------- ## +## Main body of $CONFIG_STATUS script. ## +## ----------------------------------- ## +_ASEOF +test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1 + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +# Save the log message, to keep $0 and so on meaningful, and to +# report actual input values of CONFIG_FILES etc. instead of their +# values after options handling. +ac_log=" +This file was extended by igraph $as_me 1.3.0.9016, which was +generated by GNU Autoconf 2.69. Invocation command line was + + CONFIG_FILES = $CONFIG_FILES + CONFIG_HEADERS = $CONFIG_HEADERS + CONFIG_LINKS = $CONFIG_LINKS + CONFIG_COMMANDS = $CONFIG_COMMANDS + $ $0 $@ + +on `(hostname || uname -n) 2>/dev/null | sed 1q` +" + +_ACEOF + +case $ac_config_files in *" +"*) set x $ac_config_files; shift; ac_config_files=$*;; +esac + +case $ac_config_headers in *" +"*) set x $ac_config_headers; shift; ac_config_headers=$*;; +esac + + +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +# Files that config.status was made for. +config_files="$ac_config_files" +config_headers="$ac_config_headers" + +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +ac_cs_usage="\ +\`$as_me' instantiates files and other configuration actions +from templates according to the current configuration. Unless the files +and actions are specified as TAGs, all are instantiated by default. + +Usage: $0 [OPTION]... [TAG]... + + -h, --help print this help, then exit + -V, --version print version number and configuration settings, then exit + --config print configuration, then exit + -q, --quiet, --silent + do not print progress messages + -d, --debug don't remove temporary files + --recheck update $as_me by reconfiguring in the same conditions + --file=FILE[:TEMPLATE] + instantiate the configuration file FILE + --header=FILE[:TEMPLATE] + instantiate the configuration header FILE + +Configuration files: +$config_files + +Configuration headers: +$config_headers + +Report bugs to ." + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" +ac_cs_version="\\ +igraph config.status 1.3.0.9016 +configured by $0, generated by GNU Autoconf 2.69, + with options \\"\$ac_cs_config\\" + +Copyright (C) 2012 Free Software Foundation, Inc. +This config.status script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it." + +ac_pwd='$ac_pwd' +srcdir='$srcdir' +test -n "\$AWK" || AWK=awk +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +# The default lists apply if the user does not specify any file. +ac_need_defaults=: +while test $# != 0 +do + case $1 in + --*=?*) + ac_option=`expr "X$1" : 'X\([^=]*\)='` + ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'` + ac_shift=: + ;; + --*=) + ac_option=`expr "X$1" : 'X\([^=]*\)='` + ac_optarg= + ac_shift=: + ;; + *) + ac_option=$1 + ac_optarg=$2 + ac_shift=shift + ;; + esac + + case $ac_option in + # Handling of the options. + -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) + ac_cs_recheck=: ;; + --version | --versio | --versi | --vers | --ver | --ve | --v | -V ) + $as_echo "$ac_cs_version"; exit ;; + --config | --confi | --conf | --con | --co | --c ) + $as_echo "$ac_cs_config"; exit ;; + --debug | --debu | --deb | --de | --d | -d ) + debug=: ;; + --file | --fil | --fi | --f ) + $ac_shift + case $ac_optarg in + *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; + '') as_fn_error $? "missing file argument" ;; + esac + as_fn_append CONFIG_FILES " '$ac_optarg'" + ac_need_defaults=false;; + --header | --heade | --head | --hea ) + $ac_shift + case $ac_optarg in + *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; + esac + as_fn_append CONFIG_HEADERS " '$ac_optarg'" + ac_need_defaults=false;; + --he | --h) + # Conflict between --help and --header + as_fn_error $? "ambiguous option: \`$1' +Try \`$0 --help' for more information.";; + --help | --hel | -h ) + $as_echo "$ac_cs_usage"; exit ;; + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil | --si | --s) + ac_cs_silent=: ;; + + # This is an error. + -*) as_fn_error $? "unrecognized option: \`$1' +Try \`$0 --help' for more information." ;; + + *) as_fn_append ac_config_targets " $1" + ac_need_defaults=false ;; + + esac + shift +done + +ac_configure_extra_args= + +if $ac_cs_silent; then + exec 6>/dev/null + ac_configure_extra_args="$ac_configure_extra_args --silent" +fi + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +if \$ac_cs_recheck; then + set X $SHELL '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion + shift + \$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6 + CONFIG_SHELL='$SHELL' + export CONFIG_SHELL + exec "\$@" +fi + +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +exec 5>>config.log +{ + echo + sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX +## Running $as_me. ## +_ASBOX + $as_echo "$ac_log" +} >&5 + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 + +# Handling of arguments. +for ac_config_target in $ac_config_targets +do + case $ac_config_target in + "src/config.h") CONFIG_HEADERS="$CONFIG_HEADERS src/config.h" ;; + "src/Makevars.tmp") CONFIG_FILES="$CONFIG_FILES src/Makevars.tmp:src/Makevars.in" ;; + + *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;; + esac +done + + +# If the user did not use the arguments to specify the items to instantiate, +# then the envvar interface is used. Set only those that are not. +# We use the long form for the default assignment because of an extremely +# bizarre bug on SunOS 4.1.3. +if $ac_need_defaults; then + test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files + test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers +fi + +# Have a temporary directory for convenience. Make it in the build tree +# simply because there is no reason against having it here, and in addition, +# creating and moving files from /tmp can sometimes cause problems. +# Hook for its removal unless debugging. +# Note that there is a small window in which the directory will not be cleaned: +# after its creation but before its name has been assigned to `$tmp'. +$debug || +{ + tmp= ac_tmp= + trap 'exit_status=$? + : "${ac_tmp:=$tmp}" + { test ! -d "$ac_tmp" || rm -fr "$ac_tmp"; } && exit $exit_status +' 0 + trap 'as_fn_exit 1' 1 2 13 15 +} +# Create a (secure) tmp directory for tmp files. + +{ + tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` && + test -d "$tmp" +} || +{ + tmp=./conf$$-$RANDOM + (umask 077 && mkdir "$tmp") +} || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5 +ac_tmp=$tmp + +# Set up the scripts for CONFIG_FILES section. +# No need to generate them if there are no CONFIG_FILES. +# This happens for instance with `./config.status config.h'. +if test -n "$CONFIG_FILES"; then + + +ac_cr=`echo X | tr X '\015'` +# On cygwin, bash can eat \r inside `` if the user requested igncr. +# But we know of no other shell where ac_cr would be empty at this +# point, so we can use a bashism as a fallback. +if test "x$ac_cr" = x; then + eval ac_cr=\$\'\\r\' +fi +ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' /dev/null` +if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then + ac_cs_awk_cr='\\r' +else + ac_cs_awk_cr=$ac_cr +fi + +echo 'BEGIN {' >"$ac_tmp/subs1.awk" && +_ACEOF + + +{ + echo "cat >conf$$subs.awk <<_ACEOF" && + echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' && + echo "_ACEOF" +} >conf$$subs.sh || + as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 +ac_delim_num=`echo "$ac_subst_vars" | grep -c '^'` +ac_delim='%!_!# ' +for ac_last_try in false false false false false :; do + . ./conf$$subs.sh || + as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 + + ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X` + if test $ac_delim_n = $ac_delim_num; then + break + elif $ac_last_try; then + as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 + else + ac_delim="$ac_delim!$ac_delim _$ac_delim!! " + fi +done +rm -f conf$$subs.sh + +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +cat >>"\$ac_tmp/subs1.awk" <<\\_ACAWK && +_ACEOF +sed -n ' +h +s/^/S["/; s/!.*/"]=/ +p +g +s/^[^!]*!// +:repl +t repl +s/'"$ac_delim"'$// +t delim +:nl +h +s/\(.\{148\}\)..*/\1/ +t more1 +s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/ +p +n +b repl +:more1 +s/["\\]/\\&/g; s/^/"/; s/$/"\\/ +p +g +s/.\{148\}// +t nl +:delim +h +s/\(.\{148\}\)..*/\1/ +t more2 +s/["\\]/\\&/g; s/^/"/; s/$/"/ +p +b +:more2 +s/["\\]/\\&/g; s/^/"/; s/$/"\\/ +p +g +s/.\{148\}// +t delim +' >$CONFIG_STATUS || ac_write_fail=1 +rm -f conf$$subs.awk +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +_ACAWK +cat >>"\$ac_tmp/subs1.awk" <<_ACAWK && + for (key in S) S_is_set[key] = 1 + FS = "" + +} +{ + line = $ 0 + nfields = split(line, field, "@") + substed = 0 + len = length(field[1]) + for (i = 2; i < nfields; i++) { + key = field[i] + keylen = length(key) + if (S_is_set[key]) { + value = S[key] + line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3) + len += length(value) + length(field[++i]) + substed = 1 + } else + len += 1 + keylen + } + + print line +} + +_ACAWK +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then + sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g" +else + cat +fi < "$ac_tmp/subs1.awk" > "$ac_tmp/subs.awk" \ + || as_fn_error $? "could not setup config files machinery" "$LINENO" 5 +_ACEOF + +# VPATH may cause trouble with some makes, so we remove sole $(srcdir), +# ${srcdir} and @srcdir@ entries from VPATH if srcdir is ".", strip leading and +# trailing colons and then remove the whole line if VPATH becomes empty +# (actually we leave an empty line to preserve line numbers). +if test "x$srcdir" = x.; then + ac_vpsub='/^[ ]*VPATH[ ]*=[ ]*/{ +h +s/// +s/^/:/ +s/[ ]*$/:/ +s/:\$(srcdir):/:/g +s/:\${srcdir}:/:/g +s/:@srcdir@:/:/g +s/^:*// +s/:*$// +x +s/\(=[ ]*\).*/\1/ +G +s/\n// +s/^[^=]*=[ ]*$// +}' +fi + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +fi # test -n "$CONFIG_FILES" + +# Set up the scripts for CONFIG_HEADERS section. +# No need to generate them if there are no CONFIG_HEADERS. +# This happens for instance with `./config.status Makefile'. +if test -n "$CONFIG_HEADERS"; then +cat >"$ac_tmp/defines.awk" <<\_ACAWK || +BEGIN { +_ACEOF + +# Transform confdefs.h into an awk script `defines.awk', embedded as +# here-document in config.status, that substitutes the proper values into +# config.h.in to produce config.h. + +# Create a delimiter string that does not exist in confdefs.h, to ease +# handling of long lines. +ac_delim='%!_!# ' +for ac_last_try in false false :; do + ac_tt=`sed -n "/$ac_delim/p" confdefs.h` + if test -z "$ac_tt"; then + break + elif $ac_last_try; then + as_fn_error $? "could not make $CONFIG_HEADERS" "$LINENO" 5 + else + ac_delim="$ac_delim!$ac_delim _$ac_delim!! " + fi +done + +# For the awk script, D is an array of macro values keyed by name, +# likewise P contains macro parameters if any. Preserve backslash +# newline sequences. + +ac_word_re=[_$as_cr_Letters][_$as_cr_alnum]* +sed -n ' +s/.\{148\}/&'"$ac_delim"'/g +t rset +:rset +s/^[ ]*#[ ]*define[ ][ ]*/ / +t def +d +:def +s/\\$// +t bsnl +s/["\\]/\\&/g +s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ +D["\1"]=" \3"/p +s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2"/p +d +:bsnl +s/["\\]/\\&/g +s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ +D["\1"]=" \3\\\\\\n"\\/p +t cont +s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2\\\\\\n"\\/p +t cont +d +:cont +n +s/.\{148\}/&'"$ac_delim"'/g +t clear +:clear +s/\\$// +t bsnlc +s/["\\]/\\&/g; s/^/"/; s/$/"/p +d +:bsnlc +s/["\\]/\\&/g; s/^/"/; s/$/\\\\\\n"\\/p +b cont +' >$CONFIG_STATUS || ac_write_fail=1 + +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 + for (key in D) D_is_set[key] = 1 + FS = "" +} +/^[\t ]*#[\t ]*(define|undef)[\t ]+$ac_word_re([\t (]|\$)/ { + line = \$ 0 + split(line, arg, " ") + if (arg[1] == "#") { + defundef = arg[2] + mac1 = arg[3] + } else { + defundef = substr(arg[1], 2) + mac1 = arg[2] + } + split(mac1, mac2, "(") #) + macro = mac2[1] + prefix = substr(line, 1, index(line, defundef) - 1) + if (D_is_set[macro]) { + # Preserve the white space surrounding the "#". + print prefix "define", macro P[macro] D[macro] + next + } else { + # Replace #undef with comments. This is necessary, for example, + # in the case of _POSIX_SOURCE, which is predefined and required + # on some systems where configure will not decide to define it. + if (defundef == "undef") { + print "/*", prefix defundef, macro, "*/" + next + } + } +} +{ print } +_ACAWK +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 + as_fn_error $? "could not setup config headers machinery" "$LINENO" 5 +fi # test -n "$CONFIG_HEADERS" + + +eval set X " :F $CONFIG_FILES :H $CONFIG_HEADERS " +shift +for ac_tag +do + case $ac_tag in + :[FHLC]) ac_mode=$ac_tag; continue;; + esac + case $ac_mode$ac_tag in + :[FHL]*:*);; + :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;; + :[FH]-) ac_tag=-:-;; + :[FH]*) ac_tag=$ac_tag:$ac_tag.in;; + esac + ac_save_IFS=$IFS + IFS=: + set x $ac_tag + IFS=$ac_save_IFS + shift + ac_file=$1 + shift + + case $ac_mode in + :L) ac_source=$1;; + :[FH]) + ac_file_inputs= + for ac_f + do + case $ac_f in + -) ac_f="$ac_tmp/stdin";; + *) # Look for the file first in the build tree, then in the source tree + # (if the path is not absolute). The absolute path cannot be DOS-style, + # because $ac_f cannot contain `:'. + test -f "$ac_f" || + case $ac_f in + [\\/$]*) false;; + *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";; + esac || + as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;; + esac + case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac + as_fn_append ac_file_inputs " '$ac_f'" + done + + # Let's still pretend it is `configure' which instantiates (i.e., don't + # use $as_me), people would be surprised to read: + # /* config.h. Generated by config.status. */ + configure_input='Generated from '` + $as_echo "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g' + `' by configure.' + if test x"$ac_file" != x-; then + configure_input="$ac_file. $configure_input" + { $as_echo "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5 +$as_echo "$as_me: creating $ac_file" >&6;} + fi + # Neutralize special characters interpreted by sed in replacement strings. + case $configure_input in #( + *\&* | *\|* | *\\* ) + ac_sed_conf_input=`$as_echo "$configure_input" | + sed 's/[\\\\&|]/\\\\&/g'`;; #( + *) ac_sed_conf_input=$configure_input;; + esac + + case $ac_tag in + *:-:* | *:-) cat >"$ac_tmp/stdin" \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; + esac + ;; + esac + + ac_dir=`$as_dirname -- "$ac_file" || +$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$ac_file" : 'X\(//\)[^/]' \| \ + X"$ac_file" : 'X\(//\)$' \| \ + X"$ac_file" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$ac_file" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + as_dir="$ac_dir"; as_fn_mkdir_p + ac_builddir=. + +case "$ac_dir" in +.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; +*) + ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` + # A ".." for each directory in $ac_dir_suffix. + ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` + case $ac_top_builddir_sub in + "") ac_top_builddir_sub=. ac_top_build_prefix= ;; + *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; + esac ;; +esac +ac_abs_top_builddir=$ac_pwd +ac_abs_builddir=$ac_pwd$ac_dir_suffix +# for backward compatibility: +ac_top_builddir=$ac_top_build_prefix + +case $srcdir in + .) # We are building in place. + ac_srcdir=. + ac_top_srcdir=$ac_top_builddir_sub + ac_abs_top_srcdir=$ac_pwd ;; + [\\/]* | ?:[\\/]* ) # Absolute name. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir + ac_abs_top_srcdir=$srcdir ;; + *) # Relative name. + ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_build_prefix$srcdir + ac_abs_top_srcdir=$ac_pwd/$srcdir ;; +esac +ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix + + + case $ac_mode in + :F) + # + # CONFIG_FILE + # + +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +# If the template does not know about datarootdir, expand it. +# FIXME: This hack should be removed a few years after 2.60. +ac_datarootdir_hack=; ac_datarootdir_seen= +ac_sed_dataroot=' +/datarootdir/ { + p + q +} +/@datadir@/p +/@docdir@/p +/@infodir@/p +/@localedir@/p +/@mandir@/p' +case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in +*datarootdir*) ac_datarootdir_seen=yes;; +*@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*) + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5 +$as_echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;} +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 + ac_datarootdir_hack=' + s&@datadir@&$datadir&g + s&@docdir@&$docdir&g + s&@infodir@&$infodir&g + s&@localedir@&$localedir&g + s&@mandir@&$mandir&g + s&\\\${datarootdir}&$datarootdir&g' ;; +esac +_ACEOF + +# Neutralize VPATH when `$srcdir' = `.'. +# Shell code in configure.ac might set extrasub. +# FIXME: do we really want to maintain this feature? +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +ac_sed_extra="$ac_vpsub +$extrasub +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +:t +/@[a-zA-Z_][a-zA-Z_0-9]*@/!b +s|@configure_input@|$ac_sed_conf_input|;t t +s&@top_builddir@&$ac_top_builddir_sub&;t t +s&@top_build_prefix@&$ac_top_build_prefix&;t t +s&@srcdir@&$ac_srcdir&;t t +s&@abs_srcdir@&$ac_abs_srcdir&;t t +s&@top_srcdir@&$ac_top_srcdir&;t t +s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t +s&@builddir@&$ac_builddir&;t t +s&@abs_builddir@&$ac_abs_builddir&;t t +s&@abs_top_builddir@&$ac_abs_top_builddir&;t t +$ac_datarootdir_hack +" +eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$ac_tmp/subs.awk" \ + >$ac_tmp/out || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + +test -z "$ac_datarootdir_hack$ac_datarootdir_seen" && + { ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } && + { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' \ + "$ac_tmp/out"`; test -z "$ac_out"; } && + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir' +which seems to be undefined. Please make sure it is defined" >&5 +$as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir' +which seems to be undefined. Please make sure it is defined" >&2;} + + rm -f "$ac_tmp/stdin" + case $ac_file in + -) cat "$ac_tmp/out" && rm -f "$ac_tmp/out";; + *) rm -f "$ac_file" && mv "$ac_tmp/out" "$ac_file";; + esac \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + ;; + :H) + # + # CONFIG_HEADER + # + if test x"$ac_file" != x-; then + { + $as_echo "/* $configure_input */" \ + && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" + } >"$ac_tmp/config.h" \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + if diff "$ac_file" "$ac_tmp/config.h" >/dev/null 2>&1; then + { $as_echo "$as_me:${as_lineno-$LINENO}: $ac_file is unchanged" >&5 +$as_echo "$as_me: $ac_file is unchanged" >&6;} + else + rm -f "$ac_file" + mv "$ac_tmp/config.h" "$ac_file" \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + fi + else + $as_echo "/* $configure_input */" \ + && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" \ + || as_fn_error $? "could not create -" "$LINENO" 5 + fi + ;; + + + esac + + + case $ac_file$ac_mode in + "src/Makevars.tmp":F) + if test -f src/Makevars && cmp -s src/Makevars.tmp src/Makevars; then + { $as_echo "$as_me:${as_lineno-$LINENO}: creating src/Makevars" >&5 +$as_echo "$as_me: creating src/Makevars" >&6;} + { $as_echo "$as_me:${as_lineno-$LINENO}: src/Makevars is unchanged" >&5 +$as_echo "$as_me: src/Makevars is unchanged" >&6;} + rm src/Makevars.tmp + else + { $as_echo "$as_me:${as_lineno-$LINENO}: creating src/Makevars" >&5 +$as_echo "$as_me: creating src/Makevars" >&6;} + mv src/Makevars.tmp src/Makevars + fi + + ;; + + esac +done # for ac_tag + + +as_fn_exit 0 +_ACEOF +ac_clean_files=$ac_clean_files_save + +test $ac_write_fail = 0 || + as_fn_error $? "write failure creating $CONFIG_STATUS" "$LINENO" 5 + + +# configure is writing to config.log, and then calls config.status. +# config.status does its own redirection, appending to config.log. +# Unfortunately, on DOS this fails, as config.log is still kept open +# by configure, so config.status won't be able to write to it; its +# output is simply discarded. So we exec the FD to /dev/null, +# effectively closing config.log, so it can be properly (re)opened and +# appended to by config.status. When coming back to configure, we +# need to make the FD available again. +if test "$no_create" != yes; then + ac_cs_success=: + ac_config_status_args= + test "$silent" = yes && + ac_config_status_args="$ac_config_status_args --quiet" + exec 5>/dev/null + $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false + exec 5>>config.log + # Use ||, not &&, to avoid exiting from the if with $? = 1, which + # would make configure fail if this is the last instruction. + $ac_cs_success || as_fn_exit 1 +fi +if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5 +$as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;} +fi + + +echo "" +echo "*** Compiler settings used:" +echo " CC=${CC}" +echo " LD=${LD}" +echo " CFLAGS=${CFLAGS}" +echo " CPPFLAGS=${CPPFLAGS}" +echo " CXX=${CXX}" +echo " CXXFLAGS=${CXXFLAGS}" +echo " LDFLAGS=${LDFLAGS}" +echo " LIBS=${LIBS}" + diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..e683638 --- /dev/null +++ b/configure.ac @@ -0,0 +1,181 @@ +AC_INIT(igraph, 1.3.0.9016, ntamas@gmail.com) +AC_CONFIG_SRCDIR(src/rigraph/rinterface.c) +AC_CONFIG_HEADERS(src/rigraph/config.h) + +: ${R_HOME=`R RHOME`} +if test -z "${R_HOME}"; then + echo "could not determine R_HOME" + exit 1 +fi +CC=`"${R_HOME}/bin/R" CMD config CC` +CXX=`"${R_HOME}/bin/R" CMD config CXX11` +if test -z "$CXX"; then + AC_MSG_ERROR([No C++11 compiler is available]) +fi +CXX11STD=`"${R_HOME}/bin/R" CMD config CXX11STD` +CXX="${CXX} ${CXX11STD}" +FC=`"${R_HOME}/bin/R" CMD config FC` +CFLAGS=`"${R_HOME}/bin/R" CMD config CFLAGS` +CXXFLAGS=`"${R_HOME}/bin/R" CMD config CXX11FLAGS` +CPPFLAGS=`"${R_HOME}/bin/R" CMD config CPPFLAGS` +FCFLAGS=`"${R_HOME}/bin/R" CMD config FCFLAGS` +FLIBS=`"${R_HOME}/bin/R" CMD config FLIBS` +LDFLAGS=`"${R_HOME}/bin/R" CMD config LDFLAGS` + +AC_LANG(C) +AC_PROG_CC + +AC_LANG(C++) +AC_PROG_CXX + +# Fortran compiler, we need to check if it is the GNU compiler +AC_PROG_FC +if test "x$ac_cv_fc_compiler_gnu" = xyes; then + AC_DEFINE([HAVE_GFORTRAN], [1], [Define to 1 if using the GNU Fortran compiler]) +fi + +LIBS_SAVE=$LIBS +LIBS="$LIBS -lm" +AC_CHECK_FUNCS([expm1 fmin finite log2 log1p rint rintf round stpcpy strcasecmp _stricmp strdup]) +AC_CHECK_FUNCS(isfinite, + [AC_DEFINE(HAVE_ISFINITE, 1)], + [AC_LINK_IFELSE( + [AC_LANG_PROGRAM( + [[#include ]], + [[double f = 0.0; isfinite(f)]])], + [AC_DEFINE(HAVE_ISFINITE, 1)], + [AC_MSG_FAILURE([isfinite() not available])] + )] +) +LIBS=$LIBS_SAVE + +AC_CHECK_HEADER([sys/times.h], + [AC_DEFINE([HAVE_TIMES_H], [1], [Define to 1 if you have the sys/times.h header])]) + +AC_CHECK_HEADERS([ \ + net/if.h \ + netinet/in.h \ + net/if_dl.h \ + sys/sockio.h \ + sys/un.h \ + sys/socket.h \ + sys/ioctl.h \ + sys/time.h \ + sys/file.h \ + ]) + +AC_CHECK_MEMBER([struct sockaddr.sa_len], + AC_DEFINE_UNQUOTED([HAVE_SA_LEN], [1], [Define if struct sockaddr contains sa_len]), [], + [#include + #include ]) + +graphml_support=yes +AC_ARG_ENABLE(graphml, + AS_HELP_STRING([--disable-graphml], [Disable support for GraphML format]), + [graphml_support=$enableval], [graphml_support=yes]) + +HAVE_LIBXML=0 +if test $graphml_support = yes; then + AC_PATH_PROG([XML2CONFIG], [xml2-config], [none]) + if test "$XML2CONFIG" = "none"; then + graphml_support=no + else + # xml2-config puts only preprocessor flags in --cflags (i.e. -D and -I) so + # we can put them in XML2_CPPFLAGS. This might not be true for other + # libraries, though. + XML2_CPPFLAGS=`$XML2CONFIG --cflags` + XML2_LIBS=`$XML2CONFIG --libs` + OLDLIBS=${LIBS} + LIBS=${XML2_LIBS} + AC_CHECK_LIB([xml2], [xmlSAXUserParseFile], [ + OLDCPPFLAGS=${CPPFLAGS} + CPPFLAGS=${XML2_CPPFLAGS} + AC_CHECK_HEADER([libxml/parser.h], [ + HAVE_LIBXML=1 + AC_DEFINE([HAVE_LIBXML], [1], [Define to 1 if you have the libxml2 libraries installed]) + ], [ + graphml_support=no + ]) + CPPFLAGS=${OLDCPPFLAGS} + ], [ + graphml_support=no + ]) + LIBS=${OLDLIBS} + fi +fi +AC_SUBST(HAVE_LIBXML) +AC_SUBST(XML2_LIBS) +AC_SUBST(XML2_CPPFLAGS) + +AC_DEFINE([INTERNAL_GMP], [1], [Define to 1 if you use the vendored mini-GMP library]) + +AC_LANG_PUSH([C++]) +HAVE_GMP=0 +INTERNAL_GMP=1 +GMP_LIBS="" +gmp_support=no +AC_CHECK_LIB([gmp], [__gmpz_add], [ + AC_CHECK_HEADER([gmp.h], [ + HAVE_GMP=1 + INTERNAL_GMP=0 + AC_DEFINE([HAVE_GMP], [1], [Define to 1 if you have the GMP library]) + gmp_support=yes + GMP_LIBS="-lgmp" + ]) +]) +AC_SUBST(HAVE_GMP) +AC_SUBST(INTERNAL_GMP) +AC_SUBST(GMP_LIBS) +AC_LANG_POP([C++]) + +HAVE_GLPK=0 +GLPK_LIBS="" +glpk_support=no +AC_ARG_ENABLE(glpk, AS_HELP_STRING([--disable-glpk], [Compile without the GLPK library])) +if test "x$enable_gmp" != "xno"; then + AC_CHECK_LIB([glpk], [glp_read_mps], [ + AC_CHECK_HEADER([glpk.h], [ + AC_EGREP_CPP(yes, [ + #include + #if GLP_MAJOR_VERSION > 4 || (GLP_MAJOR_VERSION == 4 && GLP_MINOR_VERSION >= 57) + yes + #endif + ], [ + HAVE_GLPK=1 + AC_DEFINE([HAVE_GLPK], [1], [Define to 1 if you have the GLPK library]) + glpk_support=yes + GLPK_LIBS="-lglpk" + ]) + ]) + ]) +fi +AC_SUBST(HAVE_GLPK) +AC_SUBST(GLPK_LIBS) + +AC_DEFINE(IGRAPH_THREAD_LOCAL, [], [We don't care about thread-local storage in R]) + +AC_CONFIG_FILES([src/rigraph/Makevars.tmp:src/Makevars.in], [ + if test -f src/rigraph/Makevars && cmp -s src/rigraph/Makevars.tmp src/rigraph/Makevars; then + AC_MSG_NOTICE([creating src/rigraph/Makevars]) + AC_MSG_NOTICE([src/rigraph/Makevars is unchanged]) + rm src/rigraph/Makevars.tmp + else + AC_MSG_NOTICE([creating src/rigraph/Makevars]) + mv src/rigraph/Makevars.tmp src/rigraph/Makevars + fi + ] +) + +AC_OUTPUT + +echo "" +echo "*** Compiler settings used:" +echo " CC=${CC}" +echo " LD=${LD}" +echo " CFLAGS=${CFLAGS}" +echo " CPPFLAGS=${CPPFLAGS}" +echo " CXX=${CXX}" +echo " CXXFLAGS=${CXXFLAGS}" +echo " LDFLAGS=${LDFLAGS}" +echo " LIBS=${LIBS}" + diff --git a/configure.win b/configure.win new file mode 100644 index 0000000..e69de29 diff --git a/src/Makevars b/src/Makevars index 9176832..9a332ae 100644 --- a/src/Makevars +++ b/src/Makevars @@ -1,7 +1,7 @@ ## include OpenMP -PKG_CXXFLAGS= -I"../inst/include" -I"./leidenalg/igraph-R" -I"./leidenalg/include" $(SHLIB_OPENMP_CXXFLAGS) +PKG_CXXFLAGS= -I"../inst/include" -I"./rigraph/include" -I"./leidenalg/include" $(SHLIB_OPENMP_CXXFLAGS) PKGB_PATH=`echo 'library(igraph); cat(system.file("libs", package="igraph"))' \ | ${R_HOME}/bin/R --vanilla --slave` diff --git a/src/leidenalg/CPMVertexPartition.cpp b/src/leidenalg/CPMVertexPartition.cpp index a743d5d..3d8c961 100644 --- a/src/leidenalg/CPMVertexPartition.cpp +++ b/src/leidenalg/CPMVertexPartition.cpp @@ -1,5 +1,5 @@ #include "CPMVertexPartition.h" -#include +#include "igraph.h" CPMVertexPartition::CPMVertexPartition(Graph* graph, vector membership, double resolution_parameter) : diff --git a/src/leidenalg/GraphHelper.cpp b/src/leidenalg/GraphHelper.cpp index c48b428..85bc664 100644 --- a/src/leidenalg/GraphHelper.cpp +++ b/src/leidenalg/GraphHelper.cpp @@ -1,5 +1,7 @@ #include "GraphHelper.h" -#include + +#include "igraph.h" + #ifdef DEBUG using std::cerr; diff --git a/src/leidenalg/LinearResolutionParameterVertexPartition.cpp b/src/leidenalg/LinearResolutionParameterVertexPartition.cpp index 545326b..3737eca 100644 --- a/src/leidenalg/LinearResolutionParameterVertexPartition.cpp +++ b/src/leidenalg/LinearResolutionParameterVertexPartition.cpp @@ -1,5 +1,6 @@ #include "LinearResolutionParameterVertexPartition.h" -#include +#include "igraph.h" + LinearResolutionParameterVertexPartition::LinearResolutionParameterVertexPartition(Graph* graph, vector membership, double resolution_parameter) : diff --git a/src/leidenalg/Makefile b/src/leidenalg/Makefile index 0b7e5d9..c023e2f 100644 --- a/src/leidenalg/Makefile +++ b/src/leidenalg/Makefile @@ -1,6 +1,6 @@ CXX ?= g++ PKG_CXXFLAGS = -O3 -std=c++11 -pthread -fPIC $(SHLIB_OPENMP_CXXFLAGS) -PKG_CXXFLAGS += -I"./include" -I"./igraph-R" +PKG_CXXFLAGS += -I"./include" -I"../rigraph/include" LDFLAGS += -lpthread diff --git a/src/leidenalg/Makefile.win b/src/leidenalg/Makefile.win index fbb4ab0..66eb712 100644 --- a/src/leidenalg/Makefile.win +++ b/src/leidenalg/Makefile.win @@ -1,6 +1,6 @@ CXX ?= g++ PKG_CXXFLAGS = -O3 -std=c++11 -pthread -fPIC $(SHLIB_OPENMP_CXXFLAGS) -PKG_CXXFLAGS += -I"./include" -I"./igraph-R" +PKG_CXXFLAGS += -I"./include" -I"../rigraph/include" LDFLAGS += -lpthread diff --git a/src/leidenalg/ModularityVertexPartition.cpp b/src/leidenalg/ModularityVertexPartition.cpp index 7f817f5..a074b04 100644 --- a/src/leidenalg/ModularityVertexPartition.cpp +++ b/src/leidenalg/ModularityVertexPartition.cpp @@ -1,5 +1,6 @@ #include "ModularityVertexPartition.h" -#include + +#include "igraph.h" #ifdef DEBUG #include diff --git a/src/leidenalg/MutableVertexPartition.cpp b/src/leidenalg/MutableVertexPartition.cpp index c7fe617..013caba 100644 --- a/src/leidenalg/MutableVertexPartition.cpp +++ b/src/leidenalg/MutableVertexPartition.cpp @@ -1,5 +1,6 @@ #include "MutableVertexPartition.h" -#include + +#include "igraph.h" #ifdef DEBUG using std::cerr; diff --git a/src/leidenalg/Optimiser.cpp b/src/leidenalg/Optimiser.cpp index 17cf308..3a672ac 100644 --- a/src/leidenalg/Optimiser.cpp +++ b/src/leidenalg/Optimiser.cpp @@ -1,6 +1,6 @@ #include "Optimiser.h" -#include "igraph-R/igraph.h" -#include +#include "igraph.h" + /**************************************************************************** Create a new Optimiser object diff --git a/src/leidenalg/RBConfigurationVertexPartition.cpp b/src/leidenalg/RBConfigurationVertexPartition.cpp index 49f8f18..b5c66ce 100644 --- a/src/leidenalg/RBConfigurationVertexPartition.cpp +++ b/src/leidenalg/RBConfigurationVertexPartition.cpp @@ -1,5 +1,6 @@ #include "RBConfigurationVertexPartition.h" -#include +#include "igraph.h" + RBConfigurationVertexPartition::RBConfigurationVertexPartition(Graph* graph, vector const& membership, double resolution_parameter) : diff --git a/src/leidenalg/RBERVertexPartition.cpp b/src/leidenalg/RBERVertexPartition.cpp index 6eb2cb5..6642337 100644 --- a/src/leidenalg/RBERVertexPartition.cpp +++ b/src/leidenalg/RBERVertexPartition.cpp @@ -1,5 +1,6 @@ #include "RBERVertexPartition.h" -#include + +#include "igraph.h" RBERVertexPartition::RBERVertexPartition(Graph* graph, vector const& membership, double resolution_parameter) : diff --git a/src/leidenalg/ResolutionParameterVertexPartition.cpp b/src/leidenalg/ResolutionParameterVertexPartition.cpp index 1abf0e2..9e991bd 100644 --- a/src/leidenalg/ResolutionParameterVertexPartition.cpp +++ b/src/leidenalg/ResolutionParameterVertexPartition.cpp @@ -1,5 +1,6 @@ #include "ResolutionParameterVertexPartition.h" -#include +#include "igraph.h" + ResolutionParameterVertexPartition::ResolutionParameterVertexPartition(Graph* graph, vector membership, double resolution_parameter) : diff --git a/src/leidenalg/SignificanceVertexPartition.cpp b/src/leidenalg/SignificanceVertexPartition.cpp index 3287047..1a1de37 100644 --- a/src/leidenalg/SignificanceVertexPartition.cpp +++ b/src/leidenalg/SignificanceVertexPartition.cpp @@ -1,5 +1,6 @@ #include "SignificanceVertexPartition.h" -#include + +#include "igraph.h" #ifdef DEBUG #include diff --git a/src/leidenalg/SurpriseVertexPartition.cpp b/src/leidenalg/SurpriseVertexPartition.cpp index 7fbea14..6046f73 100644 --- a/src/leidenalg/SurpriseVertexPartition.cpp +++ b/src/leidenalg/SurpriseVertexPartition.cpp @@ -1,5 +1,6 @@ #include "SurpriseVertexPartition.h" -#include + +#include "igraph.h" SurpriseVertexPartition::SurpriseVertexPartition(Graph* graph, vector const& membership) : diff --git a/src/leidenalg/igraph-R/igraph_adjlist.h b/src/leidenalg/igraph-R/igraph_adjlist.h deleted file mode 100644 index 00ae209..0000000 --- a/src/leidenalg/igraph-R/igraph_adjlist.h +++ /dev/null @@ -1,232 +0,0 @@ -/* -*- mode: C -*- */ -/* - IGraph library. - Copyright (C) 2009-2012 Gabor Csardi - 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 - -#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_constants.h" -#include "igraph_types.h" -#include "igraph_datatype.h" - -__BEGIN_DECLS - -typedef struct igraph_adjlist_t { - igraph_integer_t length; - igraph_vector_t *adjs; -} igraph_adjlist_t; - -int igraph_adjlist_init(const igraph_t *graph, igraph_adjlist_t *al, - igraph_neimode_t mode); -int igraph_adjlist_init_empty(igraph_adjlist_t *al, igraph_integer_t no_of_nodes); -igraph_integer_t igraph_adjlist_size(const igraph_adjlist_t *al); -int igraph_adjlist_init_complementer(const igraph_t *graph, - igraph_adjlist_t *al, - igraph_neimode_t mode, - igraph_bool_t loops); -void igraph_adjlist_destroy(igraph_adjlist_t *al); -void igraph_adjlist_sort(igraph_adjlist_t *al); -int igraph_adjlist_simplify(igraph_adjlist_t *al); -int igraph_adjlist_remove_duplicate(const igraph_t *graph, - igraph_adjlist_t *al); -int igraph_adjlist_print(const igraph_adjlist_t *al, FILE *outfile); -/* igraph_vector_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 igraph_vector_t 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 igraph_vector_t object. - * - * Time complexity: O(1). - */ -#define igraph_adjlist_get(al,no) (&(al)->adjs[(long int)(no)]) - -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_t *incs; -} igraph_inclist_t; - -int igraph_inclist_init(const igraph_t *graph, - igraph_inclist_t *il, - igraph_neimode_t mode); -int igraph_inclist_init_empty(igraph_inclist_t *il, igraph_integer_t n); -void igraph_inclist_destroy(igraph_inclist_t *il); -int igraph_inclist_remove_duplicate(const igraph_t *graph, - igraph_inclist_t *il); -int igraph_inclist_print(const igraph_inclist_t *il, FILE *outfile); - -/** - * \define igraph_inclist_get - * Query a vector in an incidence list - * - * Returns a pointer to an igraph_vector_t object from an - * incidence list containing edge ids. The vector can be modified, - * resized, etc. as desired. - * \param graph il The incidence list. - * \param no The vertex for which the incident edges are returned. - * \return Pointer to an igraph_vector_t 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; - -int igraph_lazy_adjlist_init(const igraph_t *graph, - igraph_lazy_adjlist_t *al, - igraph_neimode_t mode, - igraph_lazy_adlist_simplify_t simplify); -void igraph_lazy_adjlist_destroy(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))) -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; - -int igraph_lazy_inclist_init(const igraph_t *graph, - igraph_lazy_inclist_t *il, - igraph_neimode_t mode); -void igraph_lazy_inclist_destroy(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))) -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; - -int igraph_adjedgelist_init(const igraph_t *graph, - igraph_inclist_t *il, - igraph_neimode_t mode); -void igraph_adjedgelist_destroy(igraph_inclist_t *il); -int igraph_adjedgelist_remove_duplicate(const igraph_t *graph, - igraph_inclist_t *il); -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. - * - * - * 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; - -int igraph_lazy_adjedgelist_init(const igraph_t *graph, - igraph_lazy_inclist_t *il, - igraph_neimode_t mode); -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. - * - * - * 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))) -igraph_vector_t *igraph_lazy_adjedgelist_get_real(igraph_lazy_inclist_t *al, - igraph_integer_t no); -__END_DECLS - -#endif diff --git a/src/leidenalg/igraph-R/igraph_array_pmt.h b/src/leidenalg/igraph-R/igraph_array_pmt.h deleted file mode 100644 index c262176..0000000 --- a/src/leidenalg/igraph-R/igraph_array_pmt.h +++ /dev/null @@ -1,51 +0,0 @@ -/* -*- mode: C -*- */ -/* - IGraph library. - Copyright (C) 2007-2012 Gabor Csardi - 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/src/leidenalg/igraph-R/igraph_attributes.h b/src/leidenalg/igraph-R/igraph_attributes.h deleted file mode 100644 index 9e6604d..0000000 --- a/src/leidenalg/igraph-R/igraph_attributes.h +++ /dev/null @@ -1,717 +0,0 @@ -/* -*- mode: C -*- */ -/* - IGraph library. - Copyright (C) 2005-2012 Gabor Csardi - 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 - -#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_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 - * - * 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. - * - * 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. - * - * 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. - */ - -/** - * \section about_attribute_table - * 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. - * - * 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. - * - */ - -/** - * \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_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_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 struct igraph_attribute_combination_record_t { - const char *name; /* can be NULL, meaning: the rest */ - igraph_attribute_combination_type_t type; - void *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) - -int igraph_attribute_combination_init(igraph_attribute_combination_t *comb); -int igraph_attribute_combination(igraph_attribute_combination_t *comb, ...); -void igraph_attribute_combination_destroy(igraph_attribute_combination_t *comb); -int igraph_attribute_combination_add(igraph_attribute_combination_t *comb, - const char *name, - igraph_attribute_combination_type_t type, - void *func); -int igraph_attribute_combination_remove(igraph_attribute_combination_t *comb, - const char *name); -int igraph_attribute_combination_query(const igraph_attribute_combination_t *comb, - const char *name, - igraph_attribute_combination_type_t *type, - void **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_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_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 - * edge included in \p es. - * - * Note that the get_*_*_attr are allowed to - * convert the attributes to numeric or string. E.g. if a vertex attribute - * is a GNU R complex data type, then - * get_string_vertex_attribute may serialize it - * into a string, but this probably makes sense only if - * add_vertices 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_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_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); -} igraph_attribute_table_t; - -igraph_attribute_table_t * -igraph_i_set_attribute_table(const igraph_attribute_table_t * table); - -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_ret=0; \ - if ((from)->attr) { \ - IGRAPH_CHECK(igraph_i_ret=igraph_i_attribute_copy((to),(from),(ga),(va),(ea))); \ - } else { \ - (to)->attr = 0; \ - } \ - if (igraph_i_ret != 0) { \ - IGRAPH_ERROR("", igraph_i_ret); \ - } \ - } 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); - -/* Experimental attribute handler in C */ - -extern const igraph_attribute_table_t igraph_cattribute_table; - -igraph_real_t igraph_cattribute_GAN(const igraph_t *graph, const char *name); -const char* igraph_cattribute_GAS(const igraph_t *graph, const char *name); -igraph_real_t igraph_cattribute_VAN(const igraph_t *graph, const char *name, - igraph_integer_t vid); -const char* igraph_cattribute_VAS(const igraph_t *graph, const char *name, - igraph_integer_t vid); -igraph_real_t igraph_cattribute_EAN(const igraph_t *graph, const char *name, - igraph_integer_t eid); -const char* igraph_cattribute_EAS(const igraph_t *graph, const char *name, - igraph_integer_t eid); - -int igraph_cattribute_VANV(const igraph_t *graph, const char *name, - igraph_vs_t vids, igraph_vector_t *result); -int igraph_cattribute_EANV(const igraph_t *graph, const char *name, - igraph_es_t eids, igraph_vector_t *result); -int igraph_cattribute_VASV(const igraph_t *graph, const char *name, - igraph_vs_t vids, igraph_strvector_t *result); -int igraph_cattribute_EASV(const igraph_t *graph, const char *name, - igraph_es_t eids, igraph_strvector_t *result); - -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); -igraph_bool_t igraph_cattribute_has_attr(const igraph_t *graph, - igraph_attribute_elemtype_t type, - const char *name); - -int igraph_cattribute_GAN_set(igraph_t *graph, const char *name, - igraph_real_t value); -int igraph_cattribute_GAS_set(igraph_t *graph, const char *name, - const char *value); -int igraph_cattribute_VAN_set(igraph_t *graph, const char *name, - igraph_integer_t vid, igraph_real_t value); -int igraph_cattribute_VAS_set(igraph_t *graph, const char *name, - igraph_integer_t vid, const char *value); -int igraph_cattribute_EAN_set(igraph_t *graph, const char *name, - igraph_integer_t eid, igraph_real_t value); -int igraph_cattribute_EAS_set(igraph_t *graph, const char *name, - igraph_integer_t eid, const char *value); - -int igraph_cattribute_VAN_setv(igraph_t *graph, const char *name, - const igraph_vector_t *v); -int igraph_cattribute_VAS_setv(igraph_t *graph, const char *name, - const igraph_strvector_t *sv); -int igraph_cattribute_EAN_setv(igraph_t *graph, const char *name, - const igraph_vector_t *v); -int igraph_cattribute_EAS_setv(igraph_t *graph, const char *name, - const igraph_strvector_t *sv); - -void igraph_cattribute_remove_g(igraph_t *graph, const char *name); -void igraph_cattribute_remove_v(igraph_t *graph, const char *name); -void igraph_cattribute_remove_e(igraph_t *graph, const char *name); -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 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 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 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 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 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 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 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 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 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 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/src/leidenalg/igraph-R/igraph_bipartite.h b/src/leidenalg/igraph-R/igraph_bipartite.h deleted file mode 100644 index 313fca8..0000000 --- a/src/leidenalg/igraph-R/igraph_bipartite.h +++ /dev/null @@ -1,91 +0,0 @@ -/* -*- mode: C -*- */ -/* - IGraph library. - Copyright (C) 2009-2012 Gabor Csardi - 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 - -#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_constants.h" -#include "igraph_types.h" -#include "igraph_vector.h" -#include "igraph_matrix.h" -#include "igraph_datatype.h" - -__BEGIN_DECLS - -/* -------------------------------------------------- */ -/* Bipartite networks */ -/* -------------------------------------------------- */ - -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); - -int igraph_create_bipartite(igraph_t *g, const igraph_vector_bool_t *types, - const igraph_vector_t *edges, - igraph_bool_t directed); - -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); - -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); - -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); - -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); - -int igraph_is_bipartite(const igraph_t *graph, - igraph_bool_t *res, - igraph_vector_bool_t *type); - - -__END_DECLS - -#endif diff --git a/src/leidenalg/igraph-R/igraph_centrality.h b/src/leidenalg/igraph-R/igraph_centrality.h deleted file mode 100644 index 0d6c598..0000000 --- a/src/leidenalg/igraph-R/igraph_centrality.h +++ /dev/null @@ -1,183 +0,0 @@ -/* -*- mode: C -*- */ -/* - IGraph library. - Copyright (C) 2009-2012 Gabor Csardi - 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 - -#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_constants.h" -#include "igraph_types.h" -#include "igraph_datatype.h" -#include "igraph_iterators.h" -#include "igraph_arpack.h" - -__BEGIN_DECLS - -/* -------------------------------------------------- */ -/* Centrality */ -/* -------------------------------------------------- */ - -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); -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); - -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); -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); -int igraph_edge_betweenness(const igraph_t *graph, igraph_vector_t *result, - igraph_bool_t directed, - const igraph_vector_t *weigths); -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); -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); -int igraph_pagerank(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, - const igraph_vector_t *weights, - igraph_arpack_options_t *options); -int igraph_personalized_pagerank(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); -int igraph_personalized_pagerank_vs(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_vs_t reset_vids, - const igraph_vector_t *weights, - igraph_arpack_options_t *options); - -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); - -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); -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); - -int igraph_constraint(const igraph_t *graph, igraph_vector_t *res, - igraph_vs_t vids, const igraph_vector_t *weights); - -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); - -int igraph_convergence_degree(const igraph_t *graph, igraph_vector_t *result, - igraph_vector_t *ins, igraph_vector_t *outs); - -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); - -igraph_real_t igraph_centralization(const igraph_vector_t *scores, - igraph_real_t theoretical_max, - igraph_bool_t normalized); - -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); -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); - -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); -int igraph_centralization_betweenness_tmax(const igraph_t *graph, - igraph_integer_t nodes, - igraph_bool_t directed, - igraph_real_t *res); - -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); -int igraph_centralization_closeness_tmax(const igraph_t *graph, - igraph_integer_t nodes, - igraph_neimode_t mode, - igraph_real_t *res); - -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); -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/src/leidenalg/igraph-R/igraph_cliques.h b/src/leidenalg/igraph-R/igraph_cliques.h deleted file mode 100644 index 9bffb8e..0000000 --- a/src/leidenalg/igraph-R/igraph_cliques.h +++ /dev/null @@ -1,66 +0,0 @@ -/* -*- mode: C -*- */ -/* - IGraph library. - Copyright (C) 2009-2012 Gabor Csardi - 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 - -#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_vector_ptr.h" - -__BEGIN_DECLS - -/* -------------------------------------------------- */ -/* Cliques, maximal independent vertex sets */ -/* -------------------------------------------------- */ - -int igraph_cliques(const igraph_t *graph, igraph_vector_ptr_t *res, - igraph_integer_t min_size, igraph_integer_t max_size); -int igraph_largest_cliques(const igraph_t *graph, - igraph_vector_ptr_t *cliques); -int igraph_maximal_cliques(const igraph_t *graph, igraph_vector_ptr_t *res, - igraph_integer_t min_size, igraph_integer_t max_size); -int igraph_clique_number(const igraph_t *graph, igraph_integer_t *no); -int igraph_independent_vertex_sets(const igraph_t *graph, - igraph_vector_ptr_t *res, - igraph_integer_t min_size, - igraph_integer_t max_size); -int igraph_largest_independent_vertex_sets(const igraph_t *graph, - igraph_vector_ptr_t *res); -int igraph_maximal_independent_vertex_sets(const igraph_t *graph, - igraph_vector_ptr_t *res); -int igraph_independence_number(const igraph_t *graph, igraph_integer_t *no); - -__END_DECLS - -#endif diff --git a/src/leidenalg/igraph-R/igraph_cocitation.h b/src/leidenalg/igraph-R/igraph_cocitation.h deleted file mode 100644 index eff0867..0000000 --- a/src/leidenalg/igraph-R/igraph_cocitation.h +++ /dev/null @@ -1,75 +0,0 @@ -/* -*- mode: C -*- */ -/* - IGraph library. - Copyright (C) 2009-2012 Gabor Csardi - 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 - -#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_datatype.h" -#include "igraph_iterators.h" - -__BEGIN_DECLS - -/* -------------------------------------------------- */ -/* Cocitation and other similarity measures */ -/* -------------------------------------------------- */ - -int igraph_cocitation(const igraph_t *graph, igraph_matrix_t *res, - const igraph_vs_t vids); -int igraph_bibcoupling(const igraph_t *graph, igraph_matrix_t *res, - const igraph_vs_t vids); - -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); -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); -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); - -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); -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); -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); - -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/src/leidenalg/igraph-R/igraph_community.h b/src/leidenalg/igraph-R/igraph_community.h deleted file mode 100644 index f1c952a..0000000 --- a/src/leidenalg/igraph-R/igraph_community.h +++ /dev/null @@ -1,235 +0,0 @@ -/* -*- mode: C -*- */ -/* - IGraph library. - Copyright (C) 2009-2012 Gabor Csardi - 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 - -#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_constants.h" -#include "igraph_datatype.h" -#include "igraph_types.h" -#include "igraph_arpack.h" -#include "igraph_vector_ptr.h" - -__BEGIN_DECLS - -/* -------------------------------------------------- */ -/* K-Cores */ -/* -------------------------------------------------- */ - -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: */ - -int igraph_community_optimal_modularity(const igraph_t *graph, - igraph_real_t *modularity, - igraph_vector_t *membership, - igraph_bool_t verbose); - -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); - -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); - -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); - -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); - -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); -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); - -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); - -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); -int igraph_le_community_to_membership(const igraph_matrix_t *merges, - igraph_integer_t steps, - igraph_vector_t *membership, - igraph_vector_t *csize); - -int igraph_modularity(const igraph_t *graph, - const igraph_vector_t *membership, - igraph_real_t *modularity, - const igraph_vector_t *weights); - -int igraph_reindex_membership(igraph_vector_t *membership, - igraph_vector_t *new_to_old); - -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); - -int igraph_community_leading_eigenvector(const igraph_t *graph, - 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); - -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); -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); - -/* -------------------------------------------------- */ -/* Community Structure Comparison */ -/* -------------------------------------------------- */ - -int igraph_compare_communities(const igraph_vector_t *comm1, - const igraph_vector_t *comm2, - igraph_real_t* result, - igraph_community_comparison_t method); -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/src/leidenalg/igraph-R/igraph_complex.h b/src/leidenalg/igraph-R/igraph_complex.h deleted file mode 100644 index 9821038..0000000 --- a/src/leidenalg/igraph-R/igraph_complex.h +++ /dev/null @@ -1,111 +0,0 @@ -/* -*- mode: C -*- */ -/* - IGraph library. - Copyright (C) 2010-2012 Gabor Csardi - 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 - -#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" - -__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]) - -igraph_complex_t igraph_complex(igraph_real_t x, igraph_real_t y); -igraph_complex_t igraph_complex_polar(igraph_real_t r, igraph_real_t theta); - -igraph_bool_t igraph_complex_eq_tol(igraph_complex_t z1, - igraph_complex_t z2, - igraph_real_t tol); - -igraph_real_t igraph_complex_mod(igraph_complex_t z); -igraph_real_t igraph_complex_arg(igraph_complex_t z); - -igraph_real_t igraph_complex_abs(igraph_complex_t z); -igraph_real_t igraph_complex_logabs(igraph_complex_t z); - -igraph_complex_t igraph_complex_add(igraph_complex_t z1, - igraph_complex_t z2); -igraph_complex_t igraph_complex_sub(igraph_complex_t z1, - igraph_complex_t z2); -igraph_complex_t igraph_complex_mul(igraph_complex_t z1, - igraph_complex_t z2); -igraph_complex_t igraph_complex_div(igraph_complex_t z1, - igraph_complex_t z2); - -igraph_complex_t igraph_complex_add_real(igraph_complex_t z, - igraph_real_t x); -igraph_complex_t igraph_complex_add_imag(igraph_complex_t z, - igraph_real_t y); -igraph_complex_t igraph_complex_sub_real(igraph_complex_t z, - igraph_real_t x); -igraph_complex_t igraph_complex_sub_imag(igraph_complex_t z, - igraph_real_t y); -igraph_complex_t igraph_complex_mul_real(igraph_complex_t z, - igraph_real_t x); -igraph_complex_t igraph_complex_mul_imag(igraph_complex_t z, - igraph_real_t y); -igraph_complex_t igraph_complex_div_real(igraph_complex_t z, - igraph_real_t x); -igraph_complex_t igraph_complex_div_imag(igraph_complex_t z, - igraph_real_t y); - -igraph_complex_t igraph_complex_conj(igraph_complex_t z); -igraph_complex_t igraph_complex_neg(igraph_complex_t z); -igraph_complex_t igraph_complex_inv(igraph_complex_t z); - -igraph_complex_t igraph_complex_sqrt(igraph_complex_t z); -igraph_complex_t igraph_complex_sqrt_real(igraph_real_t x); -igraph_complex_t igraph_complex_exp(igraph_complex_t z); -igraph_complex_t igraph_complex_pow(igraph_complex_t z1, - igraph_complex_t z2); -igraph_complex_t igraph_complex_pow_real(igraph_complex_t z, - igraph_real_t x); -igraph_complex_t igraph_complex_log(igraph_complex_t z); -igraph_complex_t igraph_complex_log10(igraph_complex_t z); -igraph_complex_t igraph_complex_log_b(igraph_complex_t z, - igraph_complex_t b); - -igraph_complex_t igraph_complex_sin(igraph_complex_t z); -igraph_complex_t igraph_complex_cos(igraph_complex_t z); -igraph_complex_t igraph_complex_tan(igraph_complex_t z); -igraph_complex_t igraph_complex_sec(igraph_complex_t z); -igraph_complex_t igraph_complex_csc(igraph_complex_t z); -igraph_complex_t igraph_complex_cot(igraph_complex_t z); - -__END_DECLS - -#endif diff --git a/src/leidenalg/igraph-R/igraph_components.h b/src/leidenalg/igraph-R/igraph_components.h deleted file mode 100644 index b243be9..0000000 --- a/src/leidenalg/igraph-R/igraph_components.h +++ /dev/null @@ -1,69 +0,0 @@ -/* -*- mode: C -*- */ -/* - IGraph library. - Copyright (C) 2009-2012 Gabor Csardi - 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 - -#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_constants.h" -#include "igraph_types.h" -#include "igraph_vector.h" -#include "igraph_vector_ptr.h" -#include "igraph_datatype.h" - -__BEGIN_DECLS - -/* -------------------------------------------------- */ -/* Components */ -/* -------------------------------------------------- */ - -int igraph_clusters(const igraph_t *graph, igraph_vector_t *membership, - igraph_vector_t *csize, igraph_integer_t *no, - igraph_connectedness_t mode); -int igraph_is_connected(const igraph_t *graph, igraph_bool_t *res, - igraph_connectedness_t mode); -void igraph_decompose_destroy(igraph_vector_ptr_t *complist); -int igraph_decompose(const igraph_t *graph, igraph_vector_ptr_t *components, - igraph_connectedness_t mode, - long int maxcompno, long int minelements); -int igraph_articulation_points(const igraph_t *graph, - igraph_vector_t *res); -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); - -__END_DECLS - -#endif diff --git a/src/leidenalg/igraph-R/igraph_constants.h b/src/leidenalg/igraph-R/igraph_constants.h deleted file mode 100644 index e6476a4..0000000 --- a/src/leidenalg/igraph-R/igraph_constants.h +++ /dev/null @@ -1,157 +0,0 @@ -/* -*- mode: C -*- */ -/* - IGraph library. - Copyright (C) 2009-2012 Gabor Csardi - 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 - -#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" - -__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_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_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); - -__END_DECLS - -#endif diff --git a/src/leidenalg/igraph-R/igraph_constructors.h b/src/leidenalg/igraph-R/igraph_constructors.h deleted file mode 100644 index e2572c1..0000000 --- a/src/leidenalg/igraph-R/igraph_constructors.h +++ /dev/null @@ -1,85 +0,0 @@ -/* -*- mode: C -*- */ -/* - IGraph library. - Copyright (C) 2009-2012 Gabor Csardi - 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 - -#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_constants.h" -#include "igraph_types.h" -#include "igraph_matrix.h" -#include "igraph_datatype.h" - -__BEGIN_DECLS - -/* -------------------------------------------------- */ -/* Constructors, deterministic */ -/* -------------------------------------------------- */ - -int igraph_create(igraph_t *graph, const igraph_vector_t *edges, igraph_integer_t n, - igraph_bool_t directed); -int igraph_small(igraph_t *graph, igraph_integer_t n, igraph_bool_t directed, - ...); -int igraph_adjacency(igraph_t *graph, igraph_matrix_t *adjmatrix, - igraph_adjacency_t mode); -int igraph_weighted_adjacency(igraph_t *graph, igraph_matrix_t *adjmatrix, - igraph_adjacency_t mode, const char* attr, - igraph_bool_t loops); -int igraph_star(igraph_t *graph, igraph_integer_t n, igraph_star_mode_t mode, - igraph_integer_t center); -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); -int igraph_ring(igraph_t *graph, igraph_integer_t n, igraph_bool_t directed, - igraph_bool_t mutual, igraph_bool_t circular); -int igraph_tree(igraph_t *graph, igraph_integer_t n, igraph_integer_t children, - igraph_tree_mode_t type); -int igraph_full(igraph_t *graph, igraph_integer_t n, igraph_bool_t directed, igraph_bool_t loops); -int igraph_full_citation(igraph_t *graph, igraph_integer_t n, - igraph_bool_t directed); -int igraph_atlas(igraph_t *graph, int number); -int igraph_extended_chordal_ring(igraph_t *graph, igraph_integer_t nodes, - const igraph_matrix_t *W); -int igraph_connect_neighborhood(igraph_t *graph, igraph_integer_t order, - igraph_neimode_t mode); -int igraph_linegraph(const igraph_t *graph, igraph_t *linegraph); - -int igraph_de_bruijn(igraph_t *graph, igraph_integer_t m, igraph_integer_t n); -int igraph_kautz(igraph_t *graph, igraph_integer_t m, igraph_integer_t n); -int igraph_famous(igraph_t *graph, const char *name); -int igraph_lcf_vector(igraph_t *graph, igraph_integer_t n, - const igraph_vector_t *shifts, - igraph_integer_t repeats); -int igraph_lcf(igraph_t *graph, igraph_integer_t n, ...); - -__END_DECLS - -#endif diff --git a/src/leidenalg/igraph-R/igraph_dqueue_pmt.h b/src/leidenalg/igraph-R/igraph_dqueue_pmt.h deleted file mode 100644 index 5646408..0000000 --- a/src/leidenalg/igraph-R/igraph_dqueue_pmt.h +++ /dev/null @@ -1,49 +0,0 @@ -/* -*- mode: C -*- */ -/* - IGraph library. - Copyright (C) 2007-2012 Gabor Csardi - 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); - -int FUNCTION(igraph_dqueue,init) (TYPE(igraph_dqueue)* q, long int size); -void FUNCTION(igraph_dqueue,destroy) (TYPE(igraph_dqueue)* q); -igraph_bool_t FUNCTION(igraph_dqueue,empty) (const TYPE(igraph_dqueue)* q); -void FUNCTION(igraph_dqueue,clear) (TYPE(igraph_dqueue)* q); -igraph_bool_t FUNCTION(igraph_dqueue,full) (TYPE(igraph_dqueue)* q); -long int FUNCTION(igraph_dqueue,size) (const TYPE(igraph_dqueue)* q); -BASE FUNCTION(igraph_dqueue,pop) (TYPE(igraph_dqueue)* q); -BASE FUNCTION(igraph_dqueue,pop_back)(TYPE(igraph_dqueue)* q); -BASE FUNCTION(igraph_dqueue,head) (const TYPE(igraph_dqueue)* q); -BASE FUNCTION(igraph_dqueue,back) (const TYPE(igraph_dqueue)* q); -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); -BASE FUNCTION(igraph_dqueue,e)(const TYPE(igraph_dqueue) *q, long int idx); diff --git a/src/leidenalg/igraph-R/igraph_eigen.h b/src/leidenalg/igraph-R/igraph_eigen.h deleted file mode 100644 index 5f50896..0000000 --- a/src/leidenalg/igraph-R/igraph_eigen.h +++ /dev/null @@ -1,118 +0,0 @@ -/* -*- mode: C -*- */ -/* - IGraph library. - Copyright (C) 2010-2012 Gabor Csardi - 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 - -#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 - -typedef enum { IGRAPH_EIGEN_AUTO, - 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, - IGRAPH_EIGEN_SM, - IGRAPH_EIGEN_LA, - IGRAPH_EIGEN_SA, - IGRAPH_EIGEN_BE, - IGRAPH_EIGEN_LR, - IGRAPH_EIGEN_SR, - IGRAPH_EIGEN_LI, - IGRAPH_EIGEN_SI, - IGRAPH_EIGEN_ALL, - IGRAPH_EIGEN_INTERVAL, - IGRAPH_EIGEN_SELECT } 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; - - -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); - -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); - -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); - -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/src/leidenalg/igraph-R/igraph_flow.h b/src/leidenalg/igraph-R/igraph_flow.h deleted file mode 100644 index 09d6225..0000000 --- a/src/leidenalg/igraph-R/igraph_flow.h +++ /dev/null @@ -1,150 +0,0 @@ -/* -*- mode: C -*- */ -/* - IGraph library. - Copyright (C) 2003-2012 Gabor Csardi - 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 - -#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_constants.h" -#include "igraph_types.h" -#include "igraph_datatype.h" -#include "igraph_vector_ptr.h" - -__BEGIN_DECLS - -/* -------------------------------------------------- */ -/* MAximum flows, minimum cuts & such */ -/* -------------------------------------------------- */ - -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); -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); - -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); -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); - -int igraph_mincut_value(const igraph_t *graph, igraph_real_t *res, - const igraph_vector_t *capacity); -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); - -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); -int igraph_vertex_connectivity(const igraph_t *graph, igraph_integer_t *res, - igraph_bool_t checks); - -int igraph_st_edge_connectivity(const igraph_t *graph, igraph_integer_t *res, - igraph_integer_t source, - igraph_integer_t target); -int igraph_edge_connectivity(const igraph_t *graph, igraph_integer_t *res, - igraph_bool_t checks); - -int igraph_edge_disjoint_paths(const igraph_t *graph, igraph_integer_t *res, - igraph_integer_t source, - igraph_integer_t target); -int igraph_vertex_disjoint_paths(const igraph_t *graph, igraph_integer_t *res, - igraph_integer_t source, - igraph_integer_t target); - -int igraph_adhesion(const igraph_t *graph, igraph_integer_t *res, - igraph_bool_t checks); -int igraph_cohesion(const igraph_t *graph, igraph_integer_t *res, - igraph_bool_t checks); - -/* s-t cut listing related stuff */ - -int igraph_even_tarjan_reduction(const igraph_t *graph, igraph_t *graphbar, - igraph_vector_t *capacity); - -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); -int igraph_reverse_residual_graph(const igraph_t *graph, - const igraph_vector_t *capacity, - igraph_t *residual, - const igraph_vector_t *flow); - -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); - -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); - -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); - -__END_DECLS - -#endif diff --git a/src/leidenalg/igraph-R/igraph_foreign.h b/src/leidenalg/igraph-R/igraph_foreign.h deleted file mode 100644 index f96e38e..0000000 --- a/src/leidenalg/igraph-R/igraph_foreign.h +++ /dev/null @@ -1,93 +0,0 @@ -/* -*- mode: C -*- */ -/* - IGraph library. - Copyright (C) 2009-2012 Gabor Csardi - 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 - -#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_constants.h" -#include "igraph_datatype.h" -#include "igraph_types.h" -#include "igraph_strvector.h" - -#include - -__BEGIN_DECLS - -/* -------------------------------------------------- */ -/* Read and write foreign formats */ -/* -------------------------------------------------- */ - -int igraph_read_graph_edgelist(igraph_t *graph, FILE *instream, - igraph_integer_t n, igraph_bool_t directed); -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); -int igraph_read_graph_lgl(igraph_t *graph, FILE *instream, - igraph_bool_t names, igraph_add_weights_t weights, - igraph_bool_t directed); -int igraph_read_graph_pajek(igraph_t *graph, FILE *instream); -int igraph_read_graph_graphml(igraph_t *graph, FILE *instream, - int index); -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); -int igraph_read_graph_graphdb(igraph_t *graph, FILE *instream, - igraph_bool_t directed); -int igraph_read_graph_gml(igraph_t *graph, FILE *instream); -int igraph_read_graph_dl(igraph_t *graph, FILE *instream, - igraph_bool_t directed); - -int igraph_write_graph_edgelist(const igraph_t *graph, FILE *outstream); -int igraph_write_graph_ncol(const igraph_t *graph, FILE *outstream, - const char *names, const char *weights); -int igraph_write_graph_lgl(const igraph_t *graph, FILE *outstream, - const char *names, const char *weights, - igraph_bool_t isolates); -int igraph_write_graph_graphml(const igraph_t *graph, FILE *outstream); -int igraph_write_graph_pajek(const igraph_t *graph, FILE *outstream); -int igraph_write_graph_dimacs(const igraph_t *graph, FILE *outstream, - long int source, long int target, - const igraph_vector_t *capacity); -int igraph_write_graph_gml(const igraph_t *graph, FILE *outstream, - const igraph_vector_t *id, const char *creator); -int igraph_write_graph_dot(const igraph_t *graph, FILE *outstream); -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/src/leidenalg/igraph-R/igraph_games.h b/src/leidenalg/igraph-R/igraph_games.h deleted file mode 100644 index 78d26c0..0000000 --- a/src/leidenalg/igraph-R/igraph_games.h +++ /dev/null @@ -1,185 +0,0 @@ -/* -*- mode: C -*- */ -/* - IGraph library. - Copyright (C) 2009-2012 Gabor Csardi - 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 - -#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_constants.h" -#include "igraph_types.h" -#include "igraph_matrix.h" -#include "igraph_vector.h" -#include "igraph_datatype.h" - -__BEGIN_DECLS - -/* -------------------------------------------------- */ -/* Constructors, games (=stochastic) */ -/* -------------------------------------------------- */ - -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); -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); -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); -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); -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); -int igraph_degree_sequence_game(igraph_t *graph, const igraph_vector_t *out_deg, - const igraph_vector_t *in_deg, - igraph_degseq_t method); -int igraph_growing_random_game(igraph_t *graph, igraph_integer_t n, - igraph_integer_t m, igraph_bool_t directed, igraph_bool_t citation); -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); -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); -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); -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); -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); -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); -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); -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); - -int igraph_rewire_edges(igraph_t *graph, igraph_real_t prob, - igraph_bool_t loops, igraph_bool_t multiple); -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); - -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); - -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); - -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); - -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); - - -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); - -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); - -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); - -__END_DECLS - -#endif diff --git a/src/leidenalg/igraph-R/igraph_heap_pmt.h b/src/leidenalg/igraph-R/igraph_heap_pmt.h deleted file mode 100644 index 02a704a..0000000 --- a/src/leidenalg/igraph-R/igraph_heap_pmt.h +++ /dev/null @@ -1,45 +0,0 @@ -/* -*- mode: C -*- */ -/* - IGraph library. - Copyright (C) 2007-2012 Gabor Csardi - 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); - -int FUNCTION(igraph_heap,init)(TYPE(igraph_heap)* h, long int size); -int FUNCTION(igraph_heap,init_array)(TYPE(igraph_heap) *t, BASE* data, long int len); -void FUNCTION(igraph_heap,destroy)(TYPE(igraph_heap)* h); -igraph_bool_t FUNCTION(igraph_heap,empty)(TYPE(igraph_heap)* h); -int FUNCTION(igraph_heap,push)(TYPE(igraph_heap)* h, BASE elem); -BASE FUNCTION(igraph_heap,top)(TYPE(igraph_heap)* h); -BASE FUNCTION(igraph_heap,delete_top)(TYPE(igraph_heap)* h); -long int FUNCTION(igraph_heap,size)(TYPE(igraph_heap)* h); -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/src/leidenalg/igraph-R/igraph_interface.h b/src/leidenalg/igraph-R/igraph_interface.h deleted file mode 100644 index 4031b1f..0000000 --- a/src/leidenalg/igraph-R/igraph_interface.h +++ /dev/null @@ -1,95 +0,0 @@ -/* -*- mode: C -*- */ -/* - IGraph library. - Copyright (C) 2009-2012 Gabor Csardi - 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 - -#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" - -__BEGIN_DECLS - -/* -------------------------------------------------- */ -/* Interface */ -/* -------------------------------------------------- */ - -int igraph_empty(igraph_t *graph, igraph_integer_t n, igraph_bool_t directed); -int igraph_empty_attrs(igraph_t *graph, igraph_integer_t n, igraph_bool_t directed, void *attr); -int igraph_destroy(igraph_t *graph); -int igraph_copy(igraph_t *to, const igraph_t *from); -int igraph_add_edges(igraph_t *graph, const igraph_vector_t *edges, - void *attr); -int igraph_add_vertices(igraph_t *graph, igraph_integer_t nv, - void *attr); -int igraph_delete_edges(igraph_t *graph, igraph_es_t edges); -int igraph_delete_vertices(igraph_t *graph, const igraph_vs_t vertices); -int igraph_delete_vertices_idx(igraph_t *graph, const igraph_vs_t vertices, - igraph_vector_t *idx, - igraph_vector_t *invidx); -igraph_integer_t igraph_vcount(const igraph_t *graph); -igraph_integer_t igraph_ecount(const igraph_t *graph); -int igraph_neighbors(const igraph_t *graph, igraph_vector_t *neis, igraph_integer_t vid, - igraph_neimode_t mode); -igraph_bool_t igraph_is_directed(const igraph_t *graph); -int igraph_degree(const igraph_t *graph, igraph_vector_t *res, - const igraph_vs_t vids, igraph_neimode_t mode, - igraph_bool_t loops); -int igraph_edge(const igraph_t *graph, igraph_integer_t eid, - igraph_integer_t *from, igraph_integer_t *to); -int igraph_edges(const igraph_t *graph, igraph_es_t eids, - igraph_vector_t *edges); -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); -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); -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); -int igraph_adjacent(const igraph_t *graph, igraph_vector_t *eids, igraph_integer_t vid, - igraph_neimode_t mode); /* deprecated */ -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/src/leidenalg/igraph-R/igraph_lapack.h b/src/leidenalg/igraph-R/igraph_lapack.h deleted file mode 100644 index c8e17cb..0000000 --- a/src/leidenalg/igraph-R/igraph_lapack.h +++ /dev/null @@ -1,118 +0,0 @@ -/* -*- mode: C -*- */ -/* - IGraph library. - Copyright (C) 2010-2012 Gabor Csardi - 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_H -#define LAPACK_H - -#include "igraph_vector.h" -#include "igraph_matrix.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 - -/** - * \section about_lapack LAPACK interface in igraph - * - * - * 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. - * - * - * - * igraph provides an interface to a very limited set of LAPACK - * functions, using the regular igraph data structures. - * - * - * - * See more about LAPACK at http://www.netlib.org/lapack/ - * - */ - -int igraph_lapack_dgetrf(igraph_matrix_t *a, igraph_vector_int_t *ipiv, - int *info); -int igraph_lapack_dgetrs(igraph_bool_t transpose, const igraph_matrix_t *a, - igraph_vector_int_t *ipiv, igraph_matrix_t *b); -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; - -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? */ - -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, - IGRAPH_LAPACK_DGEEVX_BALANCE_PERM, - IGRAPH_LAPACK_DGEEVX_BALANCE_SCALE, - IGRAPH_LAPACK_DGEEVX_BALANCE_BOTH } - igraph_lapack_dgeevx_balance_t; - -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); - -int igraph_lapack_dgehrd(const igraph_matrix_t *A, - int ilo, int ihi, - igraph_matrix_t *result); - -__END_DECLS - -#endif diff --git a/src/leidenalg/igraph-R/igraph_layout.h b/src/leidenalg/igraph-R/igraph_layout.h deleted file mode 100644 index f51fbca..0000000 --- a/src/leidenalg/igraph-R/igraph_layout.h +++ /dev/null @@ -1,248 +0,0 @@ -/* -*- mode: C -*- */ -/* - IGraph library. - Copyright (C) 2009-2012 Gabor Csardi - 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 - -#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_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" - -__BEGIN_DECLS - -/* -------------------------------------------------- */ -/* Layouts */ -/* -------------------------------------------------- */ - -int igraph_layout_random(const igraph_t *graph, igraph_matrix_t *res); -int igraph_layout_circle(const igraph_t *graph, igraph_matrix_t *res); -int igraph_layout_star(const igraph_t *graph, igraph_matrix_t *res, - igraph_integer_t center, const igraph_vector_t *order); -int igraph_layout_grid(const igraph_t *graph, igraph_matrix_t *res, long int width); -int igraph_layout_fruchterman_reingold(const igraph_t *graph, igraph_matrix_t *res, - igraph_integer_t niter, igraph_real_t maxdelta, - igraph_real_t area, igraph_real_t coolexp, - igraph_real_t repulserad, igraph_bool_t use_seed, - 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); -int igraph_layout_grid_fruchterman_reingold(const igraph_t *graph, - igraph_matrix_t *res, - igraph_integer_t niter, igraph_real_t maxdelta, - igraph_real_t area, igraph_real_t coolexp, - igraph_real_t repulserad, - igraph_real_t cellsize, igraph_bool_t use_seed, - const igraph_vector_t *weight); -int igraph_layout_kamada_kawai(const igraph_t *graph, igraph_matrix_t *res, - igraph_integer_t niter, igraph_real_t sigma, - igraph_real_t initemp, igraph_real_t coolexp, - igraph_real_t kkconst, igraph_bool_t use_seed, - const igraph_vector_t *minx, - const igraph_vector_t *maxx, - const igraph_vector_t *miny, - const igraph_vector_t *maxy); -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); -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); -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); -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); -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); - -int igraph_layout_random_3d(const igraph_t *graph, igraph_matrix_t *res); -int igraph_layout_sphere(const igraph_t *graph, igraph_matrix_t *res); -int igraph_layout_grid_3d(const igraph_t *graph, igraph_matrix_t *res, - long int width, long int height); -int igraph_layout_fruchterman_reingold_3d(const igraph_t *graph, - igraph_matrix_t *res, - igraph_integer_t niter, igraph_real_t maxdelta, - igraph_real_t volume, igraph_real_t coolexp, - igraph_real_t repulserad, - igraph_bool_t use_seed, - 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); - -int igraph_layout_kamada_kawai_3d(const igraph_t *graph, igraph_matrix_t *res, - igraph_integer_t niter, igraph_real_t sigma, - igraph_real_t initemp, igraph_real_t coolexp, - igraph_real_t kkconst, igraph_bool_t use_seed, - igraph_bool_t fixz, - 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); - -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); - -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); - -/** - * \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; - -int igraph_layout_drl_options_init(igraph_layout_drl_options_t *options, - igraph_layout_drl_default_t templ); -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); - -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); - -int igraph_layout_merge_dla(igraph_vector_ptr_t *graphs, - igraph_vector_ptr_t *coords, - igraph_matrix_t *res); - -__END_DECLS - -#endif diff --git a/src/leidenalg/igraph-R/igraph_matching.h b/src/leidenalg/igraph-R/igraph_matching.h deleted file mode 100644 index 46194be..0000000 --- a/src/leidenalg/igraph-R/igraph_matching.h +++ /dev/null @@ -1,65 +0,0 @@ -/* -*- mode: C -*- */ -/* - IGraph library. - 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 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public 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 - -#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_constants.h" -#include "igraph_datatype.h" -#include "igraph_types.h" -#include "igraph_vector.h" - -__BEGIN_DECLS - -/* -------------------------------------------------- */ -/* Matchings in graphs */ -/* -------------------------------------------------- */ - -int igraph_is_matching(const igraph_t* graph, - const igraph_vector_bool_t* types, const igraph_vector_long_t* matching, - igraph_bool_t* result); -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); - -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); - -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/src/leidenalg/igraph-R/igraph_matrix_pmt.h b/src/leidenalg/igraph-R/igraph_matrix_pmt.h deleted file mode 100644 index f58c4ce..0000000 --- a/src/leidenalg/igraph-R/igraph_matrix_pmt.h +++ /dev/null @@ -1,232 +0,0 @@ -/* -*- mode: C -*- */ -/* - IGraph library. - Copyright (C) 2007-2012 Gabor Csardi - 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 */ -/*---------------*/ - -int FUNCTION(igraph_matrix,init)(TYPE(igraph_matrix) *m, - long int nrow, long int ncol); -int FUNCTION(igraph_matrix,copy)(TYPE(igraph_matrix) *to, - const TYPE(igraph_matrix) *from); -void FUNCTION(igraph_matrix,destroy)(TYPE(igraph_matrix) *m); -long int FUNCTION(igraph_matrix,capacity)(const TYPE(igraph_matrix) *m); - -/*--------------------*/ -/* Accessing elements */ -/*--------------------*/ - -/* MATRIX */ -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); -void FUNCTION(igraph_matrix,set)(TYPE(igraph_matrix)* m, long int row, long int col, - BASE value); - -/*------------------------------*/ -/* Initializing matrix elements */ -/*------------------------------*/ - -void FUNCTION(igraph_matrix,null)(TYPE(igraph_matrix) *m); -void FUNCTION(igraph_matrix,fill)(TYPE(igraph_matrix) *m, BASE e); - -/*------------------*/ -/* Copying matrices */ -/*------------------*/ - -void FUNCTION(igraph_matrix,copy_to)(const TYPE(igraph_matrix) *m, BASE *to); -int FUNCTION(igraph_matrix,update)(TYPE(igraph_matrix) *to, - const TYPE(igraph_matrix) *from); -int FUNCTION(igraph_matrix,rbind)(TYPE(igraph_matrix) *to, - const TYPE(igraph_matrix) *from); -int FUNCTION(igraph_matrix,cbind)(TYPE(igraph_matrix) *to, - const TYPE(igraph_matrix) *from); -int FUNCTION(igraph_matrix,swap)(TYPE(igraph_matrix) *m1, TYPE(igraph_matrix) *m2); - -/*--------------------------*/ -/* Copying rows and columns */ -/*--------------------------*/ - -int FUNCTION(igraph_matrix,get_row)(const TYPE(igraph_matrix) *m, - TYPE(igraph_vector) *res, long int index); -int FUNCTION(igraph_matrix,get_col)(const TYPE(igraph_matrix) *m, - TYPE(igraph_vector) *res, long int index); -int FUNCTION(igraph_matrix,set_row)(TYPE(igraph_matrix) *m, - const TYPE(igraph_vector) *v, long int index); -int FUNCTION(igraph_matrix,set_col)(TYPE(igraph_matrix) *m, - const TYPE(igraph_vector) *v, long int index); -int FUNCTION(igraph_matrix,select_rows)(const TYPE(igraph_matrix) *m, - TYPE(igraph_matrix) *res, - const igraph_vector_t *rows); -int FUNCTION(igraph_matrix,select_cols)(const TYPE(igraph_matrix) *m, - TYPE(igraph_matrix) *res, - const igraph_vector_t *cols); -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 */ -/*-----------------------------*/ - -int FUNCTION(igraph_matrix,swap_rows)(TYPE(igraph_matrix) *m, - long int i, long int j); -int FUNCTION(igraph_matrix,swap_cols)(TYPE(igraph_matrix) *m, - long int i, long int j); -int FUNCTION(igraph_matrix,swap_rowcol)(TYPE(igraph_matrix) *m, - long int i, long int j); -int FUNCTION(igraph_matrix,transpose)(TYPE(igraph_matrix) *m); - -/*-----------------------------*/ -/* Matrix operations */ -/*-----------------------------*/ - -int FUNCTION(igraph_matrix,add)(TYPE(igraph_matrix) *m1, - const TYPE(igraph_matrix) *m2); -int FUNCTION(igraph_matrix,sub)(TYPE(igraph_matrix) *m1, - const TYPE(igraph_matrix) *m2); -int FUNCTION(igraph_matrix,mul_elements)(TYPE(igraph_matrix) *m1, - const TYPE(igraph_matrix) *m2); -int FUNCTION(igraph_matrix,div_elements)(TYPE(igraph_matrix) *m1, - const TYPE(igraph_matrix) *m2); -void FUNCTION(igraph_matrix,scale)(TYPE(igraph_matrix) *m, BASE by); -void FUNCTION(igraph_matrix,add_constant)(TYPE(igraph_matrix) *m, BASE plus); - -/*-----------------------------*/ -/* Finding minimum and maximum */ -/*-----------------------------*/ - -igraph_real_t FUNCTION(igraph_matrix,min)(const TYPE(igraph_matrix) *m); -igraph_real_t FUNCTION(igraph_matrix,max)(const TYPE(igraph_matrix) *m); -int FUNCTION(igraph_matrix,which_min)(const TYPE(igraph_matrix) *m, - long int *i, long int *j); -int FUNCTION(igraph_matrix,which_max)(const TYPE(igraph_matrix) *m, - long int *i, long int *j); -int FUNCTION(igraph_matrix,minmax)(const TYPE(igraph_matrix) *m, - BASE *min, BASE *max); -int FUNCTION(igraph_matrix,which_minmax)(const TYPE(igraph_matrix) *m, - long int *imin, long int *jmin, - long int *imax, long int *jmax); - -/*------------------------------*/ -/* Comparison */ -/*------------------------------*/ - -igraph_bool_t FUNCTION(igraph_matrix,all_e)(const TYPE(igraph_matrix) *lhs, - const TYPE(igraph_matrix) *rhs); -igraph_bool_t FUNCTION(igraph_matrix,all_l)(const TYPE(igraph_matrix) *lhs, - const TYPE(igraph_matrix) *rhs); -igraph_bool_t FUNCTION(igraph_matrix,all_g)(const TYPE(igraph_matrix) *lhs, - const TYPE(igraph_matrix) *rhs); -igraph_bool_t FUNCTION(igraph_matrix,all_le)(const TYPE(igraph_matrix) *lhs, - const TYPE(igraph_matrix) *rhs); -igraph_bool_t FUNCTION(igraph_matrix,all_ge)(const TYPE(igraph_matrix) *lhs, - const TYPE(igraph_matrix) *rhs); - -/*-------------------*/ -/* Matrix properties */ -/*-------------------*/ - -igraph_bool_t FUNCTION(igraph_matrix,isnull)(const TYPE(igraph_matrix) *m); -igraph_bool_t FUNCTION(igraph_matrix,empty)(const TYPE(igraph_matrix) *m); -long int FUNCTION(igraph_matrix,size)(const TYPE(igraph_matrix) *m); -long int FUNCTION(igraph_matrix,nrow)(const TYPE(igraph_matrix) *m); -long int FUNCTION(igraph_matrix,ncol)(const TYPE(igraph_matrix) *m); -igraph_bool_t FUNCTION(igraph_matrix,is_symmetric)(const TYPE(igraph_matrix) *m); -BASE FUNCTION(igraph_matrix,sum)(const TYPE(igraph_matrix) *m); -BASE FUNCTION(igraph_matrix,prod)(const TYPE(igraph_matrix) *m); -int FUNCTION(igraph_matrix,rowsum)(const TYPE(igraph_matrix) *m, - TYPE(igraph_vector) *res); -int FUNCTION(igraph_matrix,colsum)(const TYPE(igraph_matrix) *m, - TYPE(igraph_vector) *res); -igraph_bool_t FUNCTION(igraph_matrix,is_equal)(const TYPE(igraph_matrix) *m1, - const TYPE(igraph_matrix) *m2); -BASE FUNCTION(igraph_matrix,maxdifference)(const TYPE(igraph_matrix) *m1, - const TYPE(igraph_matrix) *m2); - -/*------------------------*/ -/* Searching for elements */ -/*------------------------*/ - -igraph_bool_t FUNCTION(igraph_matrix,contains)(const TYPE(igraph_matrix) *m, - BASE e); -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 */ -/*------------------------*/ - -int FUNCTION(igraph_matrix,resize)(TYPE(igraph_matrix) *m, - long int nrow, long int ncol); -int FUNCTION(igraph_matrix,resize_min)(TYPE(igraph_matrix) *m); -int FUNCTION(igraph_matrix,add_cols)(TYPE(igraph_matrix) *m, long int n); -int FUNCTION(igraph_matrix,add_rows)(TYPE(igraph_matrix) *m, long int n); -int FUNCTION(igraph_matrix,remove_col)(TYPE(igraph_matrix) *m, long int col); -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,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/src/leidenalg/igraph-R/igraph_microscopic_update.h b/src/leidenalg/igraph-R/igraph_microscopic_update.h deleted file mode 100644 index 5789eb9..0000000 --- a/src/leidenalg/igraph-R/igraph_microscopic_update.h +++ /dev/null @@ -1,69 +0,0 @@ -/* -*- mode: C -*- */ -/* - Microscopic update rules for dealing with agent-level strategy revision. - Copyright (C) 2011 Minh Van Nguyen - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public 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 - -#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_constants.h" -#include "igraph_datatype.h" -#include "igraph_iterators.h" -#include "igraph_types.h" -#include "igraph_vector.h" - -__BEGIN_DECLS - -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); -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); -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); -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/src/leidenalg/igraph-R/igraph_nongraph.h b/src/leidenalg/igraph-R/igraph_nongraph.h deleted file mode 100644 index 082ae3e..0000000 --- a/src/leidenalg/igraph-R/igraph_nongraph.h +++ /dev/null @@ -1,66 +0,0 @@ -/* -*- mode: C -*- */ -/* - IGraph library. - Copyright (C) 2009-2012 Gabor Csardi - 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 - -#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_constants.h" -#include "igraph_matrix.h" -#include "igraph_types.h" -#include "igraph_vector.h" - -__BEGIN_DECLS - -/* -------------------------------------------------- */ -/* Other, not graph related */ -/* -------------------------------------------------- */ - -int igraph_running_mean(const igraph_vector_t *data, igraph_vector_t *res, - igraph_integer_t binwidth); -int igraph_fisher_yates_shuffle(igraph_vector_t *seq); -int igraph_random_sample(igraph_vector_t *res, igraph_real_t l, igraph_real_t h, - igraph_integer_t length); -int igraph_convex_hull(const igraph_matrix_t *data, igraph_vector_t *resverts, - igraph_matrix_t *rescoords); -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); -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); - -__END_DECLS - -#endif diff --git a/src/leidenalg/igraph-R/igraph_operators.h b/src/leidenalg/igraph-R/igraph_operators.h deleted file mode 100644 index 2e13b5c..0000000 --- a/src/leidenalg/igraph-R/igraph_operators.h +++ /dev/null @@ -1,65 +0,0 @@ -/* -*- mode: C -*- */ -/* - IGraph library. - Copyright (C) 2009-2012 Gabor Csardi - 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 - -#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_constants.h" -#include "igraph_types.h" -#include "igraph_datatype.h" -#include "igraph_vector_ptr.h" - -__BEGIN_DECLS - -/* -------------------------------------------------- */ -/* Graph operators */ -/* -------------------------------------------------- */ - -int igraph_disjoint_union(igraph_t *res, - const igraph_t *left, const igraph_t *right); -int igraph_disjoint_union_many(igraph_t *res, - const igraph_vector_ptr_t *graphs); -int igraph_union(igraph_t *res, const igraph_t *left, const igraph_t *right); -int igraph_union_many(igraph_t *res, const igraph_vector_ptr_t *graphs); -int igraph_intersection(igraph_t *res, - const igraph_t *left, const igraph_t *right); -int igraph_intersection_many(igraph_t *res, const igraph_vector_ptr_t *graphs); -int igraph_difference(igraph_t *res, - const igraph_t *orig, const igraph_t *sub); -int igraph_complementer(igraph_t *res, const igraph_t *graph, - igraph_bool_t loops); -int igraph_compose(igraph_t *res, const igraph_t *g1, const igraph_t *g2); - -__END_DECLS - -#endif diff --git a/src/leidenalg/igraph-R/igraph_paths.h b/src/leidenalg/igraph-R/igraph_paths.h deleted file mode 100644 index b91f85b..0000000 --- a/src/leidenalg/igraph-R/igraph_paths.h +++ /dev/null @@ -1,131 +0,0 @@ -/* -*- mode: C -*- */ -/* - IGraph library. - Copyright (C) 2009-2012 Gabor Csardi - 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 - -#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_constants.h" -#include "igraph_types.h" -#include "igraph_vector.h" -#include "igraph_vector_ptr.h" -#include "igraph_matrix.h" - -__BEGIN_DECLS - -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); -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); - -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); -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); -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); - -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); -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); -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); -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); -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); -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); -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); - -int igraph_average_path_length(const igraph_t *graph, igraph_real_t *res, - igraph_bool_t directed, igraph_bool_t unconn); -int igraph_path_length_hist(const igraph_t *graph, igraph_vector_t *res, - igraph_real_t *unconnected, igraph_bool_t directed); - -int igraph_eccentricity(const igraph_t *graph, - igraph_vector_t *res, - igraph_vs_t vids, - igraph_neimode_t mode); - -int igraph_radius(const igraph_t *graph, igraph_real_t *radius, - igraph_neimode_t mode); - -__END_DECLS - -#endif diff --git a/src/leidenalg/igraph-R/igraph_pmt.h b/src/leidenalg/igraph-R/igraph_pmt.h deleted file mode 100644 index ebb3912..0000000 --- a/src/leidenalg/igraph-R/igraph_pmt.h +++ /dev/null @@ -1,141 +0,0 @@ -/* -*- mode: C -*- */ -/* - IGraph library. - Copyright (C) 2007-2012 Gabor Csardi - 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_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 - -#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/src/leidenalg/igraph-R/igraph_random.h b/src/leidenalg/igraph-R/igraph_random.h deleted file mode 100644 index 2658248..0000000 --- a/src/leidenalg/igraph-R/igraph_random.h +++ /dev/null @@ -1,129 +0,0 @@ -/* -*- mode: C -*- */ -/* - IGraph library. - Copyright (C) 2003-2012 Gabor Csardi - 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 - -#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" - -#include -#include - -/* 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_rng_type_t; - -typedef struct igraph_rng_t { - const igraph_rng_type_t *type; - void *state; - int def; -} igraph_rng_t; - -/* --------------------------------- */ - -int igraph_rng_init(igraph_rng_t *rng, const igraph_rng_type_t *type); -void igraph_rng_destroy(igraph_rng_t *rng); - -int igraph_rng_seed(igraph_rng_t *rng, unsigned long int seed); -unsigned long int igraph_rng_max(igraph_rng_t *rng); -unsigned long int igraph_rng_min(igraph_rng_t *rng); -const char *igraph_rng_name(igraph_rng_t *rng); - -long int igraph_rng_get_integer(igraph_rng_t *rng, - long int l, long int h); -igraph_real_t igraph_rng_get_normal(igraph_rng_t *rng, - igraph_real_t m, igraph_real_t s); -igraph_real_t igraph_rng_get_unif(igraph_rng_t *rng, - igraph_real_t l, igraph_real_t h); -igraph_real_t igraph_rng_get_unif01(igraph_rng_t *rng); -igraph_real_t igraph_rng_get_geom(igraph_rng_t *rng, igraph_real_t p); -igraph_real_t igraph_rng_get_binom(igraph_rng_t *rng, long int n, - igraph_real_t p); -igraph_real_t igraph_rng_get_exp(igraph_rng_t *rng, igraph_real_t rate); -unsigned long int igraph_rng_get_int31(igraph_rng_t *rng); -igraph_real_t igraph_rng_get_exp(igraph_rng_t *rng, igraph_real_t rate); - -/* --------------------------------- */ - -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; - -igraph_rng_t *igraph_rng_default(void); -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() - -#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 */ - -#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/src/leidenalg/igraph-R/igraph_revolver.h b/src/leidenalg/igraph-R/igraph_revolver.h deleted file mode 100644 index 8b069cf..0000000 --- a/src/leidenalg/igraph-R/igraph_revolver.h +++ /dev/null @@ -1,1200 +0,0 @@ -/* -*- mode: C -*- */ -/* - IGraph library. - Copyright (C) 2009-2012 Gabor Csardi - 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_REVOLVER_H -#define IGRAPH_REVOLVER_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_constants.h" -#include "igraph_types.h" -#include "igraph_datatype.h" -#include "igraph_adjlist.h" -#include "igraph_matrix.h" -#include "igraph_array.h" -#include "igraph_vector_ptr.h" - -__BEGIN_DECLS - -/* -------------------------------------------------- */ -/* Network evolution measurement, new implementation */ -/* -------------------------------------------------- */ - -int igraph_evolver_d(igraph_t *graph, - igraph_integer_t nodes, - igraph_vector_t *kernel, - const igraph_vector_t *outseq, - const igraph_vector_t *outdist, - igraph_integer_t m, - igraph_bool_t directed); - -int igraph_revolver_d(const igraph_t *graph, - igraph_integer_t niter, - igraph_vector_t *kernel, - igraph_vector_t *sd, - igraph_vector_t *norm, - igraph_vector_t *cites, - igraph_vector_t *expected, - igraph_real_t *logprob, - igraph_real_t *lognull, - igraph_real_t *logmax, - const igraph_vector_t *debug, - igraph_vector_ptr_t *debugres); -int igraph_revolver_mes_d(const igraph_t *graph, - igraph_vector_t *kernel, - igraph_vector_t *sd, - igraph_vector_t *norm, - igraph_vector_t *cites, - const igraph_vector_t *debug, - igraph_vector_ptr_t *debugres, - igraph_real_t *logmax, - const igraph_vector_t *st, - igraph_integer_t pmaxind); -int igraph_revolver_st_d(const igraph_t *graph, - igraph_vector_t *st, - const igraph_vector_t *kernel); -int igraph_revolver_exp_d(const igraph_t *graphm, - igraph_vector_t *expected, - const igraph_vector_t *kernel, - const igraph_vector_t *st, - igraph_integer_t pmaxind); -int igraph_revolver_error_d(const igraph_t *graph, - const igraph_vector_t *kernel, - const igraph_vector_t *st, - igraph_integer_t maxind, - igraph_real_t *logprob, - igraph_real_t *lognull); -int igraph_revolver_error2_d(const igraph_t *graph, - const igraph_vector_t *kernel, - igraph_real_t *logprob, - igraph_real_t *lognull); - -int igraph_revolver_ad(const igraph_t *graph, - igraph_integer_t niter, - igraph_integer_t agebins, - igraph_matrix_t *kernel, - igraph_matrix_t *sd, - igraph_matrix_t *norm, - igraph_matrix_t *cites, - igraph_matrix_t *expected, - igraph_real_t *logprob, - igraph_real_t *lognull, - igraph_real_t *logmax, - const igraph_matrix_t *debug, - igraph_vector_ptr_t *debugres); -int igraph_revolver_mes_ad(const igraph_t *graph, - igraph_matrix_t *kernel, - igraph_matrix_t *sd, - igraph_matrix_t *norm, - igraph_matrix_t *cites, - const igraph_matrix_t *debug, - igraph_vector_ptr_t *debugres, - igraph_real_t *logmax, - const igraph_vector_t *st, - igraph_integer_t pmaxind, - igraph_integer_t agebins); -int igraph_revolver_st_ad(const igraph_t *graph, - igraph_vector_t *st, - const igraph_matrix_t *kernel); -int igraph_revolver_exp_ad(const igraph_t *graph, - igraph_matrix_t *expected, - const igraph_matrix_t *kernel, - const igraph_vector_t *st, - igraph_integer_t pmaxind, - igraph_integer_t agebins); -int igraph_revolver_error_ad(const igraph_t *graph, - const igraph_matrix_t *kernel, - const igraph_vector_t *st, - igraph_integer_t pmaxind, - igraph_integer_t pagebins, - igraph_real_t *logprob, - igraph_real_t *lognull); -int igraph_revolver_error2_ad(const igraph_t *graph, - const igraph_matrix_t *kernel, - igraph_real_t *logprob, - igraph_real_t *lognull); - -int igraph_revolver_ade(const igraph_t *graph, - igraph_integer_t niter, - igraph_integer_t agebins, - const igraph_vector_t *cats, - igraph_array3_t *kernel, - igraph_array3_t *sd, - igraph_array3_t *norm, - igraph_array3_t *cites, - igraph_array3_t *expected, - igraph_real_t *logprob, - igraph_real_t *lognull, - igraph_real_t *logmax, - const igraph_matrix_t *debug, - igraph_vector_ptr_t *debugres); -int igraph_revolver_mes_ade(const igraph_t *graph, - igraph_array3_t *kernel, - igraph_array3_t *sd, - igraph_array3_t *norm, - igraph_array3_t *cites, - const igraph_matrix_t *debug, - igraph_vector_ptr_t *debugres, - igraph_real_t *logmax, - const igraph_vector_t *st, - const igraph_vector_t *cats, - igraph_integer_t pnocats, - igraph_integer_t pmaxind, - igraph_integer_t pagebind); -int igraph_revolver_st_ade(const igraph_t *graph, - igraph_vector_t *st, - const igraph_array3_t *kernel, - const igraph_vector_t *cats); -int igraph_revolver_exp_ade(const igraph_t *graph, - igraph_array3_t *expected, - const igraph_array3_t *kernel, - const igraph_vector_t *st, - const igraph_vector_t *cats, - igraph_integer_t nocats, - igraph_integer_t maxdegree, - igraph_integer_t agebins); -int igraph_revolver_error_ade(const igraph_t *graph, - const igraph_array3_t *kernel, - const igraph_vector_t *st, - const igraph_vector_t *cats, - igraph_integer_t pnocats, - igraph_integer_t pmaxdegree, - igraph_integer_t pagebins, - igraph_real_t *logprob, - igraph_real_t *lognull); -int igraph_revolver_error2_ade(const igraph_t *graph, - const igraph_array3_t *kernel, - const igraph_vector_t *cats, - igraph_real_t *logprob, - igraph_real_t *lognull); - -int igraph_revolver_e(const igraph_t *graph, - igraph_integer_t niter, - const igraph_vector_t *cats, - igraph_vector_t *kernel, - igraph_vector_t *st, - igraph_vector_t *sd, - igraph_vector_t *norm, - igraph_vector_t *cites, - igraph_vector_t *expected, - igraph_real_t *logprob, - igraph_real_t *lognull, - igraph_real_t *logmax, - const igraph_vector_t *debug, - igraph_vector_ptr_t *debugres); -int igraph_revolver_mes_e(const igraph_t *graph, - igraph_vector_t *kernel, - igraph_vector_t *sd, - igraph_vector_t *norm, - igraph_vector_t *cites, - const igraph_vector_t *debug, - igraph_vector_ptr_t *debugres, - igraph_real_t *logmax, - const igraph_vector_t *st, - const igraph_vector_t *cats, - igraph_integer_t pnocats); -int igraph_revolver_st_e(const igraph_t *graph, - igraph_vector_t *st, - const igraph_vector_t *kernel, - const igraph_vector_t *cats); -int igraph_revolver_exp_e(const igraph_t *graph, - igraph_vector_t *expected, - const igraph_vector_t *kernel, - const igraph_vector_t *st, - const igraph_vector_t *cats, - igraph_integer_t pnocats); -int igraph_revolver_error_e(const igraph_t *graph, - const igraph_vector_t *kernel, - const igraph_vector_t *st, - const igraph_vector_t *cats, - igraph_integer_t pnocats, - igraph_real_t *logprob, - igraph_real_t *lognull); -int igraph_revolver_error2_e(const igraph_t *graph, - const igraph_vector_t *kernel, - const igraph_vector_t *cats, - igraph_real_t *logprob, - igraph_real_t *lognull); - -int igraph_revolver_de(const igraph_t *graph, - igraph_integer_t niter, - const igraph_vector_t *cats, - igraph_matrix_t *kernel, - igraph_matrix_t *sd, - igraph_matrix_t *norm, - igraph_matrix_t *cites, - igraph_matrix_t *expected, - igraph_real_t *logprob, - igraph_real_t *lognull, - igraph_real_t *logmax, - const igraph_matrix_t *debug, - igraph_vector_ptr_t *debugres); -int igraph_revolver_mes_de(const igraph_t *graph, - igraph_matrix_t *kernel, - igraph_matrix_t *sd, - igraph_matrix_t *norm, - igraph_matrix_t *cites, - const igraph_matrix_t *debug, - igraph_vector_ptr_t *debugres, - igraph_real_t *logmax, - const igraph_vector_t *st, - const igraph_vector_t *cats, - igraph_integer_t pnocats, - igraph_integer_t pmaxind); -int igraph_revolver_st_de(const igraph_t *graph, - igraph_vector_t *st, - const igraph_matrix_t *kernel, - const igraph_vector_t *cats); -int igraph_revolver_exp_de(const igraph_t *graph, - igraph_matrix_t *expected, - const igraph_matrix_t *kernel, - const igraph_vector_t *st, - const igraph_vector_t *cats, - igraph_integer_t pnocats, - igraph_integer_t pmaxind); -int igraph_revolver_error_de(const igraph_t *graph, - const igraph_matrix_t *kernel, - const igraph_vector_t *st, - const igraph_vector_t *cats, - igraph_integer_t pnocats, - igraph_integer_t pmaxind, - igraph_real_t *logprob, - igraph_real_t *lognull); -int igraph_revolver_error2_de(const igraph_t *graph, - const igraph_matrix_t *kernel, - const igraph_vector_t *cats, - igraph_real_t *logprob, - igraph_real_t *lognull); - -int igraph_revolver_l(const igraph_t *graph, - igraph_integer_t niter, - igraph_integer_t agebins, - igraph_vector_t *kernel, - igraph_vector_t *sd, - igraph_vector_t *norm, - igraph_vector_t *cites, - igraph_vector_t *expected, - igraph_real_t *logprob, - igraph_real_t *lognull, - igraph_real_t *logmax, - const igraph_vector_t *debug, - igraph_vector_ptr_t *debugres); -int igraph_revolver_mes_l(const igraph_t *graph, - igraph_vector_t *kernel, - igraph_vector_t *sd, - igraph_vector_t *norm, - igraph_vector_t *cites, - const igraph_vector_t *debug, - igraph_vector_ptr_t *debugres, - igraph_real_t *logmax, - const igraph_vector_t *st, - igraph_integer_t pagebins); -int igraph_revolver_st_l(const igraph_t *graph, - igraph_vector_t *st, - const igraph_vector_t *kernel); -int igraph_revolver_exp_l(const igraph_t *graph, - igraph_vector_t *expected, - const igraph_vector_t *kernel, - const igraph_vector_t *st, - igraph_integer_t pagebins); -int igraph_revolver_error_l(const igraph_t *graph, - const igraph_vector_t *kernel, - const igraph_vector_t *st, - igraph_integer_t pagebins, - igraph_real_t *logprob, - igraph_real_t *lognull); -int igraph_revolver_error2_l(const igraph_t *graph, - const igraph_vector_t *kernel, - igraph_real_t *logprob, - igraph_real_t *lognull); - -int igraph_revolver_dl(const igraph_t *graph, - igraph_integer_t niter, - igraph_integer_t agebins, - igraph_matrix_t *kernel, - igraph_matrix_t *sd, - igraph_matrix_t *norm, - igraph_matrix_t *cites, - igraph_matrix_t *expected, - igraph_real_t *logprob, - igraph_real_t *lognull, - igraph_real_t *logmax, - const igraph_matrix_t *debug, - igraph_vector_ptr_t *debugres); -int igraph_revolver_mes_dl(const igraph_t *graph, - igraph_matrix_t *kernel, - igraph_matrix_t *sd, - igraph_matrix_t *norm, - igraph_matrix_t *cites, - const igraph_matrix_t *debug, - igraph_vector_ptr_t *debugres, - igraph_real_t *logmax, - const igraph_vector_t *st, - igraph_integer_t pmaxind, - igraph_integer_t pagebins); -int igraph_revolver_st_dl(const igraph_t *graph, - igraph_vector_t *st, - const igraph_matrix_t *kernel); -int igraph_revolver_exp_dl(const igraph_t *graph, - igraph_matrix_t *expected, - const igraph_matrix_t *kernel, - const igraph_vector_t *st, - igraph_integer_t pmaxind, - igraph_integer_t pagebins); -int igraph_revolver_error_dl(const igraph_t *graph, - const igraph_matrix_t *kernel, - const igraph_vector_t *st, - igraph_integer_t pagebins, - igraph_integer_t pmaxind, - igraph_real_t *logprob, - igraph_real_t *lognull); -int igraph_revolver_error2_dl(const igraph_t *graph, - const igraph_matrix_t *kernel, - igraph_real_t *logprob, - igraph_real_t *lognull); - -int igraph_revolver_el(const igraph_t *graph, - igraph_integer_t niter, - const igraph_vector_t *cats, - igraph_integer_t agebins, - igraph_matrix_t *kernel, - igraph_matrix_t *sd, - igraph_matrix_t *norm, - igraph_matrix_t *cites, - igraph_matrix_t *expected, - igraph_real_t *logprob, - igraph_real_t *lognull, - igraph_real_t *logmax, - const igraph_matrix_t *debug, - igraph_vector_ptr_t *debugres); -int igraph_revolver_mes_el(const igraph_t *graph, - igraph_matrix_t *kernel, - igraph_matrix_t *sd, - igraph_matrix_t *norm, - igraph_matrix_t *cites, - const igraph_matrix_t *debug, - igraph_vector_ptr_t *debugres, - igraph_real_t *logmax, - const igraph_vector_t *st, - const igraph_vector_t *cats, - igraph_integer_t pnocats, - igraph_integer_t pagebins); -int igraph_revolver_st_el(const igraph_t *graph, - igraph_vector_t *st, - const igraph_matrix_t *kernel, - const igraph_vector_t *cats); -int igraph_revolver_exp_el(const igraph_t *graph, - igraph_matrix_t *expected, - const igraph_matrix_t *kernel, - const igraph_vector_t *st, - const igraph_vector_t *cats, - igraph_integer_t pnocats, - igraph_integer_t pagebins); -int igraph_revolver_error_el(const igraph_t *graph, - const igraph_matrix_t *kernel, - const igraph_vector_t *st, - const igraph_vector_t *cats, - igraph_integer_t pnocats, - igraph_integer_t pagebins, - igraph_real_t *logprob, - igraph_real_t *lognull); -int igraph_revolver_error2_el(const igraph_t *graph, - const igraph_matrix_t *kernel, - const igraph_vector_t *cats, - igraph_real_t *logprob, - igraph_real_t *lognull); - -int igraph_revolver_r(const igraph_t *graph, - igraph_integer_t niter, - igraph_integer_t window, - igraph_vector_t *kernel, - igraph_vector_t *sd, - igraph_vector_t *norm, - igraph_vector_t *cites, - igraph_vector_t *expected, - igraph_real_t *logprob, - igraph_real_t *lognull, - igraph_real_t *logmax, - const igraph_vector_t *debug, - igraph_vector_ptr_t *debugres); -int igraph_revolver_mes_r(const igraph_t *graph, - igraph_vector_t *kernel, - igraph_vector_t *sd, - igraph_vector_t *norm, - igraph_vector_t *cites, - const igraph_vector_t *debug, - igraph_vector_ptr_t *debugres, - igraph_real_t *logmax, - const igraph_vector_t *st, - igraph_integer_t window, - igraph_integer_t maxind); -int igraph_revolver_st_r(const igraph_t *graph, - igraph_vector_t *st, - const igraph_vector_t *kernel, - igraph_integer_t window); -int igraph_revolver_exp_r(const igraph_t *graph, - igraph_vector_t *expected, - const igraph_vector_t *kernel, - const igraph_vector_t *st, - igraph_integer_t window, - igraph_integer_t pmaxind); -int igraph_revolver_error_r(const igraph_t *graph, - const igraph_vector_t *kernel, - const igraph_vector_t *st, - igraph_integer_t window, - igraph_integer_t maxind, - igraph_real_t *logprob, - igraph_real_t *lognull); -int igraph_revolver_error2_r(const igraph_t *graph, - const igraph_vector_t *kernel, - igraph_integer_t window, - igraph_real_t *logprob, - igraph_real_t *lognull); - -int igraph_revolver_ar(const igraph_t *graph, - igraph_integer_t niter, - igraph_integer_t agebins, - igraph_integer_t window, - igraph_matrix_t *kernel, - igraph_matrix_t *sd, - igraph_matrix_t *norm, - igraph_matrix_t *cites, - igraph_matrix_t *expected, - igraph_real_t *logprob, - igraph_real_t *lognull, - igraph_real_t *logmax, - const igraph_matrix_t *debug, - igraph_vector_ptr_t *debugres); -int igraph_revolver_mes_ar(const igraph_t *graph, - igraph_matrix_t *kernel, - igraph_matrix_t *sd, - igraph_matrix_t *norm, - igraph_matrix_t *cites, - const igraph_matrix_t *debug, - igraph_vector_ptr_t *debugres, - igraph_real_t *logmax, - const igraph_vector_t *st, - igraph_integer_t pagebins, - igraph_integer_t pwindow, - igraph_integer_t maxind); -int igraph_revolver_st_ar(const igraph_t *graph, - igraph_vector_t *st, - const igraph_matrix_t *kernel, - igraph_integer_t pwindow); -int igraph_revolver_exp_ar(const igraph_t *graph, - igraph_matrix_t *expected, - const igraph_matrix_t *kernel, - const igraph_vector_t *st, - igraph_integer_t agebins, - igraph_integer_t window, - igraph_integer_t pmaxind); -int igraph_revolver_error_ar(const igraph_t *graph, - const igraph_matrix_t *kernel, - const igraph_vector_t *st, - igraph_integer_t pagebins, - igraph_integer_t pwindow, - igraph_integer_t maxind, - igraph_real_t *logprob, - igraph_real_t *lognull); -int igraph_revolver_error2_ar(const igraph_t *graph, - const igraph_matrix_t *kernel, - igraph_integer_t window, - igraph_real_t *logprob, - igraph_real_t *lognull); - -int igraph_revolver_di(const igraph_t *graph, - igraph_integer_t niter, - const igraph_vector_t *cats, - igraph_matrix_t *kernel, - igraph_matrix_t *sd, - igraph_matrix_t *norm, - igraph_matrix_t *cites, - igraph_matrix_t *expected, - igraph_real_t *logprob, - igraph_real_t *lognull, - igraph_real_t *logmax, - const igraph_matrix_t *debug, - igraph_vector_ptr_t *debugres); -int igraph_revolver_mes_di(const igraph_t *graph, - igraph_matrix_t *kernel, - igraph_matrix_t *sd, - igraph_matrix_t *norm, - igraph_matrix_t *cites, - const igraph_matrix_t *debug, - igraph_vector_ptr_t *debugres, - igraph_real_t *logmax, - const igraph_vector_t *st, - const igraph_vector_t *cats, - igraph_integer_t pnocats, - igraph_integer_t pmaxind); -int igraph_revolver_st_di(const igraph_t *graph, - igraph_vector_t *st, - const igraph_matrix_t *kernel, - const igraph_vector_t *cats); -int igraph_revolver_exp_di(const igraph_t *graph, - igraph_matrix_t *expected, - const igraph_matrix_t *kernel, - const igraph_vector_t *st, - const igraph_vector_t *cats, - igraph_integer_t pnocats, - igraph_integer_t pmaxind); -int igraph_revolver_error_di(const igraph_t *graph, - const igraph_matrix_t *kernel, - const igraph_vector_t *st, - const igraph_vector_t *cats, - igraph_integer_t pnocats, - igraph_integer_t pmaxind, - igraph_real_t *logprob, - igraph_real_t *lognull); -int igraph_revolver_error2_di(const igraph_t *graph, - const igraph_matrix_t *kernel, - const igraph_vector_t *cats, - igraph_real_t *logprob, - igraph_real_t *lognull); - -int igraph_revolver_adi(const igraph_t *graph, - igraph_integer_t niter, - igraph_integer_t agebins, - const igraph_vector_t *cats, - igraph_array3_t *kernel, - igraph_array3_t *sd, - igraph_array3_t *norm, - igraph_array3_t *cites, - igraph_array3_t *expected, - igraph_real_t *logprob, - igraph_real_t *lognull, - igraph_real_t *logmax, - const igraph_matrix_t *debug, - igraph_vector_ptr_t *debugres); -int igraph_revolver_mes_adi(const igraph_t *graph, - igraph_array3_t *kernel, - igraph_array3_t *sd, - igraph_array3_t *norm, - igraph_array3_t *cites, - const igraph_matrix_t *debug, - igraph_vector_ptr_t *debugres, - igraph_real_t *logmax, - const igraph_vector_t *st, - const igraph_vector_t *cats, - igraph_integer_t pnocats, - igraph_integer_t pmaxind, - igraph_integer_t pagebins); -int igraph_revolver_st_adi(const igraph_t *graph, - igraph_vector_t *st, - const igraph_array3_t *kernel, - const igraph_vector_t *cats); -int igraph_revolver_exp_adi(const igraph_t *graph, - igraph_array3_t *expected, - const igraph_array3_t *kernel, - const igraph_vector_t *st, - const igraph_vector_t *cats, - igraph_integer_t pnocats, - igraph_integer_t pmaxind, - igraph_integer_t pagebins); -int igraph_revolver_error_adi(const igraph_t *graph, - const igraph_array3_t *kernel, - const igraph_vector_t *st, - const igraph_vector_t *cats, - igraph_integer_t pnocats, - igraph_integer_t pmaxind, - igraph_integer_t pagebins, - igraph_real_t *logprob, - igraph_real_t *lognull); -int igraph_revolver_error2_adi(const igraph_t *graph, - const igraph_array3_t *kernel, - const igraph_vector_t *cats, - igraph_real_t *logprob, - igraph_real_t *lognull); - -int igraph_revolver_il(const igraph_t *graph, - igraph_integer_t niter, - igraph_integer_t agebins, - const igraph_vector_t *cats, - igraph_matrix_t *kernel, - igraph_matrix_t *sd, - igraph_matrix_t *norm, - igraph_matrix_t *cites, - igraph_matrix_t *expected, - igraph_real_t *logprob, - igraph_real_t *lognull, - igraph_real_t *logmax, - const igraph_matrix_t *debug, - igraph_vector_ptr_t *debugres); -int igraph_revolver_mes_il(const igraph_t *graph, - igraph_matrix_t *kernel, - igraph_matrix_t *sd, - igraph_matrix_t *norm, - igraph_matrix_t *cites, - const igraph_matrix_t *debug, - igraph_vector_ptr_t *debugres, - igraph_real_t *logmax, - const igraph_vector_t *st, - const igraph_vector_t *cats, - igraph_integer_t pnocats, - igraph_integer_t pagebins); -int igraph_revolver_st_il(const igraph_t *graph, - igraph_vector_t *st, - const igraph_matrix_t *kernel, - const igraph_vector_t *cats); -int igraph_revolver_exp_il(const igraph_t *graph, - igraph_matrix_t *expected, - const igraph_matrix_t *kernel, - const igraph_vector_t *st, - const igraph_vector_t *cats, - igraph_integer_t nocats, - igraph_integer_t pagebins); -int igraph_revolver_error_il(const igraph_t *graph, - const igraph_matrix_t *kernel, - const igraph_vector_t *st, - const igraph_vector_t *cats, - igraph_integer_t nocats, - igraph_integer_t pagebins, - igraph_real_t *logprob, - igraph_real_t *lognull); -int igraph_revolver_error2_il(const igraph_t *graph, - const igraph_matrix_t *kernel, - const igraph_vector_t *cats, - igraph_real_t *logprob, - igraph_real_t *lognull); - -int igraph_revolver_ir(const igraph_t *graph, - igraph_integer_t niter, - igraph_integer_t window, - const igraph_vector_t *cats, - igraph_matrix_t *kernel, - igraph_matrix_t *sd, - igraph_matrix_t *norm, - igraph_matrix_t *cites, - igraph_matrix_t *expected, - igraph_real_t *logprob, - igraph_real_t *lognull, - igraph_real_t *logmax, - const igraph_matrix_t *debug, - igraph_vector_ptr_t *debugres); -int igraph_revolver_mes_ir(const igraph_t *graph, - igraph_matrix_t *kernel, - igraph_matrix_t *sd, - igraph_matrix_t *norm, - igraph_matrix_t *cites, - const igraph_matrix_t *debug, - igraph_vector_ptr_t *debugres, - igraph_real_t *logmax, - const igraph_vector_t *st, - igraph_integer_t pwindow, - const igraph_vector_t *cats, - igraph_integer_t pnocats, - igraph_integer_t pmaxind); -int igraph_revolver_st_ir(const igraph_t *graph, - igraph_vector_t *st, - const igraph_matrix_t *kernel, - igraph_integer_t pwindow, - const igraph_vector_t *cats); -int igraph_revolver_exp_ir(const igraph_t *graph, - igraph_matrix_t *expected, - const igraph_matrix_t *kernel, - const igraph_vector_t *st, - igraph_integer_t pwindow, - const igraph_vector_t *cats, - igraph_integer_t pnocats, - igraph_integer_t pmaxind); -int igraph_revolver_error_ir(const igraph_t *graph, - const igraph_matrix_t *kernel, - const igraph_vector_t *st, - igraph_integer_t pwindow, - const igraph_vector_t *cats, - igraph_integer_t pnocats, - igraph_integer_t pmaxind, - igraph_real_t *logprob, - igraph_real_t *lognull); -int igraph_revolver_error2_ir(const igraph_t *graph, - const igraph_matrix_t *kernel, - const igraph_vector_t *cats, - igraph_integer_t window, - igraph_real_t *logprob, - igraph_real_t *lognull); - -int igraph_revolver_air(const igraph_t *graph, - igraph_integer_t niter, - igraph_integer_t window, - igraph_integer_t agebins, - const igraph_vector_t *cats, - igraph_array3_t *kernel, - igraph_array3_t *sd, - igraph_array3_t *norm, - igraph_array3_t *cites, - igraph_array3_t *expected, - igraph_real_t *logprob, - igraph_real_t *lognull, - igraph_real_t *logmax, - const igraph_matrix_t *debug, - igraph_vector_ptr_t *debugres); -int igraph_revolver_mes_air(const igraph_t *graph, - igraph_array3_t *kernel, - igraph_array3_t *sd, - igraph_array3_t *norm, - igraph_array3_t *cites, - const igraph_matrix_t *debug, - igraph_vector_ptr_t *debugres, - igraph_real_t *logmax, - const igraph_vector_t *st, - igraph_integer_t pwindow, - const igraph_vector_t *cats, - igraph_integer_t pnocats, - igraph_integer_t pmaxind, - igraph_integer_t pagebins); -int igraph_revolver_st_air(const igraph_t *graph, - igraph_vector_t *st, - const igraph_array3_t *kernel, - igraph_integer_t pwindow, - const igraph_vector_t *cats); -int igraph_revolver_exp_air(const igraph_t *graph, - igraph_array3_t *expected, - const igraph_array3_t *kernel, - const igraph_vector_t *st, - igraph_integer_t pwindow, - const igraph_vector_t *cats, - igraph_integer_t pnocats, - igraph_integer_t pmaxind, - igraph_integer_t pagebins); -int igraph_revolver_error_air(const igraph_t *graph, - const igraph_array3_t *kernel, - const igraph_vector_t *st, - igraph_integer_t pwindow, - const igraph_vector_t *cats, - igraph_integer_t pnocats, - igraph_integer_t pmaxind, - igraph_integer_t pagebins, - igraph_real_t *logprob, - igraph_real_t *lognull); -int igraph_revolver_error2_air(const igraph_t *graph, - const igraph_array3_t *kernel, - const igraph_vector_t *cats, - igraph_integer_t window, - igraph_real_t *logprob, - igraph_real_t *lognull); - -/* Non-citation networks */ - -int igraph_revolver_d_d(const igraph_t *graph, - igraph_integer_t niter, - const igraph_vector_t *vtime, - const igraph_vector_t *etime, - igraph_matrix_t *kernel, - igraph_matrix_t *sd, - igraph_matrix_t *norm, - igraph_matrix_t *cites, - igraph_matrix_t *expected, - igraph_real_t *logprob, - igraph_real_t *lognull, - const igraph_matrix_t *debug, - igraph_vector_ptr_t *debugres); -int igraph_revolver_mes_d_d(const igraph_t *graph, - igraph_lazy_inclist_t *inclist, - igraph_matrix_t *kernel, - igraph_matrix_t *sd, - igraph_matrix_t *norm, - igraph_matrix_t *cites, - const igraph_matrix_t *debug, - igraph_vector_ptr_t *debugres, - const igraph_vector_t *st, - const igraph_vector_t *vtime, - const igraph_vector_t *vtimeidx, - const igraph_vector_t *etime, - const igraph_vector_t *etimeidx, - igraph_integer_t pno_of_events, - igraph_integer_t pmaxdegree); -int igraph_revolver_st_d_d(const igraph_t *graph, - igraph_lazy_inclist_t *inclist, - igraph_vector_t *st, - const igraph_matrix_t *kernel, - const igraph_vector_t *vtime, - const igraph_vector_t *vtimeidx, - const igraph_vector_t *etime, - const igraph_vector_t *etimeidx, - igraph_integer_t pno_of_events); -int igraph_revolver_exp_d_d(const igraph_t *graph, - igraph_lazy_inclist_t *inclist, - igraph_matrix_t *expected, - const igraph_matrix_t *kernel, - const igraph_vector_t *st, - const igraph_vector_t *vtime, - const igraph_vector_t *vtimeidx, - const igraph_vector_t *etime, - const igraph_vector_t *etimeidx, - igraph_integer_t pno_of_events, - igraph_integer_t pmaxdegree); -int igraph_revolver_error_d_d(const igraph_t *graph, - igraph_lazy_inclist_t *inclist, - const igraph_matrix_t *kernel, - const igraph_vector_t *st, - const igraph_vector_t *vtime, - const igraph_vector_t *vtimeidx, - const igraph_vector_t *etime, - const igraph_vector_t *etimeidx, - igraph_integer_t pno_of_events, - igraph_integer_t pmaxdegree, - igraph_real_t *logprob, - igraph_real_t *lognull); - -int igraph_revolver_p_p(const igraph_t *graph, - igraph_integer_t niter, - const igraph_vector_t *vtime, - const igraph_vector_t *etime, - const igraph_vector_t *authors, - const igraph_vector_t *eventsizes, - igraph_matrix_t *kernel, - igraph_matrix_t *sd, - igraph_matrix_t *norm, - igraph_matrix_t *cites, - igraph_matrix_t *expected, - igraph_real_t *logprob, - igraph_real_t *lognull, - const igraph_matrix_t *debug, - igraph_vector_ptr_t *debugres); -int igraph_revolver_mes_p_p(const igraph_t *graph, - igraph_lazy_inclist_t *inclist, - igraph_matrix_t *kernel, - igraph_matrix_t *sd, - igraph_matrix_t *norm, - igraph_matrix_t *cites, - const igraph_matrix_t *debug, - igraph_vector_ptr_t *debugres, - const igraph_vector_t *st, - const igraph_vector_t *vtime, - const igraph_vector_t *vtimeidx, - const igraph_vector_t *etime, - const igraph_vector_t *etimeidx, - igraph_integer_t pno_of_events, - const igraph_vector_t *authors, - const igraph_vector_t *eventsizes, - igraph_integer_t pmaxpapers); -int igraph_revolver_st_p_p(const igraph_t *graph, - igraph_lazy_inclist_t *inclist, - igraph_vector_t *st, - const igraph_matrix_t *kernel, - const igraph_vector_t *vtime, - const igraph_vector_t *vtimeidx, - const igraph_vector_t *etime, - const igraph_vector_t *etimeidx, - igraph_integer_t pno_of_events, - const igraph_vector_t *authors, - const igraph_vector_t *eventsizes, - igraph_integer_t pmaxpapers); -int igraph_revolver_exp_p_p(const igraph_t *graph, - igraph_lazy_inclist_t *inclist, - igraph_matrix_t *expected, - const igraph_matrix_t *kernel, - const igraph_vector_t *st, - const igraph_vector_t *vtime, - const igraph_vector_t *vtimeidx, - const igraph_vector_t *etime, - const igraph_vector_t *etimeidx, - igraph_integer_t pno_of_events, - const igraph_vector_t *authors, - const igraph_vector_t *eventsizes, - igraph_integer_t pmaxpapers); -int igraph_revolver_error_p_p(const igraph_t *graph, - igraph_lazy_inclist_t *inclist, - const igraph_matrix_t *kernel, - const igraph_vector_t *st, - const igraph_vector_t *vtime, - const igraph_vector_t *vtimeidx, - const igraph_vector_t *etime, - const igraph_vector_t *etimeidx, - igraph_integer_t pno_of_events, - const igraph_vector_t *authors, - const igraph_vector_t *eventsizes, - igraph_integer_t pmaxpapers, - igraph_real_t *logprob, - igraph_real_t *lognull); - -/* -------------------------------------------------- */ -/* Maximum likelihood revolver */ -/* -------------------------------------------------- */ - -int igraph_revolver_ml_d(const igraph_t *graph, - igraph_integer_t niter, - igraph_vector_t *kernel, - igraph_vector_t *cites, - igraph_real_t delta, - const igraph_vector_t *filter, - igraph_real_t *logprob, - igraph_real_t *logmax); - -int igraph_revolver_probs_d(const igraph_t *graph, - const igraph_vector_t *kernel, - igraph_vector_t *logprobs, - igraph_vector_t *logcited, - igraph_vector_t *logciting, - igraph_bool_t ntk); - -int igraph_revolver_ml_de(const igraph_t *graph, - igraph_integer_t niter, - igraph_matrix_t *kernel, - const igraph_vector_t *cats, - igraph_matrix_t *cites, - igraph_real_t delta, - const igraph_vector_t *filter, - igraph_real_t *logprob, - igraph_real_t *logmax); - -int igraph_revolver_probs_de(const igraph_t *graph, - const igraph_matrix_t *kernel, - const igraph_vector_t *cats, - igraph_vector_t *logprobs, - igraph_vector_t *logcited, - igraph_vector_t *logciting); - -int igraph_revolver_ml_ade(const igraph_t *graph, - igraph_integer_t niter, - igraph_array3_t *kernel, - const igraph_vector_t *cats, - igraph_array3_t *cites, - igraph_integer_t pagebins, - igraph_real_t delta, - const igraph_vector_t *filter, - igraph_real_t *logprob, - igraph_real_t *logmax); - -int igraph_revolver_probs_ade(const igraph_t *graph, - const igraph_array3_t *kernel, - const igraph_vector_t *cats, - igraph_vector_t *logprobs, - igraph_vector_t *logcited, - igraph_vector_t *logciting); - -int igraph_revolver_ml_f(const igraph_t *graph, - igraph_integer_t niter, - igraph_vector_t *kernel, - igraph_vector_t *cites, - igraph_real_t delta, - igraph_real_t *logprob, - igraph_real_t *logmax); - -int igraph_revolver_ml_df(const igraph_t *graph, - igraph_integer_t niter, - igraph_matrix_t *kernel, - igraph_matrix_t *cites, - igraph_real_t delta, - igraph_real_t *logprob, - igraph_real_t *logmax); - -int igraph_revolver_ml_l(const igraph_t *graph, - igraph_integer_t niter, - igraph_vector_t *kernel, - igraph_vector_t *cites, - igraph_integer_t pagebins, - igraph_real_t delta, - igraph_real_t *logprob, - igraph_real_t *logmax); - -int igraph_revolver_ml_ad(const igraph_t *graph, - igraph_integer_t niter, - igraph_matrix_t *kernel, - igraph_matrix_t *cites, - igraph_integer_t pagebins, - igraph_real_t delta, - const igraph_vector_t *filter, - igraph_real_t *logprob, - igraph_real_t *logmax); - -int igraph_revolver_probs_ad(const igraph_t *graph, - const igraph_matrix_t *kernel, - igraph_vector_t *logprobs, - igraph_vector_t *logcited, - igraph_vector_t *logciting, - igraph_bool_t ntk); - -int igraph_revolver_ml_D(const igraph_t *graph, - igraph_vector_t *res, - igraph_real_t *Fmin, - igraph_real_t abstol, igraph_real_t reltol, int maxit, - igraph_scalar_function_t *A_fun, - igraph_vector_function_t *dA_fun, - const igraph_vector_t *filter, - igraph_integer_t *fncount, igraph_integer_t *grcount); - -int igraph_revolver_ml_D_alpha(const igraph_t *graph, - igraph_real_t *alpha, igraph_real_t *Fmin, - igraph_real_t abstol, igraph_real_t reltol, - int maxit, const igraph_vector_t *filter, - igraph_integer_t *fncount, - igraph_integer_t *grcount); - -int igraph_revolver_ml_D_alpha_a(const igraph_t *graph, - igraph_real_t *alpha, igraph_real_t *a, - igraph_real_t *Fmin, - igraph_real_t abstol, igraph_real_t reltol, - int maxit, const igraph_vector_t *filter, - igraph_integer_t *fncount, - igraph_integer_t *grcount); - -int igraph_revolver_ml_DE(const igraph_t *graph, - const igraph_vector_t *cats, - igraph_vector_t *res, - igraph_real_t *Fmin, - igraph_real_t abstol, igraph_real_t reltol, int maxit, - igraph_scalar_function_t *A_fun, - igraph_vector_function_t *dA_fun, - const igraph_vector_t *filter, - igraph_integer_t *fncount, - igraph_integer_t *grcount, - igraph_vector_t *lastderiv); - -int igraph_revolver_ml_DE_alpha_a(const igraph_t *graph, - const igraph_vector_t *cats, - igraph_real_t *alpha, igraph_real_t *a, - igraph_vector_t *coeffs, - igraph_real_t *Fmin, - igraph_real_t abstol, igraph_real_t reltol, - int maxit, const igraph_vector_t *filter, - igraph_integer_t *fncount, - igraph_integer_t *grcount); - -int igraph_revolver_ml_AD(const igraph_t *graph, - igraph_vector_t *res, - igraph_real_t *Fmin, - igraph_real_t abstol, igraph_real_t reltol, int maxit, - igraph_scalar_function_t *A_fun, - igraph_vector_function_t *dA_fun, - int agebins, const igraph_vector_t *filter, - igraph_integer_t *fncount, - igraph_integer_t *grcount, - igraph_vector_t *lastderiv); - -int igraph_revolver_ml_AD_alpha_a_beta(const igraph_t *graph, - igraph_real_t *alpha, igraph_real_t *a, - igraph_real_t *beta, igraph_real_t *Fmin, - igraph_real_t abstol, igraph_real_t reltol, - int maxit, int agebins, - const igraph_vector_t *filter, - igraph_integer_t *fncount, - igraph_integer_t *grcount); - -int igraph_revolver_ml_AD_dpareto(const igraph_t *graph, - igraph_real_t *alpha, igraph_real_t *a, - igraph_real_t *paralpha, igraph_real_t *parbeta, - igraph_real_t *parscale, - igraph_real_t *Fmin, - igraph_real_t abstol, igraph_real_t reltol, - int maxit, int agebins, - const igraph_vector_t *filter, - igraph_integer_t *fncount, - igraph_integer_t *grcount); - -int igraph_revolver_ml_AD_dpareto_eval(const igraph_t *graph, - igraph_real_t alpha, igraph_real_t a, - igraph_real_t paralpha, - igraph_real_t parbeta, - igraph_real_t parscale, - igraph_real_t *value, - igraph_vector_t *deriv, - int agebins, - const igraph_vector_t *filter); - - - -int igraph_revolver_ml_ADE(const igraph_t *graph, - const igraph_vector_t *cats, - igraph_vector_t *res, - igraph_real_t *Fmin, - igraph_real_t abstol, igraph_real_t reltol, int maxit, - igraph_scalar_function_t *A_fun, - igraph_vector_function_t *dA_fun, - int agebins, const igraph_vector_t *filter, - igraph_integer_t *fncount, - igraph_integer_t *grcount, - igraph_vector_t *lastderiv); - -int igraph_revolver_probs_ADE(const igraph_t *graph, - igraph_scalar_function_t *A_fun, - const igraph_matrix_t *par, - const igraph_vector_t *cats, - const igraph_vector_t *gcats, - int agebins, - igraph_vector_t *logprobs, - igraph_vector_t *logcited, - igraph_vector_t *logciting); - -int igraph_revolver_ml_ADE_alpha_a_beta(const igraph_t *graph, - const igraph_vector_t *cats, - igraph_real_t *alpha, igraph_real_t *a, - igraph_real_t *beta, igraph_vector_t *coeffs, - igraph_real_t *Fmin, - igraph_real_t abstol, igraph_real_t reltol, - int maxit, int agebins, - const igraph_vector_t *filter, - igraph_integer_t *fncount, - igraph_integer_t *grcount); - -int igraph_revolver_ml_ADE_dpareto(const igraph_t *graph, - const igraph_vector_t *cats, - igraph_real_t *alpha, igraph_real_t *a, - igraph_real_t *paralpha, igraph_real_t *parbeta, - igraph_real_t *parscale, igraph_vector_t *coeffs, - igraph_real_t *Fmin, - igraph_real_t abstol, igraph_real_t reltol, - int maxit, int agebins, - const igraph_vector_t *filter, - igraph_integer_t *fncount, - igraph_integer_t *grcount); - -int igraph_revolver_ml_ADE_dpareto_eval(const igraph_t *graph, - const igraph_vector_t *cats, - igraph_real_t alpha, igraph_real_t a, - igraph_real_t paralpha, - igraph_real_t parbeta, - igraph_real_t parscale, - const igraph_vector_t *coeffs, - igraph_real_t *value, - igraph_vector_t *deriv, - int agebins, - const igraph_vector_t *filter); - -int igraph_revolver_ml_ADE_dpareto_evalf(const igraph_t *graph, - const igraph_vector_t *cats, - const igraph_matrix_t *par, - igraph_vector_t *value, - int agebins, - const igraph_vector_t *filter); - -int igraph_revolver_probs_ADE_dpareto(const igraph_t *graph, - const igraph_matrix_t *par, - const igraph_vector_t *cats, - const igraph_vector_t *gcats, - int agebins, - igraph_vector_t *logprobs, - igraph_vector_t *logcited, - igraph_vector_t *logciting); - -__END_DECLS - -#endif diff --git a/src/leidenalg/igraph-R/igraph_scg.h b/src/leidenalg/igraph-R/igraph_scg.h deleted file mode 100644 index 20e1a4a..0000000 --- a/src/leidenalg/igraph-R/igraph_scg.h +++ /dev/null @@ -1,135 +0,0 @@ -/* -*- mode: C -*- */ -/* - IGraph library. - Copyright (C) 2011-2012 Gabor Csardi - 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" - -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); - -#endif diff --git a/src/leidenalg/igraph-R/igraph_sparsemat.h b/src/leidenalg/igraph-R/igraph_sparsemat.h deleted file mode 100644 index add7356..0000000 --- a/src/leidenalg/igraph-R/igraph_sparsemat.h +++ /dev/null @@ -1,264 +0,0 @@ -/* -*- mode: C -*- */ -/* - IGraph library. - Copyright (C) 2009-2012 Gabor Csardi - 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 - -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_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); - -#endif diff --git a/src/leidenalg/igraph-R/igraph_spmatrix.h b/src/leidenalg/igraph-R/igraph_spmatrix.h deleted file mode 100644 index 54c1c86..0000000 --- a/src/leidenalg/igraph-R/igraph_spmatrix.h +++ /dev/null @@ -1,123 +0,0 @@ -/* -*- mode: C -*- */ -/* - IGraph library. - Copyright (C) 2009-2012 Gabor Csardi - 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 - -#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_vector.h" - -__BEGIN_DECLS - -/* -------------------------------------------------- */ -/* Sparse matrix */ -/* -------------------------------------------------- */ - -/** - * \section about_igraph_spmatrix_t_objects About \type igraph_spmatrix_t objects - * - * 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. - * - * 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. - */ -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) - -int igraph_spmatrix_init(igraph_spmatrix_t *m, long int nrow, long int ncol); -void igraph_spmatrix_destroy(igraph_spmatrix_t *m); -int igraph_spmatrix_resize(igraph_spmatrix_t *m, long int nrow, long int ncol); -igraph_real_t igraph_spmatrix_e(const igraph_spmatrix_t *m, long int row, long int col); -int igraph_spmatrix_set(igraph_spmatrix_t *m, long int row, long int col, - igraph_real_t value); -int igraph_spmatrix_add_e(igraph_spmatrix_t *m, long int row, long int col, - igraph_real_t value); -int igraph_spmatrix_add_col_values(igraph_spmatrix_t *m, long int to, long int from); -long int igraph_spmatrix_count_nonzero(const igraph_spmatrix_t *m); -long int igraph_spmatrix_size(const igraph_spmatrix_t *m); -long int igraph_spmatrix_nrow(const igraph_spmatrix_t *m); -long int igraph_spmatrix_ncol(const igraph_spmatrix_t *m); -int igraph_spmatrix_copy_to(const igraph_spmatrix_t *m, igraph_real_t *to); -int igraph_spmatrix_null(igraph_spmatrix_t *m); -int igraph_spmatrix_add_cols(igraph_spmatrix_t *m, long int n); -int igraph_spmatrix_add_rows(igraph_spmatrix_t *m, long int n); -int igraph_spmatrix_clear_col(igraph_spmatrix_t *m, long int col); -int igraph_spmatrix_clear_row(igraph_spmatrix_t *m, long int row); -int igraph_spmatrix_copy(igraph_spmatrix_t *to, const igraph_spmatrix_t *from); -igraph_real_t igraph_spmatrix_max_nonzero(const igraph_spmatrix_t *m, - igraph_real_t *ridx, igraph_real_t *cidx); -igraph_real_t igraph_spmatrix_max(const igraph_spmatrix_t *m, - igraph_real_t *ridx, igraph_real_t *cidx); -void igraph_spmatrix_scale(igraph_spmatrix_t *m, igraph_real_t by); -int igraph_spmatrix_colsums(const igraph_spmatrix_t *m, igraph_vector_t *res); -int igraph_spmatrix_rowsums(const igraph_spmatrix_t *m, igraph_vector_t *res); - -int igraph_spmatrix_print(const igraph_spmatrix_t *matrix); -int igraph_spmatrix_fprint(const igraph_spmatrix_t *matrix, FILE* file); - -int igraph_i_spmatrix_get_col_nonzero_indices(const igraph_spmatrix_t *m, - igraph_vector_t *res, long int col); -int igraph_i_spmatrix_clear_row_fast(igraph_spmatrix_t *m, long int row); -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; - -int igraph_spmatrix_iter_create(igraph_spmatrix_iter_t *mit, const igraph_spmatrix_t *m); -int igraph_spmatrix_iter_reset(igraph_spmatrix_iter_t *mit); -int igraph_spmatrix_iter_next(igraph_spmatrix_iter_t *mit); -igraph_bool_t igraph_spmatrix_iter_end(igraph_spmatrix_iter_t *mit); -void igraph_spmatrix_iter_destroy(igraph_spmatrix_iter_t *mit); - -__END_DECLS - -#endif diff --git a/src/leidenalg/igraph-R/igraph_structural.h b/src/leidenalg/igraph-R/igraph_structural.h deleted file mode 100644 index 67253a7..0000000 --- a/src/leidenalg/igraph-R/igraph_structural.h +++ /dev/null @@ -1,157 +0,0 @@ -/* -*- mode: C -*- */ -/* - IGraph library. - Copyright (C) 2009-2012 Gabor Csardi - 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 - -#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_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 */ -/* -------------------------------------------------- */ - -int igraph_are_connected(const igraph_t *graph, igraph_integer_t v1, igraph_integer_t v2, igraph_bool_t *res); - -/* -------------------------------------------------- */ -/* Structural properties */ -/* -------------------------------------------------- */ - -int igraph_minimum_spanning_tree(const igraph_t *graph, igraph_vector_t *res, - const igraph_vector_t *weights); -int igraph_minimum_spanning_tree_unweighted(const igraph_t *graph, - igraph_t *mst); -int igraph_minimum_spanning_tree_prim(const igraph_t *graph, igraph_t *mst, - const igraph_vector_t *weights); - -int igraph_subcomponent(const igraph_t *graph, igraph_vector_t *res, igraph_real_t vid, - igraph_neimode_t mode); -int igraph_rewire(igraph_t *graph, igraph_integer_t n, igraph_rewiring_t mode); -int igraph_subgraph(const igraph_t *graph, igraph_t *res, - const igraph_vs_t vids); -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); -int igraph_induced_subgraph(const igraph_t *graph, igraph_t *res, - const igraph_vs_t vids, igraph_subgraph_implementation_t impl); -int igraph_subgraph_edges(const igraph_t *graph, igraph_t *res, - const igraph_es_t eids, igraph_bool_t delete_vertices); -int igraph_simplify(igraph_t *graph, igraph_bool_t multiple, - igraph_bool_t loops, - const igraph_attribute_combination_t *edge_comb); -int igraph_reciprocity(const igraph_t *graph, igraph_real_t *res, - igraph_bool_t ignore_loops, - igraph_reciprocity_t mode); - -int igraph_maxdegree(const igraph_t *graph, igraph_integer_t *res, - igraph_vs_t vids, igraph_neimode_t mode, - igraph_bool_t loops); -int igraph_density(const igraph_t *graph, igraph_real_t *res, - igraph_bool_t loops); - -int igraph_is_loop(const igraph_t *graph, igraph_vector_bool_t *res, - igraph_es_t es); -int igraph_is_simple(const igraph_t *graph, igraph_bool_t *res); -int igraph_has_multiple(const igraph_t *graph, igraph_bool_t *res); -int igraph_is_multiple(const igraph_t *graph, igraph_vector_bool_t *res, - igraph_es_t es); -int igraph_count_multiple(const igraph_t *graph, igraph_vector_t *res, igraph_es_t es); -int igraph_girth(const igraph_t *graph, igraph_integer_t *girth, - igraph_vector_t *circle); -int igraph_add_edge(igraph_t *graph, igraph_integer_t from, igraph_integer_t to); - -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); - -int igraph_is_mutual(igraph_t *graph, igraph_vector_bool_t *res, igraph_es_t es); - -int igraph_maximum_cardinality_search(const igraph_t *graph, - igraph_vector_t *alpha, - igraph_vector_t *alpham1); -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); -int igraph_avg_nearest_neighbor_degree(const igraph_t *graph, - igraph_vs_t vids, - igraph_vector_t *knn, - igraph_vector_t *knnk, - const igraph_vector_t *weights); -int igraph_contract_vertices(igraph_t *graph, - const igraph_vector_t *mapping, - const igraph_attribute_combination_t - *vertex_comb); - -int igraph_transitive_closure_dag(const igraph_t *graph, - igraph_t *closure); - -int igraph_feedback_arc_set(const igraph_t *graph, igraph_vector_t *result, - const igraph_vector_t *weights, igraph_fas_algorithm_t algo); - -int igraph_diversity(igraph_t *graph, const igraph_vector_t *weights, - igraph_vector_t *res, const igraph_vs_t vs); - -/* -------------------------------------------------- */ -/* Spectral Properties */ -/* -------------------------------------------------- */ - -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/src/leidenalg/igraph-R/igraph_strvector.h b/src/leidenalg/igraph-R/igraph_strvector.h deleted file mode 100644 index b67d3dd..0000000 --- a/src/leidenalg/igraph-R/igraph_strvector.h +++ /dev/null @@ -1,106 +0,0 @@ -/* -*- mode: C -*- */ -/* - IGraph library. - Copyright (C) 2009-2012 Gabor Csardi - 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 - -#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_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) - -int igraph_strvector_init(igraph_strvector_t *sv, long int len); -void igraph_strvector_destroy(igraph_strvector_t *sv); -long int igraph_strvector_size(const igraph_strvector_t *sv); -void igraph_strvector_get(const igraph_strvector_t *sv, - long int idx, char **value); -int igraph_strvector_set(igraph_strvector_t *sv, long int idx, - const char *value); -int igraph_strvector_set2(igraph_strvector_t *sv, long int idx, - const char *value, int len); -void igraph_strvector_clear(igraph_strvector_t *sv); -void igraph_strvector_remove_section(igraph_strvector_t *v, long int from, - long int to); -void igraph_strvector_remove(igraph_strvector_t *v, long int elem); -void igraph_strvector_move_interval(igraph_strvector_t *v, long int begin, - long int end, long int to); -int igraph_strvector_copy(igraph_strvector_t *to, - const igraph_strvector_t *from); -int igraph_strvector_append(igraph_strvector_t *to, - const igraph_strvector_t *from); -int igraph_strvector_resize(igraph_strvector_t* v, long int newsize); -int igraph_strvector_add(igraph_strvector_t *v, const char *value); -void igraph_strvector_permdelete(igraph_strvector_t *v, const igraph_vector_t *index, - long int nremove); -void igraph_strvector_remove_negidx(igraph_strvector_t *v, const igraph_vector_t *neg, - long int nremove); -int igraph_strvector_print(const igraph_strvector_t *v, FILE *file, - const char *sep); - -int igraph_strvector_index(const igraph_strvector_t *v, - igraph_strvector_t *newv, - const igraph_vector_t *idx); - - -__END_DECLS - -#endif diff --git a/src/leidenalg/igraph-R/igraph_topology.h b/src/leidenalg/igraph-R/igraph_topology.h deleted file mode 100644 index 4acab82..0000000 --- a/src/leidenalg/igraph-R/igraph_topology.h +++ /dev/null @@ -1,268 +0,0 @@ -/* -*- mode: C -*- */ -/* - IGraph library. - Copyright (C) 2009-2012 Gabor Csardi - 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 - -#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_constants.h" -#include "igraph_datatype.h" -#include "igraph_types.h" -#include "igraph_vector_ptr.h" - -__BEGIN_DECLS - -int igraph_topological_sorting(const igraph_t *graph, igraph_vector_t *res, - igraph_neimode_t mode); -int igraph_is_dag(const igraph_t *graph, igraph_bool_t *res); - -/* -------------------------------------------------- */ -/* Graph isomorphisms */ -/* -------------------------------------------------- */ - -/* Common functions */ -int igraph_permute_vertices(const igraph_t *graph, igraph_t *res, - const igraph_vector_t *permutation); - -/* Generic interface */ -int igraph_isomorphic(const igraph_t *graph1, const igraph_t *graph2, - igraph_bool_t *iso); -int igraph_subisomorphic(const igraph_t *graph1, const igraph_t *graph2, - igraph_bool_t *iso); - -/* 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. - * - * 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. - * - * 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); - -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); -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); -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); -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); - -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); -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); -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); -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 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 - * 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 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; - -int igraph_canonical_permutation(const igraph_t *graph, igraph_vector_t *labeling, - igraph_bliss_sh_t sh, igraph_bliss_info_t *info); -int igraph_isomorphic_bliss(const igraph_t *graph1, const igraph_t *graph2, - igraph_bool_t *iso, igraph_vector_t *map12, - igraph_vector_t *map21, - igraph_bliss_sh_t sh1, igraph_bliss_sh_t sh2, - igraph_bliss_info_t *info1, igraph_bliss_info_t *info2); - -int igraph_automorphisms(const igraph_t *graph, - igraph_bliss_sh_t sh, igraph_bliss_info_t *info); - -/* Functions for 3-4 graphs */ -int igraph_isomorphic_34(const igraph_t *graph1, const igraph_t *graph2, - igraph_bool_t *iso); -int igraph_isoclass(const igraph_t *graph, igraph_integer_t *isoclass); -int igraph_isoclass_subgraph(const igraph_t *graph, igraph_vector_t *vids, - igraph_integer_t *isoclass); -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/src/leidenalg/igraph-R/igraph_transitivity.h b/src/leidenalg/igraph-R/igraph_transitivity.h deleted file mode 100644 index a6ce9c3..0000000 --- a/src/leidenalg/igraph-R/igraph_transitivity.h +++ /dev/null @@ -1,69 +0,0 @@ -/* -*- mode: C -*- */ -/* - IGraph library. - Copyright (C) 2009-2012 Gabor Csardi - 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 - -#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 - -int igraph_transitivity_undirected(const igraph_t *graph, - igraph_real_t *res, - igraph_transitivity_mode_t mode); -int igraph_transitivity_local_undirected(const igraph_t *graph, - igraph_vector_t *res, - const igraph_vs_t vids, - igraph_transitivity_mode_t mode); -int igraph_transitivity_local_undirected1(const igraph_t *graph, - igraph_vector_t *res, - const igraph_vs_t vids, - igraph_transitivity_mode_t mode); -int igraph_transitivity_local_undirected2(const igraph_t *graph, - igraph_vector_t *res, - const igraph_vs_t vids, - igraph_transitivity_mode_t mode); -int igraph_transitivity_local_undirected4(const igraph_t *graph, - igraph_vector_t *res, - const igraph_vs_t vids, - igraph_transitivity_mode_t mode); -int igraph_transitivity_avglocal_undirected(const igraph_t *graph, - igraph_real_t *res, - igraph_transitivity_mode_t mode); -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/src/leidenalg/igraph-R/igraph_vector.h b/src/leidenalg/igraph-R/igraph_vector.h deleted file mode 100644 index ab47fbb..0000000 --- a/src/leidenalg/igraph-R/igraph_vector.h +++ /dev/null @@ -1,108 +0,0 @@ -/* -*- mode: C -*- */ -/* - IGraph library. - Copyright (C) 2009-2012 Gabor Csardi - 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 - -#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_complex.h" - -#ifdef HAVE_STDINT_H -# include -#else -# if HAVE_SYS_INT_TYPES_H -# include /* for Solaris */ -# endif -#endif - -__BEGIN_DECLS - -/* -------------------------------------------------- */ -/* Flexible vector */ -/* -------------------------------------------------- */ - -#define BASE_IGRAPH_REAL -#include "igraph_pmt.h" -#include "igraph_vector_pmt.h" -#include "igraph_pmt_off.h" -#undef BASE_IGRAPH_REAL - -#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 - -int igraph_vector_floor(const igraph_vector_t *from, igraph_vector_long_t *to); -int igraph_vector_round(const igraph_vector_t *from, igraph_vector_long_t *to); - -igraph_bool_t igraph_vector_e_tol(const igraph_vector_t *lhs, - const igraph_vector_t *rhs, - 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_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/src/leidenalg/igraph-R/igraph_vector_pmt.h b/src/leidenalg/igraph-R/igraph_vector_pmt.h deleted file mode 100644 index f34a3f8..0000000 --- a/src/leidenalg/igraph-R/igraph_vector_pmt.h +++ /dev/null @@ -1,283 +0,0 @@ -/* -*- mode: C -*- */ -/* - IGraph library. - Copyright (C) 2007-2012 Gabor Csardi - 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); - -#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 - -/*--------------------*/ -/* Allocation */ -/*--------------------*/ - -int FUNCTION(igraph_vector,init)(TYPE(igraph_vector)* v, long int size); -int FUNCTION(igraph_vector,init_copy)(TYPE(igraph_vector)* v, - BASE* data, long int length); -int FUNCTION(igraph_vector,init_seq)(TYPE(igraph_vector)*v, BASE from, BASE to); -int FUNCTION(igraph_vector,copy)(TYPE(igraph_vector) *to, - const TYPE(igraph_vector) *from); -void FUNCTION(igraph_vector,destroy)(TYPE(igraph_vector)* v); - -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 #define. - * \param v The vector object. - * - * Time complexity: O(1). - */ -#define VECTOR(v) ((v).stor_begin) -#endif - -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); -void FUNCTION(igraph_vector,set)(TYPE(igraph_vector)* v, long int pos, BASE value); -BASE FUNCTION(igraph_vector,tail)(const TYPE(igraph_vector) *v); - -/*-----------------------*/ -/* Initializing elements */ -/*-----------------------*/ - -void FUNCTION(igraph_vector,null)(TYPE(igraph_vector)* v); -void FUNCTION(igraph_vector,fill)(TYPE(igraph_vector)* v, BASE e); - -/*-----------------------*/ -/* Vector views */ -/*-----------------------*/ - -const TYPE(igraph_vector) *FUNCTION(igraph_vector,view)(const TYPE(igraph_vector) *v, - const BASE *data, - long int length); - -/*-----------------------*/ -/* Copying vectors */ -/*-----------------------*/ - -void FUNCTION(igraph_vector,copy_to)(const TYPE(igraph_vector) *v, BASE* to); -int FUNCTION(igraph_vector,update)(TYPE(igraph_vector) *to, - const TYPE(igraph_vector) *from); -int FUNCTION(igraph_vector,append)(TYPE(igraph_vector) *to, - const TYPE(igraph_vector) *from); -int FUNCTION(igraph_vector,swap)(TYPE(igraph_vector) *v1, TYPE(igraph_vector) *v2); - -/*-----------------------*/ -/* Exchanging elements */ -/*-----------------------*/ - -int FUNCTION(igraph_vector,swap_elements)(TYPE(igraph_vector) *v, - long int i, long int j); -int FUNCTION(igraph_vector,reverse)(TYPE(igraph_vector) *v); -int FUNCTION(igraph_vector,shuffle)(TYPE(igraph_vector) *v); - -/*-----------------------*/ -/* Vector operations */ -/*-----------------------*/ - -void FUNCTION(igraph_vector,add_constant)(TYPE(igraph_vector) *v, BASE plus); -void FUNCTION(igraph_vector,scale)(TYPE(igraph_vector) *v, BASE by); -int FUNCTION(igraph_vector,add)(TYPE(igraph_vector) *v1, - const TYPE(igraph_vector) *v2); -int FUNCTION(igraph_vector,sub)(TYPE(igraph_vector) *v1, - const TYPE(igraph_vector) *v2); -int FUNCTION(igraph_vector,mul)(TYPE(igraph_vector) *v1, - const TYPE(igraph_vector) *v2); -int FUNCTION(igraph_vector,div)(TYPE(igraph_vector) *v1, - const TYPE(igraph_vector) *v2); -int FUNCTION(igraph_vector,cumsum)(TYPE(igraph_vector) *to, - const TYPE(igraph_vector) *from); - -#ifndef NOABS -int FUNCTION(igraph_vector,abs)(TYPE(igraph_vector) *v); -#endif - -/*------------------------------*/ -/* Comparison */ -/*------------------------------*/ - -igraph_bool_t FUNCTION(igraph_vector,all_e)(const TYPE(igraph_vector) *lhs, - const TYPE(igraph_vector) *rhs); -igraph_bool_t FUNCTION(igraph_vector,all_l)(const TYPE(igraph_vector) *lhs, - const TYPE(igraph_vector) *rhs); -igraph_bool_t FUNCTION(igraph_vector,all_g)(const TYPE(igraph_vector) *lhs, - const TYPE(igraph_vector) *rhs); -igraph_bool_t FUNCTION(igraph_vector,all_le)(const TYPE(igraph_vector) *lhs, - const TYPE(igraph_vector) *rhs); -igraph_bool_t FUNCTION(igraph_vector,all_ge)(const TYPE(igraph_vector) *lhs, - const TYPE(igraph_vector) *rhs); - -/*------------------------------*/ -/* Finding minimum and maximum */ -/*------------------------------*/ - -BASE FUNCTION(igraph_vector,min)(const TYPE(igraph_vector)* v); -BASE FUNCTION(igraph_vector,max)(const TYPE(igraph_vector)* v); -long int FUNCTION(igraph_vector,which_min)(const TYPE(igraph_vector)* v); -long int FUNCTION(igraph_vector,which_max)(const TYPE(igraph_vector)* v); -int FUNCTION(igraph_vector,minmax)(const TYPE(igraph_vector) *v, - BASE *min, BASE *max); -int FUNCTION(igraph_vector,which_minmax)(const TYPE(igraph_vector) *v, - long int *which_min, long int *which_max); - -/*-------------------*/ -/* Vector properties */ -/*-------------------*/ - -igraph_bool_t FUNCTION(igraph_vector,empty) (const TYPE(igraph_vector)* v); -long int FUNCTION(igraph_vector,size) (const TYPE(igraph_vector)* v); -igraph_bool_t FUNCTION(igraph_vector,isnull)(const TYPE(igraph_vector) *v); -BASE FUNCTION(igraph_vector,sum)(const TYPE(igraph_vector) *v); -igraph_real_t FUNCTION(igraph_vector,sumsq)(const TYPE(igraph_vector) *v); -BASE FUNCTION(igraph_vector,prod)(const TYPE(igraph_vector) *v); -igraph_bool_t FUNCTION(igraph_vector,isininterval)(const TYPE(igraph_vector) *v, - BASE low, BASE high); -igraph_bool_t FUNCTION(igraph_vector,any_smaller)(const TYPE(igraph_vector) *v, - BASE limit); -igraph_bool_t FUNCTION(igraph_vector,is_equal)(const TYPE(igraph_vector) *lhs, - const TYPE(igraph_vector) *rhs); -BASE FUNCTION(igraph_vector,maxdifference)(const TYPE(igraph_vector) *m1, - const TYPE(igraph_vector) *m2); - -/*------------------------*/ -/* Searching for elements */ -/*------------------------*/ - -igraph_bool_t FUNCTION(igraph_vector,contains)(const TYPE(igraph_vector) *v, BASE e); -igraph_bool_t FUNCTION(igraph_vector,search)(const TYPE(igraph_vector) *v, - long int from, BASE what, - long int *pos); -igraph_bool_t FUNCTION(igraph_vector,binsearch)(const TYPE(igraph_vector) *v, - BASE what, long int *pos); -igraph_bool_t FUNCTION(igraph_vector,binsearch2)(const TYPE(igraph_vector) *v, - BASE what); - -/*------------------------*/ -/* Resizing operations */ -/*------------------------*/ - -void FUNCTION(igraph_vector,clear)(TYPE(igraph_vector)* v); -int FUNCTION(igraph_vector,resize)(TYPE(igraph_vector)* v, long int newsize); -int FUNCTION(igraph_vector,resize_min)(TYPE(igraph_vector)*v); -int FUNCTION(igraph_vector,reserve)(TYPE(igraph_vector)* v, long int size); -int FUNCTION(igraph_vector,push_back)(TYPE(igraph_vector)* v, BASE e); -BASE FUNCTION(igraph_vector,pop_back)(TYPE(igraph_vector)* v); -int FUNCTION(igraph_vector,insert)(TYPE(igraph_vector) *v, long int pos, BASE value); -void FUNCTION(igraph_vector,remove)(TYPE(igraph_vector) *v, long int elem); -void FUNCTION(igraph_vector,remove_section)(TYPE(igraph_vector) *v, - long int from, long int to); - -/*-----------*/ -/* Sorting */ -/*-----------*/ - -void FUNCTION(igraph_vector,sort)(TYPE(igraph_vector) *v); -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,fprint)(const TYPE(igraph_vector) *v, FILE *file); - -#ifdef BASE_COMPLEX - -int igraph_vector_complex_real(const igraph_vector_complex_t *v, - igraph_vector_t *real); -int igraph_vector_complex_imag(const igraph_vector_complex_t *v, - igraph_vector_t *imag); -int igraph_vector_complex_realimag(const igraph_vector_complex_t *v, - igraph_vector_t *real, - igraph_vector_t *imag); -int igraph_vector_complex_create(igraph_vector_complex_t *v, - const igraph_vector_t *real, - const igraph_vector_t *imag); -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); -void FUNCTION(igraph_vector,remove_negidx)(TYPE(igraph_vector) *v, - const igraph_vector_t *neg, - 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); diff --git a/src/leidenalg/igraph-R/igraph_vector_ptr.h b/src/leidenalg/igraph-R/igraph_vector_ptr.h deleted file mode 100644 index 54ca964..0000000 --- a/src/leidenalg/igraph-R/igraph_vector_ptr.h +++ /dev/null @@ -1,105 +0,0 @@ -/* -*- mode: C -*- */ -/* - IGraph library. - Copyright (C) 2009-2012 Gabor Csardi - 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 - -#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 - -/* -------------------------------------------------- */ -/* 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) - -int igraph_vector_ptr_init (igraph_vector_ptr_t* v, long int size); -int igraph_vector_ptr_init_copy (igraph_vector_ptr_t* v, void** data, long int length); -const igraph_vector_ptr_t *igraph_vector_ptr_view (const igraph_vector_ptr_t *v, - void *const *data, long int length); -void igraph_vector_ptr_destroy (igraph_vector_ptr_t* v); -void igraph_vector_ptr_free_all (igraph_vector_ptr_t* v); -void igraph_vector_ptr_destroy_all (igraph_vector_ptr_t* v); -int igraph_vector_ptr_reserve (igraph_vector_ptr_t* v, long int size); -igraph_bool_t igraph_vector_ptr_empty (const igraph_vector_ptr_t* v); -long int igraph_vector_ptr_size (const igraph_vector_ptr_t* v); -void igraph_vector_ptr_clear (igraph_vector_ptr_t* v); -void igraph_vector_ptr_null (igraph_vector_ptr_t* v); -int igraph_vector_ptr_push_back (igraph_vector_ptr_t* v, void* e); -int igraph_vector_ptr_append (igraph_vector_ptr_t *to, - const igraph_vector_ptr_t *from); -void *igraph_vector_ptr_pop_back (igraph_vector_ptr_t *v); -int igraph_vector_ptr_insert(igraph_vector_ptr_t *v, long int pos, void* e); -void* igraph_vector_ptr_e (const igraph_vector_ptr_t* v, long int pos); -void igraph_vector_ptr_set (igraph_vector_ptr_t* v, long int pos, void* value); -int igraph_vector_ptr_resize(igraph_vector_ptr_t* v, long int newsize); -void igraph_vector_ptr_copy_to(const igraph_vector_ptr_t *v, void** to); -int igraph_vector_ptr_copy(igraph_vector_ptr_t *to, const igraph_vector_ptr_t *from); -void igraph_vector_ptr_remove(igraph_vector_ptr_t *v, long int pos); -void igraph_vector_ptr_sort(igraph_vector_ptr_t *v, int(*compar)(const void*, const void*)); - -igraph_finally_func_t* igraph_vector_ptr_get_item_destructor(const igraph_vector_ptr_t *v); -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/src/leidenalg/include/CPMVertexPartition.h b/src/leidenalg/include/CPMVertexPartition.h index 639d9ed..ca820b9 100644 --- a/src/leidenalg/include/CPMVertexPartition.h +++ b/src/leidenalg/include/CPMVertexPartition.h @@ -2,6 +2,7 @@ #define CPMVERTEXPARTITION_H #include +#include "igraph.h" class CPMVertexPartition : public LinearResolutionParameterVertexPartition { diff --git a/src/leidenalg/include/GraphHelper.h b/src/leidenalg/include/GraphHelper.h index 5f56cc4..4339106 100644 --- a/src/leidenalg/include/GraphHelper.h +++ b/src/leidenalg/include/GraphHelper.h @@ -1,7 +1,7 @@ #ifndef GRAPHHELPER_INCLUDED #define GRAPHHELPER_INCLUDED -#include +#include "igraph.h" #include #include #include @@ -13,7 +13,7 @@ using std::endl; //#endif -#include + class MutableVertexPartition; diff --git a/src/leidenalg/include/LinearResolutionParameterVertexPartition.h b/src/leidenalg/include/LinearResolutionParameterVertexPartition.h index 13ce65c..9050928 100644 --- a/src/leidenalg/include/LinearResolutionParameterVertexPartition.h +++ b/src/leidenalg/include/LinearResolutionParameterVertexPartition.h @@ -2,7 +2,7 @@ #define LINEARRESOLUTIONPARAMETERVERTEXPARTITION_H #include -#include +#include "igraph.h" class LinearResolutionParameterVertexPartition : public ResolutionParameterVertexPartition { diff --git a/src/leidenalg/include/ModularityVertexPartition.h b/src/leidenalg/include/ModularityVertexPartition.h index e975cd9..b9c06b7 100644 --- a/src/leidenalg/include/ModularityVertexPartition.h +++ b/src/leidenalg/include/ModularityVertexPartition.h @@ -2,7 +2,8 @@ #define MODULARITYVERTEXPARTITION_H #include -#include +#include "igraph.h" + class ModularityVertexPartition : public MutableVertexPartition { diff --git a/src/leidenalg/include/MutableVertexPartition.h b/src/leidenalg/include/MutableVertexPartition.h index 6449662..2dbd097 100644 --- a/src/leidenalg/include/MutableVertexPartition.h +++ b/src/leidenalg/include/MutableVertexPartition.h @@ -7,6 +7,8 @@ #include #include #include +#include "igraph.h" + using std::string; using std::map; diff --git a/src/leidenalg/include/Optimiser.h b/src/leidenalg/include/Optimiser.h index 4d36e93..3f52635 100644 --- a/src/leidenalg/include/Optimiser.h +++ b/src/leidenalg/include/Optimiser.h @@ -4,7 +4,9 @@ #include "MutableVertexPartition.h" #include #include -#include + +#include "igraph.h" + #include using std::cerr; diff --git a/src/leidenalg/include/RBConfigurationVertexPartition.h b/src/leidenalg/include/RBConfigurationVertexPartition.h index 42abf75..860856d 100644 --- a/src/leidenalg/include/RBConfigurationVertexPartition.h +++ b/src/leidenalg/include/RBConfigurationVertexPartition.h @@ -2,7 +2,9 @@ #define RBCONFIGURATIONVERTEXPARTITION_H #include "LinearResolutionParameterVertexPartition.h" -#include + +#include "igraph.h" + class RBConfigurationVertexPartition : public LinearResolutionParameterVertexPartition { diff --git a/src/leidenalg/include/RBERVertexPartition.h b/src/leidenalg/include/RBERVertexPartition.h index 06c778e..dff2ad5 100644 --- a/src/leidenalg/include/RBERVertexPartition.h +++ b/src/leidenalg/include/RBERVertexPartition.h @@ -2,7 +2,9 @@ #define RBERVERTEXPARTITION_H #include -#include + +#include "igraph.h" + class RBERVertexPartition : public LinearResolutionParameterVertexPartition { diff --git a/src/leidenalg/include/ResolutionParameterVertexPartition.h b/src/leidenalg/include/ResolutionParameterVertexPartition.h index 8bd6f36..544021c 100644 --- a/src/leidenalg/include/ResolutionParameterVertexPartition.h +++ b/src/leidenalg/include/ResolutionParameterVertexPartition.h @@ -2,7 +2,9 @@ #define RESOLUTIONPARAMETERVERTEXPARTITION_H #include -#include + +#include "igraph.h" + class ResolutionParameterVertexPartition : public MutableVertexPartition { diff --git a/src/leidenalg/include/SignificanceVertexPartition.h b/src/leidenalg/include/SignificanceVertexPartition.h index 59c06f2..afc6bd0 100644 --- a/src/leidenalg/include/SignificanceVertexPartition.h +++ b/src/leidenalg/include/SignificanceVertexPartition.h @@ -2,7 +2,9 @@ #define SIGNIFICANCEVERTEXPARTITION_H #include -#include + +#include "igraph.h" + class SignificanceVertexPartition : public MutableVertexPartition { diff --git a/src/leidenalg/include/SurpriseVertexPartition.h b/src/leidenalg/include/SurpriseVertexPartition.h index ea1c14b..9591034 100644 --- a/src/leidenalg/include/SurpriseVertexPartition.h +++ b/src/leidenalg/include/SurpriseVertexPartition.h @@ -2,7 +2,10 @@ #define SURPRISEVERTEXPARTITION_H #include "MutableVertexPartition.h" -#include + +#include "igraph.h" + + #include using std::cerr; using std::endl; diff --git a/src/rigraph/Makefile b/src/rigraph/Makefile new file mode 100644 index 0000000..e2751dd --- /dev/null +++ b/src/rigraph/Makefile @@ -0,0 +1,15 @@ +CXX_STD=CXX11 + +PKG_CFLAGS=$(C_VISIBILITY) +PKG_CXXFLAGS=$(CXX_VISIBILITY) +PKG_FFLAGS=$(F_VISIBILITY) + +PKG_CPPFLAGS=-DUSING_R -I. -Icore -Iinclude -Ivendor \ + @XML2_CPPFLAGS@ -DNDEBUG -DNTIMER -DNPRINT \ + -DPACKAGE_VERSION=\"@PACKAGE_VERSION@\" -DINTERNAL_ARPACK \ + -DPRPACK_IGRAPH_SUPPORT -DIGRAPH_THREAD_LOCAL=/**/ +PKG_LIBS=@XML2_LIBS@ @GMP_LIBS@ @GLPK_LIBS@ $(LAPACK_LIBS) $(BLAS_LIBS) $(FLIBS) + +all: $(SHLIB) + +OBJECTS=core/centrality/betweenness.o core/centrality/centrality_other.o core/centrality/centralization.o core/centrality/closeness.o core/centrality/coreness.o core/centrality/prpack.o core/centrality/prpack/prpack_base_graph.o core/centrality/prpack/prpack_igraph_graph.o core/centrality/prpack/prpack_preprocessed_ge_graph.o core/centrality/prpack/prpack_preprocessed_gs_graph.o core/centrality/prpack/prpack_preprocessed_scc_graph.o core/centrality/prpack/prpack_preprocessed_schur_graph.o core/centrality/prpack/prpack_result.o core/centrality/prpack/prpack_solver.o core/centrality/prpack/prpack_utils.o core/cliques/cliquer/cliquer.o core/cliques/cliquer/cliquer_graph.o core/cliques/cliquer/reorder.o core/cliques/cliquer_wrapper.o core/cliques/cliques.o core/cliques/glet.o core/cliques/maximal_cliques.o core/community/community_misc.o core/community/edge_betweenness.o core/community/fast_modularity.o core/community/fluid.o core/community/infomap/infomap.o core/community/infomap/infomap_FlowGraph.o core/community/infomap/infomap_Greedy.o core/community/infomap/infomap_Node.o core/community/label_propagation.o core/community/leading_eigenvector.o core/community/leiden.o core/community/louvain.o core/community/modularity.o core/community/optimal_modularity.o core/community/spinglass/NetDataTypes.o core/community/spinglass/NetRoutines.o core/community/spinglass/clustertool.o core/community/spinglass/pottsmodel_2.o core/community/walktrap/walktrap.o core/community/walktrap/walktrap_communities.o core/community/walktrap/walktrap_graph.o core/community/walktrap/walktrap_heap.o core/connectivity/cohesive_blocks.o core/connectivity/components.o core/connectivity/separators.o core/constructors/adjacency.o core/constructors/atlas.o core/constructors/basic_constructors.o core/constructors/de_bruijn.o core/constructors/famous.o core/constructors/full.o core/constructors/kautz.o core/constructors/lcf.o core/constructors/linegraph.o core/constructors/prufer.o core/constructors/regular.o core/core/array.o core/core/buckets.o core/core/cutheap.o core/core/dqueue.o core/core/error.o core/core/estack.o core/core/fixed_vectorlist.o core/core/grid.o core/core/heap.o core/core/indheap.o core/core/interruption.o core/core/marked_queue.o core/core/matrix.o core/core/memory.o core/core/printing.o core/core/progress.o core/core/psumtree.o core/core/set.o core/core/sparsemat.o core/core/spmatrix.o core/core/stack.o core/core/statusbar.o core/core/strvector.o core/core/trie.o core/core/vector.o core/core/vector_ptr.o core/flow/flow.o core/flow/st-cuts.o core/games/barabasi.o core/games/callaway_traits.o core/games/citations.o core/games/correlated.o core/games/degree_sequence.o core/games/degree_sequence_vl/gengraph_box_list.o core/games/degree_sequence_vl/gengraph_degree_sequence.o core/games/degree_sequence_vl/gengraph_graph_molloy_hash.o core/games/degree_sequence_vl/gengraph_graph_molloy_optimized.o core/games/degree_sequence_vl/gengraph_mr-connected.o core/games/degree_sequence_vl/gengraph_powerlaw.o core/games/degree_sequence_vl/gengraph_random.o core/games/dotproduct.o core/games/erdos_renyi.o core/games/establishment.o core/games/forestfire.o core/games/grg.o core/games/growing_random.o core/games/islands.o core/games/k_regular.o core/games/preference.o core/games/recent_degree.o core/games/sbm.o core/games/static_fitness.o core/games/tree.o core/games/watts_strogatz.o core/graph/adjlist.o core/graph/attributes.o core/graph/basic_query.o core/graph/cattributes.o core/graph/iterators.o core/graph/type_indexededgelist.o core/graph/visitors.o core/hrg/hrg.o core/hrg/hrg_types.o core/internal/glpk_support.o core/internal/hacks.o core/internal/lsap.o core/internal/qsort.o core/internal/qsort_r.o core/internal/zeroin.o core/io/dimacs.o core/io/dl-lexer.o core/io/dl-parser.o core/io/dl.o core/io/dot.o core/io/edgelist.o core/io/gml-lexer.o core/io/gml-parser.o core/io/gml-tree.o core/io/gml.o core/io/graphdb.o core/io/graphml.o core/io/leda.o core/io/lgl-lexer.o core/io/lgl-parser.o core/io/lgl.o core/io/ncol-lexer.o core/io/ncol-parser.o core/io/ncol.o core/io/pajek-lexer.o core/io/pajek-parser.o core/io/pajek.o core/isomorphism/bliss.o core/isomorphism/bliss/defs.o core/isomorphism/bliss/graph.o core/isomorphism/bliss/heap.o core/isomorphism/bliss/orbit.o core/isomorphism/bliss/partition.o core/isomorphism/bliss/uintseqhash.o core/isomorphism/bliss/utils.o core/isomorphism/isoclasses.o core/isomorphism/isomorphism_misc.o core/isomorphism/lad.o core/isomorphism/queries.o core/isomorphism/vf2.o core/layout/circular.o core/layout/davidson_harel.o core/layout/drl/DensityGrid.o core/layout/drl/DensityGrid_3d.o core/layout/drl/drl_graph.o core/layout/drl/drl_graph_3d.o core/layout/drl/drl_layout.o core/layout/drl/drl_layout_3d.o core/layout/drl/drl_parse.o core/layout/fruchterman_reingold.o core/layout/gem.o core/layout/graphopt.o core/layout/kamada_kawai.o core/layout/large_graph.o core/layout/layout_bipartite.o core/layout/layout_grid.o core/layout/layout_random.o core/layout/mds.o core/layout/merge_dla.o core/layout/merge_grid.o core/layout/reingold_tilford.o core/layout/sugiyama.o core/linalg/arpack.o core/linalg/blas.o core/linalg/eigen.o core/linalg/lapack.o core/math/bfgs.o core/math/complex.o core/math/utils.o core/misc/bipartite.o core/misc/chordality.o core/misc/cocitation.o core/misc/coloring.o core/misc/conversion.o core/misc/degree_sequence.o core/misc/embedding.o core/misc/feedback_arc_set.o core/misc/graphicality.o core/misc/matching.o core/misc/microscopic_update.o core/misc/mixing.o core/misc/motifs.o core/misc/other.o core/misc/scan.o core/misc/sir.o core/misc/spanning_trees.o core/operators/add_edge.o core/operators/complementer.o core/operators/compose.o core/operators/connect_neighborhood.o core/operators/contract.o core/operators/difference.o core/operators/disjoint_union.o core/operators/intersection.o core/operators/misc_internal.o core/operators/permute.o core/operators/rewire.o core/operators/rewire_edges.o core/operators/simplify.o core/operators/subgraph.o core/operators/union.o core/paths/all_shortest_paths.o core/paths/bellman_ford.o core/paths/dijkstra.o core/paths/distances.o core/paths/eulerian.o core/paths/histogram.o core/paths/johnson.o core/paths/random_walk.o core/paths/shortest_paths.o core/paths/simple_paths.o core/paths/unweighted.o core/properties/basic_properties.o core/properties/constraint.o core/properties/convergence_degree.o core/properties/dag.o core/properties/degrees.o core/properties/girth.o core/properties/loops.o core/properties/multiplicity.o core/properties/neighborhood.o core/properties/spectral.o core/properties/trees.o core/properties/triangles.o core/random/random.o core/scg/scg.o core/scg/scg_approximate_methods.o core/scg/scg_exact_scg.o core/scg/scg_kmeans.o core/scg/scg_optimal_method.o core/scg/scg_utils.o core/version.o vendor/cs/cs_add.o vendor/cs/cs_amd.o vendor/cs/cs_chol.o vendor/cs/cs_cholsol.o vendor/cs/cs_compress.o vendor/cs/cs_counts.o vendor/cs/cs_cumsum.o vendor/cs/cs_dfs.o vendor/cs/cs_dmperm.o vendor/cs/cs_droptol.o vendor/cs/cs_dropzeros.o vendor/cs/cs_dupl.o vendor/cs/cs_entry.o vendor/cs/cs_ereach.o vendor/cs/cs_etree.o vendor/cs/cs_fkeep.o vendor/cs/cs_gaxpy.o vendor/cs/cs_happly.o vendor/cs/cs_house.o vendor/cs/cs_ipvec.o vendor/cs/cs_leaf.o vendor/cs/cs_load.o vendor/cs/cs_lsolve.o vendor/cs/cs_ltsolve.o vendor/cs/cs_lu.o vendor/cs/cs_lusol.o vendor/cs/cs_malloc.o vendor/cs/cs_maxtrans.o vendor/cs/cs_multiply.o vendor/cs/cs_norm.o vendor/cs/cs_permute.o vendor/cs/cs_pinv.o vendor/cs/cs_post.o vendor/cs/cs_print.o vendor/cs/cs_pvec.o vendor/cs/cs_qr.o vendor/cs/cs_qrsol.o vendor/cs/cs_randperm.o vendor/cs/cs_reach.o vendor/cs/cs_scatter.o vendor/cs/cs_scc.o vendor/cs/cs_schol.o vendor/cs/cs_spsolve.o vendor/cs/cs_sqr.o vendor/cs/cs_symperm.o vendor/cs/cs_tdfs.o vendor/cs/cs_transpose.o vendor/cs/cs_updown.o vendor/cs/cs_usolve.o vendor/cs/cs_util.o vendor/cs/cs_utsolve.o vendor/mini-gmp/mini-gmp.o vendor/plfit/gss.o vendor/plfit/hzeta.o vendor/plfit/kolmogorov.o vendor/plfit/lbfgs.o vendor/plfit/mt.o vendor/plfit/options.o vendor/plfit/platform.o vendor/plfit/plfit.o vendor/plfit/plfit_error.o vendor/plfit/rbinom.o vendor/plfit/sampling.o vendor/arpack/dgetv0.o vendor/arpack/dlaqrb.o vendor/arpack/dmout.o vendor/arpack/dnaitr.o vendor/arpack/dnapps.o vendor/arpack/dnaup2.o vendor/arpack/dnaupd.o vendor/arpack/dnconv.o vendor/arpack/dneigh.o vendor/arpack/dneupd.o vendor/arpack/dngets.o vendor/arpack/dsaitr.o vendor/arpack/dsapps.o vendor/arpack/dsaup2.o vendor/arpack/dsaupd.o vendor/arpack/dsconv.o vendor/arpack/dseigt.o vendor/arpack/dsesrt.o vendor/arpack/dseupd.o vendor/arpack/dsgets.o vendor/arpack/dsortc.o vendor/arpack/dsortr.o vendor/arpack/dstatn.o vendor/arpack/dstats.o vendor/arpack/dstqrb.o vendor/arpack/dvout.o vendor/arpack/ivout.o vendor/arpack/second.o vendor/arpack/wrap.o vendor/simpleraytracer/Color.o vendor/simpleraytracer/Light.o vendor/simpleraytracer/Point.o vendor/simpleraytracer/RIgraphRay.o vendor/simpleraytracer/Ray.o vendor/simpleraytracer/RayTracer.o vendor/simpleraytracer/RayVector.o vendor/simpleraytracer/Shape.o vendor/simpleraytracer/Sphere.o vendor/simpleraytracer/Triangle.o vendor/simpleraytracer/unit_limiter.o vendor/uuid/R.o vendor/uuid/clear.o vendor/uuid/compare.o vendor/uuid/copy.o vendor/uuid/gen_uuid.o vendor/uuid/isnull.o vendor/uuid/pack.o vendor/uuid/parse.o vendor/uuid/unpack.o vendor/uuid/unparse.o rinterface.o rinterface_extra.o rrandom.o lazyeval.o diff --git a/src/rigraph/Makevars.in b/src/rigraph/Makevars.in new file mode 100644 index 0000000..e2751dd --- /dev/null +++ b/src/rigraph/Makevars.in @@ -0,0 +1,15 @@ +CXX_STD=CXX11 + +PKG_CFLAGS=$(C_VISIBILITY) +PKG_CXXFLAGS=$(CXX_VISIBILITY) +PKG_FFLAGS=$(F_VISIBILITY) + +PKG_CPPFLAGS=-DUSING_R -I. -Icore -Iinclude -Ivendor \ + @XML2_CPPFLAGS@ -DNDEBUG -DNTIMER -DNPRINT \ + -DPACKAGE_VERSION=\"@PACKAGE_VERSION@\" -DINTERNAL_ARPACK \ + -DPRPACK_IGRAPH_SUPPORT -DIGRAPH_THREAD_LOCAL=/**/ +PKG_LIBS=@XML2_LIBS@ @GMP_LIBS@ @GLPK_LIBS@ $(LAPACK_LIBS) $(BLAS_LIBS) $(FLIBS) + +all: $(SHLIB) + +OBJECTS=core/centrality/betweenness.o core/centrality/centrality_other.o core/centrality/centralization.o core/centrality/closeness.o core/centrality/coreness.o core/centrality/prpack.o core/centrality/prpack/prpack_base_graph.o core/centrality/prpack/prpack_igraph_graph.o core/centrality/prpack/prpack_preprocessed_ge_graph.o core/centrality/prpack/prpack_preprocessed_gs_graph.o core/centrality/prpack/prpack_preprocessed_scc_graph.o core/centrality/prpack/prpack_preprocessed_schur_graph.o core/centrality/prpack/prpack_result.o core/centrality/prpack/prpack_solver.o core/centrality/prpack/prpack_utils.o core/cliques/cliquer/cliquer.o core/cliques/cliquer/cliquer_graph.o core/cliques/cliquer/reorder.o core/cliques/cliquer_wrapper.o core/cliques/cliques.o core/cliques/glet.o core/cliques/maximal_cliques.o core/community/community_misc.o core/community/edge_betweenness.o core/community/fast_modularity.o core/community/fluid.o core/community/infomap/infomap.o core/community/infomap/infomap_FlowGraph.o core/community/infomap/infomap_Greedy.o core/community/infomap/infomap_Node.o core/community/label_propagation.o core/community/leading_eigenvector.o core/community/leiden.o core/community/louvain.o core/community/modularity.o core/community/optimal_modularity.o core/community/spinglass/NetDataTypes.o core/community/spinglass/NetRoutines.o core/community/spinglass/clustertool.o core/community/spinglass/pottsmodel_2.o core/community/walktrap/walktrap.o core/community/walktrap/walktrap_communities.o core/community/walktrap/walktrap_graph.o core/community/walktrap/walktrap_heap.o core/connectivity/cohesive_blocks.o core/connectivity/components.o core/connectivity/separators.o core/constructors/adjacency.o core/constructors/atlas.o core/constructors/basic_constructors.o core/constructors/de_bruijn.o core/constructors/famous.o core/constructors/full.o core/constructors/kautz.o core/constructors/lcf.o core/constructors/linegraph.o core/constructors/prufer.o core/constructors/regular.o core/core/array.o core/core/buckets.o core/core/cutheap.o core/core/dqueue.o core/core/error.o core/core/estack.o core/core/fixed_vectorlist.o core/core/grid.o core/core/heap.o core/core/indheap.o core/core/interruption.o core/core/marked_queue.o core/core/matrix.o core/core/memory.o core/core/printing.o core/core/progress.o core/core/psumtree.o core/core/set.o core/core/sparsemat.o core/core/spmatrix.o core/core/stack.o core/core/statusbar.o core/core/strvector.o core/core/trie.o core/core/vector.o core/core/vector_ptr.o core/flow/flow.o core/flow/st-cuts.o core/games/barabasi.o core/games/callaway_traits.o core/games/citations.o core/games/correlated.o core/games/degree_sequence.o core/games/degree_sequence_vl/gengraph_box_list.o core/games/degree_sequence_vl/gengraph_degree_sequence.o core/games/degree_sequence_vl/gengraph_graph_molloy_hash.o core/games/degree_sequence_vl/gengraph_graph_molloy_optimized.o core/games/degree_sequence_vl/gengraph_mr-connected.o core/games/degree_sequence_vl/gengraph_powerlaw.o core/games/degree_sequence_vl/gengraph_random.o core/games/dotproduct.o core/games/erdos_renyi.o core/games/establishment.o core/games/forestfire.o core/games/grg.o core/games/growing_random.o core/games/islands.o core/games/k_regular.o core/games/preference.o core/games/recent_degree.o core/games/sbm.o core/games/static_fitness.o core/games/tree.o core/games/watts_strogatz.o core/graph/adjlist.o core/graph/attributes.o core/graph/basic_query.o core/graph/cattributes.o core/graph/iterators.o core/graph/type_indexededgelist.o core/graph/visitors.o core/hrg/hrg.o core/hrg/hrg_types.o core/internal/glpk_support.o core/internal/hacks.o core/internal/lsap.o core/internal/qsort.o core/internal/qsort_r.o core/internal/zeroin.o core/io/dimacs.o core/io/dl-lexer.o core/io/dl-parser.o core/io/dl.o core/io/dot.o core/io/edgelist.o core/io/gml-lexer.o core/io/gml-parser.o core/io/gml-tree.o core/io/gml.o core/io/graphdb.o core/io/graphml.o core/io/leda.o core/io/lgl-lexer.o core/io/lgl-parser.o core/io/lgl.o core/io/ncol-lexer.o core/io/ncol-parser.o core/io/ncol.o core/io/pajek-lexer.o core/io/pajek-parser.o core/io/pajek.o core/isomorphism/bliss.o core/isomorphism/bliss/defs.o core/isomorphism/bliss/graph.o core/isomorphism/bliss/heap.o core/isomorphism/bliss/orbit.o core/isomorphism/bliss/partition.o core/isomorphism/bliss/uintseqhash.o core/isomorphism/bliss/utils.o core/isomorphism/isoclasses.o core/isomorphism/isomorphism_misc.o core/isomorphism/lad.o core/isomorphism/queries.o core/isomorphism/vf2.o core/layout/circular.o core/layout/davidson_harel.o core/layout/drl/DensityGrid.o core/layout/drl/DensityGrid_3d.o core/layout/drl/drl_graph.o core/layout/drl/drl_graph_3d.o core/layout/drl/drl_layout.o core/layout/drl/drl_layout_3d.o core/layout/drl/drl_parse.o core/layout/fruchterman_reingold.o core/layout/gem.o core/layout/graphopt.o core/layout/kamada_kawai.o core/layout/large_graph.o core/layout/layout_bipartite.o core/layout/layout_grid.o core/layout/layout_random.o core/layout/mds.o core/layout/merge_dla.o core/layout/merge_grid.o core/layout/reingold_tilford.o core/layout/sugiyama.o core/linalg/arpack.o core/linalg/blas.o core/linalg/eigen.o core/linalg/lapack.o core/math/bfgs.o core/math/complex.o core/math/utils.o core/misc/bipartite.o core/misc/chordality.o core/misc/cocitation.o core/misc/coloring.o core/misc/conversion.o core/misc/degree_sequence.o core/misc/embedding.o core/misc/feedback_arc_set.o core/misc/graphicality.o core/misc/matching.o core/misc/microscopic_update.o core/misc/mixing.o core/misc/motifs.o core/misc/other.o core/misc/scan.o core/misc/sir.o core/misc/spanning_trees.o core/operators/add_edge.o core/operators/complementer.o core/operators/compose.o core/operators/connect_neighborhood.o core/operators/contract.o core/operators/difference.o core/operators/disjoint_union.o core/operators/intersection.o core/operators/misc_internal.o core/operators/permute.o core/operators/rewire.o core/operators/rewire_edges.o core/operators/simplify.o core/operators/subgraph.o core/operators/union.o core/paths/all_shortest_paths.o core/paths/bellman_ford.o core/paths/dijkstra.o core/paths/distances.o core/paths/eulerian.o core/paths/histogram.o core/paths/johnson.o core/paths/random_walk.o core/paths/shortest_paths.o core/paths/simple_paths.o core/paths/unweighted.o core/properties/basic_properties.o core/properties/constraint.o core/properties/convergence_degree.o core/properties/dag.o core/properties/degrees.o core/properties/girth.o core/properties/loops.o core/properties/multiplicity.o core/properties/neighborhood.o core/properties/spectral.o core/properties/trees.o core/properties/triangles.o core/random/random.o core/scg/scg.o core/scg/scg_approximate_methods.o core/scg/scg_exact_scg.o core/scg/scg_kmeans.o core/scg/scg_optimal_method.o core/scg/scg_utils.o core/version.o vendor/cs/cs_add.o vendor/cs/cs_amd.o vendor/cs/cs_chol.o vendor/cs/cs_cholsol.o vendor/cs/cs_compress.o vendor/cs/cs_counts.o vendor/cs/cs_cumsum.o vendor/cs/cs_dfs.o vendor/cs/cs_dmperm.o vendor/cs/cs_droptol.o vendor/cs/cs_dropzeros.o vendor/cs/cs_dupl.o vendor/cs/cs_entry.o vendor/cs/cs_ereach.o vendor/cs/cs_etree.o vendor/cs/cs_fkeep.o vendor/cs/cs_gaxpy.o vendor/cs/cs_happly.o vendor/cs/cs_house.o vendor/cs/cs_ipvec.o vendor/cs/cs_leaf.o vendor/cs/cs_load.o vendor/cs/cs_lsolve.o vendor/cs/cs_ltsolve.o vendor/cs/cs_lu.o vendor/cs/cs_lusol.o vendor/cs/cs_malloc.o vendor/cs/cs_maxtrans.o vendor/cs/cs_multiply.o vendor/cs/cs_norm.o vendor/cs/cs_permute.o vendor/cs/cs_pinv.o vendor/cs/cs_post.o vendor/cs/cs_print.o vendor/cs/cs_pvec.o vendor/cs/cs_qr.o vendor/cs/cs_qrsol.o vendor/cs/cs_randperm.o vendor/cs/cs_reach.o vendor/cs/cs_scatter.o vendor/cs/cs_scc.o vendor/cs/cs_schol.o vendor/cs/cs_spsolve.o vendor/cs/cs_sqr.o vendor/cs/cs_symperm.o vendor/cs/cs_tdfs.o vendor/cs/cs_transpose.o vendor/cs/cs_updown.o vendor/cs/cs_usolve.o vendor/cs/cs_util.o vendor/cs/cs_utsolve.o vendor/mini-gmp/mini-gmp.o vendor/plfit/gss.o vendor/plfit/hzeta.o vendor/plfit/kolmogorov.o vendor/plfit/lbfgs.o vendor/plfit/mt.o vendor/plfit/options.o vendor/plfit/platform.o vendor/plfit/plfit.o vendor/plfit/plfit_error.o vendor/plfit/rbinom.o vendor/plfit/sampling.o vendor/arpack/dgetv0.o vendor/arpack/dlaqrb.o vendor/arpack/dmout.o vendor/arpack/dnaitr.o vendor/arpack/dnapps.o vendor/arpack/dnaup2.o vendor/arpack/dnaupd.o vendor/arpack/dnconv.o vendor/arpack/dneigh.o vendor/arpack/dneupd.o vendor/arpack/dngets.o vendor/arpack/dsaitr.o vendor/arpack/dsapps.o vendor/arpack/dsaup2.o vendor/arpack/dsaupd.o vendor/arpack/dsconv.o vendor/arpack/dseigt.o vendor/arpack/dsesrt.o vendor/arpack/dseupd.o vendor/arpack/dsgets.o vendor/arpack/dsortc.o vendor/arpack/dsortr.o vendor/arpack/dstatn.o vendor/arpack/dstats.o vendor/arpack/dstqrb.o vendor/arpack/dvout.o vendor/arpack/ivout.o vendor/arpack/second.o vendor/arpack/wrap.o vendor/simpleraytracer/Color.o vendor/simpleraytracer/Light.o vendor/simpleraytracer/Point.o vendor/simpleraytracer/RIgraphRay.o vendor/simpleraytracer/Ray.o vendor/simpleraytracer/RayTracer.o vendor/simpleraytracer/RayVector.o vendor/simpleraytracer/Shape.o vendor/simpleraytracer/Sphere.o vendor/simpleraytracer/Triangle.o vendor/simpleraytracer/unit_limiter.o vendor/uuid/R.o vendor/uuid/clear.o vendor/uuid/compare.o vendor/uuid/copy.o vendor/uuid/gen_uuid.o vendor/uuid/isnull.o vendor/uuid/pack.o vendor/uuid/parse.o vendor/uuid/unpack.o vendor/uuid/unparse.o rinterface.o rinterface_extra.o rrandom.o lazyeval.o diff --git a/src/rigraph/Makevars.ucrt b/src/rigraph/Makevars.ucrt new file mode 100644 index 0000000..9def5a3 --- /dev/null +++ b/src/rigraph/Makevars.ucrt @@ -0,0 +1,11 @@ +CXX_STD = CXX11 + +LIB_XML ?= $(R_TOOLS_SOFT) +GLPK_HOME ?= $(R_TOOLS_SOFT) +LIB_GMP ?= $(R_TOOLS_SOFT) + +PKG_CPPFLAGS = -I${LIB_XML}/include/libxml2 -I${LIB_XML}/include -DLIBXML_STATIC -DUSING_R -DHAVE_FMEMOPEN=0 -DHAVE_OPEN_MEMSTREAM=0 -DHAVE_RINTF -DHAVE_STRCASECMP -DWin32 -DHAVE_LIBXML=1 -DHAVE_UNISTD_H -Wall -DPACKAGE_VERSION=\"1.3.0.9016\" -DHAVE_FMIN=1 -DHAVE_LOG2=1 -DHAVE_GFORTRAN -DINTERNAL_ARPACK -I${GLPK_HOME}/include -DHAVE_GLPK=1 -DIGRAPH_THREAD_LOCAL= -DPRPACK_IGRAPH_SUPPORT -I. -Icore -Iinclude -Ivendor -DNDEBUG -DNTIMER -DNPRINT -I${LIB_GMP}/include + +PKG_LIBS = -L${LIB_XML}/lib -lxml2 -liconv -lz -lws2_32 -lstdc++ -L${GLPK_HOME}/lib -lglpk -lgmp -lgfortran -L$(LIB_GMP)/lib $(BLAS_LIBS) $(LAPACK_LIBS) -llzma + +OBJECTS=core/centrality/betweenness.o core/centrality/centrality_other.o core/centrality/centralization.o core/centrality/closeness.o core/centrality/coreness.o core/centrality/prpack.o core/centrality/prpack/prpack_base_graph.o core/centrality/prpack/prpack_igraph_graph.o core/centrality/prpack/prpack_preprocessed_ge_graph.o core/centrality/prpack/prpack_preprocessed_gs_graph.o core/centrality/prpack/prpack_preprocessed_scc_graph.o core/centrality/prpack/prpack_preprocessed_schur_graph.o core/centrality/prpack/prpack_result.o core/centrality/prpack/prpack_solver.o core/centrality/prpack/prpack_utils.o core/cliques/cliquer/cliquer.o core/cliques/cliquer/cliquer_graph.o core/cliques/cliquer/reorder.o core/cliques/cliquer_wrapper.o core/cliques/cliques.o core/cliques/glet.o core/cliques/maximal_cliques.o core/community/community_misc.o core/community/edge_betweenness.o core/community/fast_modularity.o core/community/fluid.o core/community/infomap/infomap.o core/community/infomap/infomap_FlowGraph.o core/community/infomap/infomap_Greedy.o core/community/infomap/infomap_Node.o core/community/label_propagation.o core/community/leading_eigenvector.o core/community/leiden.o core/community/louvain.o core/community/modularity.o core/community/optimal_modularity.o core/community/spinglass/NetDataTypes.o core/community/spinglass/NetRoutines.o core/community/spinglass/clustertool.o core/community/spinglass/pottsmodel_2.o core/community/walktrap/walktrap.o core/community/walktrap/walktrap_communities.o core/community/walktrap/walktrap_graph.o core/community/walktrap/walktrap_heap.o core/connectivity/cohesive_blocks.o core/connectivity/components.o core/connectivity/separators.o core/constructors/adjacency.o core/constructors/atlas.o core/constructors/basic_constructors.o core/constructors/de_bruijn.o core/constructors/famous.o core/constructors/full.o core/constructors/kautz.o core/constructors/lcf.o core/constructors/linegraph.o core/constructors/prufer.o core/constructors/regular.o core/core/array.o core/core/buckets.o core/core/cutheap.o core/core/dqueue.o core/core/error.o core/core/estack.o core/core/fixed_vectorlist.o core/core/grid.o core/core/heap.o core/core/indheap.o core/core/interruption.o core/core/marked_queue.o core/core/matrix.o core/core/memory.o core/core/printing.o core/core/progress.o core/core/psumtree.o core/core/set.o core/core/sparsemat.o core/core/spmatrix.o core/core/stack.o core/core/statusbar.o core/core/strvector.o core/core/trie.o core/core/vector.o core/core/vector_ptr.o core/flow/flow.o core/flow/st-cuts.o core/games/barabasi.o core/games/callaway_traits.o core/games/citations.o core/games/correlated.o core/games/degree_sequence.o core/games/degree_sequence_vl/gengraph_box_list.o core/games/degree_sequence_vl/gengraph_degree_sequence.o core/games/degree_sequence_vl/gengraph_graph_molloy_hash.o core/games/degree_sequence_vl/gengraph_graph_molloy_optimized.o core/games/degree_sequence_vl/gengraph_mr-connected.o core/games/degree_sequence_vl/gengraph_powerlaw.o core/games/degree_sequence_vl/gengraph_random.o core/games/dotproduct.o core/games/erdos_renyi.o core/games/establishment.o core/games/forestfire.o core/games/grg.o core/games/growing_random.o core/games/islands.o core/games/k_regular.o core/games/preference.o core/games/recent_degree.o core/games/sbm.o core/games/static_fitness.o core/games/tree.o core/games/watts_strogatz.o core/graph/adjlist.o core/graph/attributes.o core/graph/basic_query.o core/graph/cattributes.o core/graph/iterators.o core/graph/type_indexededgelist.o core/graph/visitors.o core/hrg/hrg.o core/hrg/hrg_types.o core/internal/glpk_support.o core/internal/hacks.o core/internal/lsap.o core/internal/qsort.o core/internal/qsort_r.o core/internal/zeroin.o core/io/dimacs.o core/io/dl-lexer.o core/io/dl-parser.o core/io/dl.o core/io/dot.o core/io/edgelist.o core/io/gml-lexer.o core/io/gml-parser.o core/io/gml-tree.o core/io/gml.o core/io/graphdb.o core/io/graphml.o core/io/leda.o core/io/lgl-lexer.o core/io/lgl-parser.o core/io/lgl.o core/io/ncol-lexer.o core/io/ncol-parser.o core/io/ncol.o core/io/pajek-lexer.o core/io/pajek-parser.o core/io/pajek.o core/isomorphism/bliss.o core/isomorphism/bliss/defs.o core/isomorphism/bliss/graph.o core/isomorphism/bliss/heap.o core/isomorphism/bliss/orbit.o core/isomorphism/bliss/partition.o core/isomorphism/bliss/uintseqhash.o core/isomorphism/bliss/utils.o core/isomorphism/isoclasses.o core/isomorphism/isomorphism_misc.o core/isomorphism/lad.o core/isomorphism/queries.o core/isomorphism/vf2.o core/layout/circular.o core/layout/davidson_harel.o core/layout/drl/DensityGrid.o core/layout/drl/DensityGrid_3d.o core/layout/drl/drl_graph.o core/layout/drl/drl_graph_3d.o core/layout/drl/drl_layout.o core/layout/drl/drl_layout_3d.o core/layout/drl/drl_parse.o core/layout/fruchterman_reingold.o core/layout/gem.o core/layout/graphopt.o core/layout/kamada_kawai.o core/layout/large_graph.o core/layout/layout_bipartite.o core/layout/layout_grid.o core/layout/layout_random.o core/layout/mds.o core/layout/merge_dla.o core/layout/merge_grid.o core/layout/reingold_tilford.o core/layout/sugiyama.o core/linalg/arpack.o core/linalg/blas.o core/linalg/eigen.o core/linalg/lapack.o core/math/bfgs.o core/math/complex.o core/math/utils.o core/misc/bipartite.o core/misc/chordality.o core/misc/cocitation.o core/misc/coloring.o core/misc/conversion.o core/misc/degree_sequence.o core/misc/embedding.o core/misc/feedback_arc_set.o core/misc/graphicality.o core/misc/matching.o core/misc/microscopic_update.o core/misc/mixing.o core/misc/motifs.o core/misc/other.o core/misc/scan.o core/misc/sir.o core/misc/spanning_trees.o core/operators/add_edge.o core/operators/complementer.o core/operators/compose.o core/operators/connect_neighborhood.o core/operators/contract.o core/operators/difference.o core/operators/disjoint_union.o core/operators/intersection.o core/operators/misc_internal.o core/operators/permute.o core/operators/rewire.o core/operators/rewire_edges.o core/operators/simplify.o core/operators/subgraph.o core/operators/union.o core/paths/all_shortest_paths.o core/paths/bellman_ford.o core/paths/dijkstra.o core/paths/distances.o core/paths/eulerian.o core/paths/histogram.o core/paths/johnson.o core/paths/random_walk.o core/paths/shortest_paths.o core/paths/simple_paths.o core/paths/unweighted.o core/properties/basic_properties.o core/properties/constraint.o core/properties/convergence_degree.o core/properties/dag.o core/properties/degrees.o core/properties/girth.o core/properties/loops.o core/properties/multiplicity.o core/properties/neighborhood.o core/properties/spectral.o core/properties/trees.o core/properties/triangles.o core/random/random.o core/scg/scg.o core/scg/scg_approximate_methods.o core/scg/scg_exact_scg.o core/scg/scg_kmeans.o core/scg/scg_optimal_method.o core/scg/scg_utils.o core/version.o vendor/cs/cs_add.o vendor/cs/cs_amd.o vendor/cs/cs_chol.o vendor/cs/cs_cholsol.o vendor/cs/cs_compress.o vendor/cs/cs_counts.o vendor/cs/cs_cumsum.o vendor/cs/cs_dfs.o vendor/cs/cs_dmperm.o vendor/cs/cs_droptol.o vendor/cs/cs_dropzeros.o vendor/cs/cs_dupl.o vendor/cs/cs_entry.o vendor/cs/cs_ereach.o vendor/cs/cs_etree.o vendor/cs/cs_fkeep.o vendor/cs/cs_gaxpy.o vendor/cs/cs_happly.o vendor/cs/cs_house.o vendor/cs/cs_ipvec.o vendor/cs/cs_leaf.o vendor/cs/cs_load.o vendor/cs/cs_lsolve.o vendor/cs/cs_ltsolve.o vendor/cs/cs_lu.o vendor/cs/cs_lusol.o vendor/cs/cs_malloc.o vendor/cs/cs_maxtrans.o vendor/cs/cs_multiply.o vendor/cs/cs_norm.o vendor/cs/cs_permute.o vendor/cs/cs_pinv.o vendor/cs/cs_post.o vendor/cs/cs_print.o vendor/cs/cs_pvec.o vendor/cs/cs_qr.o vendor/cs/cs_qrsol.o vendor/cs/cs_randperm.o vendor/cs/cs_reach.o vendor/cs/cs_scatter.o vendor/cs/cs_scc.o vendor/cs/cs_schol.o vendor/cs/cs_spsolve.o vendor/cs/cs_sqr.o vendor/cs/cs_symperm.o vendor/cs/cs_tdfs.o vendor/cs/cs_transpose.o vendor/cs/cs_updown.o vendor/cs/cs_usolve.o vendor/cs/cs_util.o vendor/cs/cs_utsolve.o vendor/mini-gmp/mini-gmp.o vendor/plfit/gss.o vendor/plfit/hzeta.o vendor/plfit/kolmogorov.o vendor/plfit/lbfgs.o vendor/plfit/mt.o vendor/plfit/options.o vendor/plfit/platform.o vendor/plfit/plfit.o vendor/plfit/plfit_error.o vendor/plfit/rbinom.o vendor/plfit/sampling.o vendor/arpack/dgetv0.o vendor/arpack/dlaqrb.o vendor/arpack/dmout.o vendor/arpack/dnaitr.o vendor/arpack/dnapps.o vendor/arpack/dnaup2.o vendor/arpack/dnaupd.o vendor/arpack/dnconv.o vendor/arpack/dneigh.o vendor/arpack/dneupd.o vendor/arpack/dngets.o vendor/arpack/dsaitr.o vendor/arpack/dsapps.o vendor/arpack/dsaup2.o vendor/arpack/dsaupd.o vendor/arpack/dsconv.o vendor/arpack/dseigt.o vendor/arpack/dsesrt.o vendor/arpack/dseupd.o vendor/arpack/dsgets.o vendor/arpack/dsortc.o vendor/arpack/dsortr.o vendor/arpack/dstatn.o vendor/arpack/dstats.o vendor/arpack/dstqrb.o vendor/arpack/dvout.o vendor/arpack/ivout.o vendor/arpack/second.o vendor/arpack/wrap.o vendor/simpleraytracer/Color.o vendor/simpleraytracer/Light.o vendor/simpleraytracer/Point.o vendor/simpleraytracer/RIgraphRay.o vendor/simpleraytracer/Ray.o vendor/simpleraytracer/RayTracer.o vendor/simpleraytracer/RayVector.o vendor/simpleraytracer/Shape.o vendor/simpleraytracer/Sphere.o vendor/simpleraytracer/Triangle.o vendor/simpleraytracer/unit_limiter.o vendor/uuid/R.o vendor/uuid/clear.o vendor/uuid/compare.o vendor/uuid/copy.o vendor/uuid/gen_uuid.o vendor/uuid/isnull.o vendor/uuid/pack.o vendor/uuid/parse.o vendor/uuid/unpack.o vendor/uuid/unparse.o rinterface.o rinterface_extra.o rrandom.o lazyeval.o diff --git a/src/rigraph/Makevars.win b/src/rigraph/Makevars.win new file mode 100644 index 0000000..b584711 --- /dev/null +++ b/src/rigraph/Makevars.win @@ -0,0 +1,10 @@ +CXX_STD = CXX11 + +LIB_XML ?= $(MINGW_PREFIX) +GLPK_HOME ?= $(MINGW_PREFIX) +LIB_GMP ?= $(MINGW_PREFIX) + +PKG_CPPFLAGS= -I${LIB_XML}/include/libxml2 -I${LIB_XML}/include -DLIBXML_STATIC -DUSING_R -DHAVE_FMEMOPEN=0 -DHAVE_OPEN_MEMSTREAM=0 -DHAVE_RINTF -DHAVE_STRCASECMP -DWin32 -DHAVE_LIBXML=1 -DHAVE_UNISTD_H -Wall -DPACKAGE_VERSION=\"1.3.0.9016\" -DHAVE_FMIN=1 -DHAVE_LOG2=1 -DHAVE_GFORTRAN -DINTERNAL_ARPACK -I${GLPK_HOME}/include -DHAVE_GLPK=1 -DIGRAPH_THREAD_LOCAL= -DPRPACK_IGRAPH_SUPPORT -I. -Icore -Iinclude -Ivendor -DNDEBUG -DNTIMER -DNPRINT -I${LIB_GMP}/include + +PKG_LIBS = -L${LIB_XML}/lib -lxml2 -liconv -lz -lws2_32 -lstdc++ -L${GLPK_HOME}/lib -lglpk -lgmp -lgfortran -L$(LIB_GMP)/lib $(BLAS_LIBS) $(LAPACK_LIBS) +OBJECTS=core/centrality/betweenness.o core/centrality/centrality_other.o core/centrality/centralization.o core/centrality/closeness.o core/centrality/coreness.o core/centrality/prpack.o core/centrality/prpack/prpack_base_graph.o core/centrality/prpack/prpack_igraph_graph.o core/centrality/prpack/prpack_preprocessed_ge_graph.o core/centrality/prpack/prpack_preprocessed_gs_graph.o core/centrality/prpack/prpack_preprocessed_scc_graph.o core/centrality/prpack/prpack_preprocessed_schur_graph.o core/centrality/prpack/prpack_result.o core/centrality/prpack/prpack_solver.o core/centrality/prpack/prpack_utils.o core/cliques/cliquer/cliquer.o core/cliques/cliquer/cliquer_graph.o core/cliques/cliquer/reorder.o core/cliques/cliquer_wrapper.o core/cliques/cliques.o core/cliques/glet.o core/cliques/maximal_cliques.o core/community/community_misc.o core/community/edge_betweenness.o core/community/fast_modularity.o core/community/fluid.o core/community/infomap/infomap.o core/community/infomap/infomap_FlowGraph.o core/community/infomap/infomap_Greedy.o core/community/infomap/infomap_Node.o core/community/label_propagation.o core/community/leading_eigenvector.o core/community/leiden.o core/community/louvain.o core/community/modularity.o core/community/optimal_modularity.o core/community/spinglass/NetDataTypes.o core/community/spinglass/NetRoutines.o core/community/spinglass/clustertool.o core/community/spinglass/pottsmodel_2.o core/community/walktrap/walktrap.o core/community/walktrap/walktrap_communities.o core/community/walktrap/walktrap_graph.o core/community/walktrap/walktrap_heap.o core/connectivity/cohesive_blocks.o core/connectivity/components.o core/connectivity/separators.o core/constructors/adjacency.o core/constructors/atlas.o core/constructors/basic_constructors.o core/constructors/de_bruijn.o core/constructors/famous.o core/constructors/full.o core/constructors/kautz.o core/constructors/lcf.o core/constructors/linegraph.o core/constructors/prufer.o core/constructors/regular.o core/core/array.o core/core/buckets.o core/core/cutheap.o core/core/dqueue.o core/core/error.o core/core/estack.o core/core/fixed_vectorlist.o core/core/grid.o core/core/heap.o core/core/indheap.o core/core/interruption.o core/core/marked_queue.o core/core/matrix.o core/core/memory.o core/core/printing.o core/core/progress.o core/core/psumtree.o core/core/set.o core/core/sparsemat.o core/core/spmatrix.o core/core/stack.o core/core/statusbar.o core/core/strvector.o core/core/trie.o core/core/vector.o core/core/vector_ptr.o core/flow/flow.o core/flow/st-cuts.o core/games/barabasi.o core/games/callaway_traits.o core/games/citations.o core/games/correlated.o core/games/degree_sequence.o core/games/degree_sequence_vl/gengraph_box_list.o core/games/degree_sequence_vl/gengraph_degree_sequence.o core/games/degree_sequence_vl/gengraph_graph_molloy_hash.o core/games/degree_sequence_vl/gengraph_graph_molloy_optimized.o core/games/degree_sequence_vl/gengraph_mr-connected.o core/games/degree_sequence_vl/gengraph_powerlaw.o core/games/degree_sequence_vl/gengraph_random.o core/games/dotproduct.o core/games/erdos_renyi.o core/games/establishment.o core/games/forestfire.o core/games/grg.o core/games/growing_random.o core/games/islands.o core/games/k_regular.o core/games/preference.o core/games/recent_degree.o core/games/sbm.o core/games/static_fitness.o core/games/tree.o core/games/watts_strogatz.o core/graph/adjlist.o core/graph/attributes.o core/graph/basic_query.o core/graph/cattributes.o core/graph/iterators.o core/graph/type_indexededgelist.o core/graph/visitors.o core/hrg/hrg.o core/hrg/hrg_types.o core/internal/glpk_support.o core/internal/hacks.o core/internal/lsap.o core/internal/qsort.o core/internal/qsort_r.o core/internal/zeroin.o core/io/dimacs.o core/io/dl-lexer.o core/io/dl-parser.o core/io/dl.o core/io/dot.o core/io/edgelist.o core/io/gml-lexer.o core/io/gml-parser.o core/io/gml-tree.o core/io/gml.o core/io/graphdb.o core/io/graphml.o core/io/leda.o core/io/lgl-lexer.o core/io/lgl-parser.o core/io/lgl.o core/io/ncol-lexer.o core/io/ncol-parser.o core/io/ncol.o core/io/pajek-lexer.o core/io/pajek-parser.o core/io/pajek.o core/isomorphism/bliss.o core/isomorphism/bliss/defs.o core/isomorphism/bliss/graph.o core/isomorphism/bliss/heap.o core/isomorphism/bliss/orbit.o core/isomorphism/bliss/partition.o core/isomorphism/bliss/uintseqhash.o core/isomorphism/bliss/utils.o core/isomorphism/isoclasses.o core/isomorphism/isomorphism_misc.o core/isomorphism/lad.o core/isomorphism/queries.o core/isomorphism/vf2.o core/layout/circular.o core/layout/davidson_harel.o core/layout/drl/DensityGrid.o core/layout/drl/DensityGrid_3d.o core/layout/drl/drl_graph.o core/layout/drl/drl_graph_3d.o core/layout/drl/drl_layout.o core/layout/drl/drl_layout_3d.o core/layout/drl/drl_parse.o core/layout/fruchterman_reingold.o core/layout/gem.o core/layout/graphopt.o core/layout/kamada_kawai.o core/layout/large_graph.o core/layout/layout_bipartite.o core/layout/layout_grid.o core/layout/layout_random.o core/layout/mds.o core/layout/merge_dla.o core/layout/merge_grid.o core/layout/reingold_tilford.o core/layout/sugiyama.o core/linalg/arpack.o core/linalg/blas.o core/linalg/eigen.o core/linalg/lapack.o core/math/bfgs.o core/math/complex.o core/math/utils.o core/misc/bipartite.o core/misc/chordality.o core/misc/cocitation.o core/misc/coloring.o core/misc/conversion.o core/misc/degree_sequence.o core/misc/embedding.o core/misc/feedback_arc_set.o core/misc/graphicality.o core/misc/matching.o core/misc/microscopic_update.o core/misc/mixing.o core/misc/motifs.o core/misc/other.o core/misc/scan.o core/misc/sir.o core/misc/spanning_trees.o core/operators/add_edge.o core/operators/complementer.o core/operators/compose.o core/operators/connect_neighborhood.o core/operators/contract.o core/operators/difference.o core/operators/disjoint_union.o core/operators/intersection.o core/operators/misc_internal.o core/operators/permute.o core/operators/rewire.o core/operators/rewire_edges.o core/operators/simplify.o core/operators/subgraph.o core/operators/union.o core/paths/all_shortest_paths.o core/paths/bellman_ford.o core/paths/dijkstra.o core/paths/distances.o core/paths/eulerian.o core/paths/histogram.o core/paths/johnson.o core/paths/random_walk.o core/paths/shortest_paths.o core/paths/simple_paths.o core/paths/unweighted.o core/properties/basic_properties.o core/properties/constraint.o core/properties/convergence_degree.o core/properties/dag.o core/properties/degrees.o core/properties/girth.o core/properties/loops.o core/properties/multiplicity.o core/properties/neighborhood.o core/properties/spectral.o core/properties/trees.o core/properties/triangles.o core/random/random.o core/scg/scg.o core/scg/scg_approximate_methods.o core/scg/scg_exact_scg.o core/scg/scg_kmeans.o core/scg/scg_optimal_method.o core/scg/scg_utils.o core/version.o vendor/cs/cs_add.o vendor/cs/cs_amd.o vendor/cs/cs_chol.o vendor/cs/cs_cholsol.o vendor/cs/cs_compress.o vendor/cs/cs_counts.o vendor/cs/cs_cumsum.o vendor/cs/cs_dfs.o vendor/cs/cs_dmperm.o vendor/cs/cs_droptol.o vendor/cs/cs_dropzeros.o vendor/cs/cs_dupl.o vendor/cs/cs_entry.o vendor/cs/cs_ereach.o vendor/cs/cs_etree.o vendor/cs/cs_fkeep.o vendor/cs/cs_gaxpy.o vendor/cs/cs_happly.o vendor/cs/cs_house.o vendor/cs/cs_ipvec.o vendor/cs/cs_leaf.o vendor/cs/cs_load.o vendor/cs/cs_lsolve.o vendor/cs/cs_ltsolve.o vendor/cs/cs_lu.o vendor/cs/cs_lusol.o vendor/cs/cs_malloc.o vendor/cs/cs_maxtrans.o vendor/cs/cs_multiply.o vendor/cs/cs_norm.o vendor/cs/cs_permute.o vendor/cs/cs_pinv.o vendor/cs/cs_post.o vendor/cs/cs_print.o vendor/cs/cs_pvec.o vendor/cs/cs_qr.o vendor/cs/cs_qrsol.o vendor/cs/cs_randperm.o vendor/cs/cs_reach.o vendor/cs/cs_scatter.o vendor/cs/cs_scc.o vendor/cs/cs_schol.o vendor/cs/cs_spsolve.o vendor/cs/cs_sqr.o vendor/cs/cs_symperm.o vendor/cs/cs_tdfs.o vendor/cs/cs_transpose.o vendor/cs/cs_updown.o vendor/cs/cs_usolve.o vendor/cs/cs_util.o vendor/cs/cs_utsolve.o vendor/mini-gmp/mini-gmp.o vendor/plfit/gss.o vendor/plfit/hzeta.o vendor/plfit/kolmogorov.o vendor/plfit/lbfgs.o vendor/plfit/mt.o vendor/plfit/options.o vendor/plfit/platform.o vendor/plfit/plfit.o vendor/plfit/plfit_error.o vendor/plfit/rbinom.o vendor/plfit/sampling.o vendor/arpack/dgetv0.o vendor/arpack/dlaqrb.o vendor/arpack/dmout.o vendor/arpack/dnaitr.o vendor/arpack/dnapps.o vendor/arpack/dnaup2.o vendor/arpack/dnaupd.o vendor/arpack/dnconv.o vendor/arpack/dneigh.o vendor/arpack/dneupd.o vendor/arpack/dngets.o vendor/arpack/dsaitr.o vendor/arpack/dsapps.o vendor/arpack/dsaup2.o vendor/arpack/dsaupd.o vendor/arpack/dsconv.o vendor/arpack/dseigt.o vendor/arpack/dsesrt.o vendor/arpack/dseupd.o vendor/arpack/dsgets.o vendor/arpack/dsortc.o vendor/arpack/dsortr.o vendor/arpack/dstatn.o vendor/arpack/dstats.o vendor/arpack/dstqrb.o vendor/arpack/dvout.o vendor/arpack/ivout.o vendor/arpack/second.o vendor/arpack/wrap.o vendor/simpleraytracer/Color.o vendor/simpleraytracer/Light.o vendor/simpleraytracer/Point.o vendor/simpleraytracer/RIgraphRay.o vendor/simpleraytracer/Ray.o vendor/simpleraytracer/RayTracer.o vendor/simpleraytracer/RayVector.o vendor/simpleraytracer/Shape.o vendor/simpleraytracer/Sphere.o vendor/simpleraytracer/Triangle.o vendor/simpleraytracer/unit_limiter.o vendor/uuid/R.o vendor/uuid/clear.o vendor/uuid/compare.o vendor/uuid/copy.o vendor/uuid/gen_uuid.o vendor/uuid/isnull.o vendor/uuid/pack.o vendor/uuid/parse.o vendor/uuid/unpack.o vendor/uuid/unparse.o rinterface.o rinterface_extra.o rrandom.o lazyeval.o diff --git a/src/rigraph/config.h b/src/rigraph/config.h new file mode 100644 index 0000000..e69de29 diff --git a/src/rigraph/config.h.in b/src/rigraph/config.h.in new file mode 100644 index 0000000..cb269be --- /dev/null +++ b/src/rigraph/config.h.in @@ -0,0 +1,140 @@ +/* src/config.h.in. Generated from configure.ac by autoheader. */ + +/* Define to 1 if you have the `expm1' function. */ +#undef HAVE_EXPM1 + +/* Define to 1 if you have the `finite' function. */ +#undef HAVE_FINITE + +/* Define to 1 if you have the `fmin' function. */ +#undef HAVE_FMIN + +/* Define to 1 if using the GNU Fortran compiler */ +#undef HAVE_GFORTRAN + +/* Define to 1 if you have the GLPK library */ +#undef HAVE_GLPK + +/* Define to 1 if you have the GMP library */ +#undef HAVE_GMP + +/* Define to 1 if you have the header file. */ +#undef HAVE_INTTYPES_H + +/* Define to 1 if you have the `isfinite' function. */ +#undef HAVE_ISFINITE + +/* Define to 1 if you have the libxml2 libraries installed */ +#undef HAVE_LIBXML + +/* Define to 1 if you have the `log1p' function. */ +#undef HAVE_LOG1P + +/* Define to 1 if you have the `log2' function. */ +#undef HAVE_LOG2 + +/* Define to 1 if you have the header file. */ +#undef HAVE_MEMORY_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_NETINET_IN_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_NET_IF_DL_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_NET_IF_H + +/* Define to 1 if you have the `rint' function. */ +#undef HAVE_RINT + +/* Define to 1 if you have the `rintf' function. */ +#undef HAVE_RINTF + +/* Define to 1 if you have the `round' function. */ +#undef HAVE_ROUND + +/* Define if struct sockaddr contains sa_len */ +#undef HAVE_SA_LEN + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDINT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDLIB_H + +/* Define to 1 if you have the `stpcpy' function. */ +#undef HAVE_STPCPY + +/* Define to 1 if you have the `strcasecmp' function. */ +#undef HAVE_STRCASECMP + +/* Define to 1 if you have the `strdup' function. */ +#undef HAVE_STRDUP + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRINGS_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRING_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_FILE_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_IOCTL_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_SOCKET_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_SOCKIO_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_STAT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_TIME_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_TYPES_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_UN_H + +/* Define to 1 if you have the sys/times.h header */ +#undef HAVE_TIMES_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_UNISTD_H + +/* Define to 1 if you have the `_stricmp' function. */ +#undef HAVE__STRICMP + +/* We don't care about thread-local storage in R */ +#undef IGRAPH_THREAD_LOCAL + +/* Define to 1 if you use the vendored mini-GMP library */ +#undef INTERNAL_GMP + +/* Define to the address where bug reports for this package should be sent. */ +#undef PACKAGE_BUGREPORT + +/* Define to the full name of this package. */ +#undef PACKAGE_NAME + +/* Define to the full name and version of this package. */ +#undef PACKAGE_STRING + +/* Define to the one symbol short name of this package. */ +#undef PACKAGE_TARNAME + +/* Define to the home page for this package. */ +#undef PACKAGE_URL + +/* Define to the version of this package. */ +#undef PACKAGE_VERSION + +/* Define to 1 if you have the ANSI C header files. */ +#undef STDC_HEADERS +#define HAVE_ISFINITE HAVE_DECL_ISFINITE diff --git a/src/rigraph/core/centrality/betweenness.c b/src/rigraph/core/centrality/betweenness.c new file mode 100644 index 0000000..8c9f9d8 --- /dev/null +++ b/src/rigraph/core/centrality/betweenness.c @@ -0,0 +1,1016 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2007-2020 The igraph development 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, see . +*/ + +#include "igraph_centrality.h" + +#include "igraph_memory.h" +#include "igraph_adjlist.h" +#include "igraph_interface.h" +#include "igraph_progress.h" +#include "igraph_stack.h" +#include "igraph_dqueue.h" + +#include "core/indheap.h" +#include "core/interruption.h" +#include "core/math.h" + +/***** Vertex betweenness *****/ + +/** + * \ingroup structural + * \function igraph_betweenness + * \brief Betweenness centrality of some vertices. + * + * 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. No edge weight may be NaN. + * Supply a null pointer here for unweighted betweenness. + * \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_cutoff() to + * calculate the range-limited betweenness of the vertices in a graph. + */ +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) { + return igraph_betweenness_cutoff(graph, res, vids, directed, weights, -1); +} + +static int igraph_i_betweenness_cutoff_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_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; + + if (igraph_vector_size(weights) != no_of_edges) { + IGRAPH_ERROR("Weight vector length must agree with number of edges.", IGRAPH_EINVAL); + } + if (no_of_edges > 0) { + igraph_real_t minweight = igraph_vector_min(weights); + if (minweight <= 0) { + IGRAPH_ERROR("Weight vector must be positive.", IGRAPH_EINVAL); + } else if (igraph_is_nan(minweight)) { + IGRAPH_ERROR("Weight vector must not contain NaN values.", 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_LOOPS)); + 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; + + /* Ignore vertices that are more distant than the cutoff */ + if (cutoff >= 0 && mindist > cutoff + 1.0) { + /* Reset variables if node is too distant */ + VECTOR(tmpscore)[minnei] = 0; + VECTOR(dist)[minnei] = 0; + VECTOR(nrgeo)[minnei] = 0; + igraph_vector_int_clear(igraph_adjlist_get(&fathers, minnei)); + continue; + } + + igraph_stack_push(&S, minnei); + + /* 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 && + (altdist <= cutoff + 1.0 || cutoff < 0)) { + /* Only add if the node is not more distant than the cutoff */ + 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]; + } + + /* Reset variables */ + 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; +} + + +/** + * \ingroup structural + * \function igraph_betweenness_cutoff + * \brief Range-limited betweenness centrality. + * + * This function computes a range-limited version of betweenness centrality + * by considering only those shortest paths whose length is no greater + * then the given cutoff value. + * + * \param graph The graph object. + * \param res The result of the computation, a vector containing the + * range-limited betweenness scores for the specified vertices. + * \param vids The vertices for which the range-limited betweenness centrality + * scores will be computed. + * \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. No edge weight may be NaN. + * Supply a null pointer here for unweighted betweenness. + * \param cutoff The maximal length of paths that will be considered. + * If negative, the exact betweenness will be calculated, and + * there will be no upper limit on path lengths. + * \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 \ref igraph_betweenness() to calculate the exact betweenness and + * \ref igraph_edge_betweenness_cutoff() to calculate the range-limited + * edge betweenness. + */ +int igraph_betweenness_cutoff(const igraph_t *graph, igraph_vector_t *res, + const igraph_vs_t vids, igraph_bool_t directed, + const igraph_vector_t *weights, igraph_real_t cutoff) { + + long int no_of_nodes = igraph_vcount(graph); + igraph_dqueue_t q = IGRAPH_DQUEUE_NULL; + long int *distance; + /* Note: nrgeo holds the number of shortest paths, which may be very large in some cases, + * e.g. in a grid graph. If using an integer type, this results in overflow. + * With a 'long long int', overflow already affects the result for a grid as small as 36*36. + * Therefore, we use a 'double' instead. While a 'double' holds fewer digits than a 'long long int', + * i.e. its precision is lower, it is effectively immune to overflow. The impact on the precision + * of the final result is negligible. The max betweenness is correct to 14 decimal digits, + * i.e. the precision limit of 'double', even for a 101*101 grid graph. */ + double *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; + + if (weights) { + return igraph_i_betweenness_cutoff_weighted(graph, res, vids, directed, + cutoff, weights); + } + + 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_LOOPS_ONCE, IGRAPH_MULTIPLE)); + IGRAPH_FINALLY(igraph_adjlist_destroy, &adjlist_out); + IGRAPH_CHECK(igraph_adjlist_init(graph, &adjlist_in, IGRAPH_IN, IGRAPH_LOOPS_ONCE, IGRAPH_MULTIPLE)); + 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_LOOPS_TWICE, IGRAPH_MULTIPLE)); + IGRAPH_FINALLY(igraph_adjlist_destroy, &adjlist_out); + IGRAPH_CHECK(igraph_adjlist_init(graph, &adjlist_in, IGRAPH_ALL, IGRAPH_LOOPS_TWICE, IGRAPH_MULTIPLE)); + 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("Insufficient memory for betweenness calculation.", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, distance); + nrgeo = IGRAPH_CALLOC(no_of_nodes, double); + if (nrgeo == 0) { + IGRAPH_ERROR("Insufficient memory for betweenness calculation.", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, nrgeo); + tmpscore = IGRAPH_CALLOC(no_of_nodes, double); + if (tmpscore == 0) { + IGRAPH_ERROR("Insufficient memory for betweenness calculation.", 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)); + nrgeo[source] = 1; + distance[source] = 1; + + while (!igraph_dqueue_empty(&q)) { + long int actnode = (long int) igraph_dqueue_pop(&q); + + /* Ignore vertices that are more distant than the cutoff */ + if (cutoff >= 0 && distance[actnode] > cutoff + 1) { + /* Reset variables if node is too distant */ + distance[actnode] = 0; + nrgeo[actnode] = 0; + tmpscore[actnode] = 0; + igraph_vector_int_clear(igraph_adjlist_get(adjlist_in_p, actnode)); + continue; + } + + IGRAPH_CHECK(igraph_stack_push(&stack, actnode)); + 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 && + (distance[neighbor] <= cutoff + 1 || cutoff < 0)) { + /* Only add if the node is not more distant than the cutoff */ + igraph_vector_int_t *v = igraph_adjlist_get(adjlist_in_p, + neighbor); + igraph_vector_int_push_back(v, actnode); + nrgeo[neighbor] += 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]; + tmpscore[neighbor] += (tmpscore[actnode] + 1) * nrgeo[neighbor] / nrgeo[actnode]; + } + + if (actnode != source) { + VECTOR(*tmpres)[actnode] += tmpscore[actnode]; + } + + /* Reset variables */ + distance[actnode] = 0; + 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); + IGRAPH_FREE(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; +} + + +/** + * \ingroup structural + * \function igraph_betweenness_estimate + * \brief Estimated betweenness centrality of some vertices. + * + * \deprecated-by igraph_betweenness_cutoff 0.9 + * + * + * 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 negative, the exact betweenness will be calculated, and + * there will be no upper limit on path lengths. + * \param weights An optional vector containing edge weights for + * calculating weighted betweenness. No edge weight may be NaN. + * Supply a null pointer here for unweighted betweenness. + * \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_WARNING("igraph_betweenness_estimate is deprecated, use igraph_betweenness_cutoff."); + return igraph_betweenness_cutoff(graph, res, vids, directed, weights, cutoff); +} + +/***** Edge betweenness *****/ + +static int igraph_i_edge_betweenness_cutoff_weighted( + const igraph_t *graph, + igraph_vector_t *result, + igraph_bool_t directed, + igraph_real_t cutoff, + const igraph_vector_t *weights) { + + 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 must match number of edges.", IGRAPH_EINVAL); + } + if (no_of_edges > 0) { + igraph_real_t minweight = igraph_vector_min(weights); + if (minweight <= 0) { + IGRAPH_ERROR("Weight vector must be positive.", IGRAPH_EINVAL); + } else if (igraph_is_nan(minweight)) { + IGRAPH_ERROR("Weight vector must not contain NaN values.", 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_LOOPS)); + 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_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]); */ + + /* Ignore vertices that are more distant than the cutoff */ + if (cutoff >= 0 && VECTOR(distance)[minnei] > cutoff + 1.0) { + /* Reset variables if node is too distant */ + VECTOR(tmpscore)[minnei] = 0; + VECTOR(distance)[minnei] = 0; + VECTOR(nrgeo)[minnei] = 0; + igraph_vector_int_clear(igraph_inclist_get(&fathers, minnei)); + continue; + } + + igraph_stack_push(&S, minnei); + + 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 && + (altdist <= cutoff + 1.0 || cutoff < 0)) { + /* Only add if the edge is not more distant than the cutoff */ + igraph_vector_int_t *v = igraph_inclist_get(&fathers, to); + /* printf("Found a second SP to %li (from %li)\n", to, minnei); */ + IGRAPH_CHECK(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]; + } + + /* Reset variables */ + 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. + * + * 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. No edge weight may be NaN. 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_cutoff() to + * compute the range-limited betweenness score of the edges in a graph. + */ +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_cutoff(graph, result, directed, + weights, -1); +} + +/** + * \ingroup structural + * \function igraph_edge_betweenness_cutoff + * \brief Range-limited betweenness centrality of the edges. + * + * This function computes a range-limited version of edge betweenness centrality + * by considering only those shortest paths whose length is no greater + * then the given cutoff value. + * + * \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 + * betweenness. No edge weight may be NaN. Supply a null + * pointer here for unweighted betweenness. + * \param cutoff The maximal length of paths that will be considered. + * If negative, the exact betweenness will be calculated (no + * upper limit on path lengths). + * \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 \ref igraph_edge_betweenness() to compute the exact edge betweenness and + * \ref igraph_betweenness_cutoff() to compute the range-limited vertex betweenness. + */ +int igraph_edge_betweenness_cutoff(const igraph_t *graph, igraph_vector_t *result, + igraph_bool_t directed, + const igraph_vector_t *weights, igraph_real_t cutoff) { + 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; + double *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_cutoff_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_LOOPS_ONCE)); + IGRAPH_FINALLY(igraph_inclist_destroy, &elist_out); + IGRAPH_CHECK(igraph_inclist_init(graph, &elist_in, IGRAPH_IN, IGRAPH_LOOPS_ONCE)); + 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_LOOPS_TWICE)); + 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("Insufficient memory for edge betweenness calculation.", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, distance); + nrgeo = IGRAPH_CALLOC(no_of_nodes, double); + if (nrgeo == 0) { + IGRAPH_ERROR("Insufficient memory for edge betweenness calculation.", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, nrgeo); + tmpscore = IGRAPH_CALLOC(no_of_nodes, double); + if (tmpscore == 0) { + IGRAPH_ERROR("Insufficient memory for edge betweenness calculation.", 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(); + + 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 ) { + /* Reset variables if node is too distant */ + distance[actnode] = 0; + tmpscore[actnode] = 0; + nrgeo[actnode] = 0; + continue; + } + + IGRAPH_CHECK(igraph_stack_push(&stack, actnode)); + + /* 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]; + long int neighbor = (long int) IGRAPH_OTHER(graph, edge, actnode); + if (nrgeo[neighbor] != 0) { + /* we've already seen this node, another shortest path? */ + if (distance[neighbor] == distance[actnode] + 1) { + nrgeo[neighbor] += nrgeo[actnode]; + } + } else if (distance[actnode] + 1 <= cutoff || cutoff < 0) { + /* we haven't seen this node yet, but we only consider + * it if it is not more distant than the cutoff. */ + nrgeo[neighbor] += nrgeo[actnode]; + distance[neighbor] = distance[actnode] + 1; + IGRAPH_CHECK(igraph_dqueue_push(&q, 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) { + distance[actnode] = 0; + tmpscore[actnode] = 0; + nrgeo[actnode] = 0; + 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 edgeno = (igraph_integer_t) VECTOR(*neip)[i]; + long int neighbor = (long int) IGRAPH_OTHER(graph, edgeno, actnode); + 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]; + } + } + /* Reset variables */ + distance[actnode] = 0; + tmpscore[actnode] = 0; + nrgeo[actnode] = 0; + } + /* 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_edge_betweenness_estimate + * \brief Estimated betweenness centrality of the edges. + * + * \deprecated-by igraph_edge_betweenness_cutoff 0.9 + * + * + * 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 negative, the exact betweenness will be calculated (no + * upper limit on path lengths). + * \param weights An optional weight vector for weighted betweenness. + * No edge weight may be NaN. 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) { + IGRAPH_WARNING("igraph_edge_betweenness_estimate is deprecated, use igraph_edge_betweenness_cutoff."); + return igraph_edge_betweenness_cutoff(graph, result, directed, weights, cutoff); +} diff --git a/src/rigraph/core/centrality/centrality_other.c b/src/rigraph/core/centrality/centrality_other.c new file mode 100644 index 0000000..a31ac8a --- /dev/null +++ b/src/rigraph/core/centrality/centrality_other.c @@ -0,0 +1,1584 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2007-2021 The igraph development 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, see . +*/ + +#include "igraph_centrality.h" + +#include "igraph_memory.h" +#include "igraph_random.h" +#include "igraph_adjlist.h" +#include "igraph_interface.h" +#include "igraph_progress.h" +#include "igraph_structural.h" +#include "igraph_topology.h" +#include "igraph_stack.h" +#include "igraph_dqueue.h" + +#include "centrality/prpack_internal.h" +#include "core/indheap.h" +#include "core/interruption.h" +#include "core/math.h" + +#include "config.h" + +#include +#include /* memset */ + +static int igraph_i_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, + const igraph_vector_t *reset, + const igraph_vector_t *weights, + igraph_arpack_options_t *options); + +static 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 n = igraph_vector_size(vector); + igraph_real_t mi, ma; + + if (n == 0) { + return 0; + } + + igraph_vector_minmax(vector, &mi, &ma); + + if (mi >= 0) { + return 0; + } + if (ma <= 0) { + return 1; + } + + /* is the most negative value larger in magnitude than the most positive? */ + return (-mi/ma > 1); +} + +static 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; + +static 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 IGRAPH_SUCCESS; +} + +static int igraph_i_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); + } + /* Safe to call minmax, ecount == 0 case was caught earlier */ + 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(°ree, options->n); + IGRAPH_CHECK(igraph_degree(graph, °ree, 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(°ree); + 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_LOOPS_TWICE, IGRAPH_MULTIPLE)); + IGRAPH_FINALLY(igraph_adjlist_destroy, &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; + + data.graph = graph; + data.inclist = &inclist; + data.weights = weights; + + IGRAPH_CHECK(igraph_inclist_init(graph, &inclist, IGRAPH_ALL, IGRAPH_LOOPS_TWICE)); + IGRAPH_FINALLY(igraph_inclist_destroy, &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 IGRAPH_SUCCESS; +} + +/* 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; */ +/* } */ + +static int igraph_i_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"); + } + + /* Safe to call minmax, ecount == 0 case was caught earlier */ + 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_LOOPS_ONCE, IGRAPH_MULTIPLE)); + 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; + + data.graph = graph; + data.inclist = &inclist; + data.weights = weights; + + IGRAPH_CHECK(igraph_inclist_init(graph, &inclist, IGRAPH_IN, IGRAPH_LOOPS_ONCE)); + 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 from high-scoring nodes contribute + * more to the score of the node in question than equal connections from + * low-scoring nodes. Specifically, the eigenvector centrality of each + * vertex is proportional to the sum of eigenvector centralities of its + * neighbors. In practice, the centralities are determined by calculating the + * eigenvector corresponding to the largest positive eigenvalue of the + * adjacency matrix. In the undirected case, this function considers + * the diagonal entries of the adjacency matrix to be \em twice the number of + * self-loops on the corresponding vertex. + * + * + * In the weighted case, the eigenvector centrality of a vertex is proportional + * to the weighted sum of centralities of its neighbours, i.e. + * c_i = sum_j w_ij c_j, where w_ij is the weight + * of the edge connecting vertices \c i and \c j. The weights of parallel edges + * are added up. + * + * + * The centrality scores returned by igraph can be normalized + * (using the \p scale parameter) such that the largest eigenvector centrality + * score is 1 (with one exception, see below). + * + * + * In the directed case, the left eigenvector of the adjacency matrix is + * calculated. In other words, the centrality of a vertex is proportional + * to the sum of centralities of vertices pointing to it. + * + * + * Eigenvector centrality is meaningful only for connected graphs. + * Graphs that are not connected should be decomposed into connected + * components, and the eigenvector centrality calculated for each separately. + * This function does not verify that the graph is connected. If it is not, + * in the undirected case the scores of all but one component will be zeros. + * + * + * 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 may 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 produce + * complex numbers when 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 + * n (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_i_eigenvector_centrality_directed(graph, vector, value, + scale, weights, options); + } else { + return igraph_i_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 */ +static 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 */ +static 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; +} + +static 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); + } + /* Safe to call minmax, ecount == 0 case was caught earlier */ + 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_LOOPS_ONCE, IGRAPH_MULTIPLE)); + IGRAPH_FINALLY(igraph_adjlist_destroy, &myinadjlist); + IGRAPH_CHECK(igraph_adjlist_init(graph, &myoutadjlist, IGRAPH_OUT, IGRAPH_LOOPS_ONCE, IGRAPH_MULTIPLE)); + IGRAPH_FINALLY(igraph_adjlist_destroy, &myoutadjlist); + } else { + IGRAPH_CHECK(igraph_inclist_init(graph, &myininclist, IGRAPH_IN, IGRAPH_LOOPS_ONCE)); + IGRAPH_FINALLY(igraph_inclist_destroy, &myininclist); + IGRAPH_CHECK(igraph_inclist_init(graph, &myoutinclist, IGRAPH_OUT, IGRAPH_LOOPS_ONCE)); + 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 + * \brief Kleinberg's hub scores. + * + * The hub scores of the vertices are defined as the principal + * eigenvector of A*A^T, where A is the adjacency + * matrix of the graph, A^T is its transposed. + * + * 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 + * n (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 + * \brief Kleinerg's authority scores. + * + * The authority scores of the vertices are defined as the principal + * eigenvector of A^T*A, where A is the adjacency + * matrix of the graph, A^T is its transposed. + * + * 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 + * n (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; + +static 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; +} + +static 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; idamping; + } + + /* 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; i1 - damping. + * If the random walker gets stuck in a sink vertex, it will also restart + * from a random vertex. + * + * + * The PageRank centrality is mainly useful for directed graphs. In undirected + * graphs it converges to trivial values proportional to degrees as the damping + * factor approaches 1. + * + * + * Starting from version 0.9, igraph has two PageRank implementations, + * and the user can choose between them. The first implementation is + * \c IGRAPH_PAGERANK_ALGO_ARPACK, based on the ARPACK library. This + * was the default before igraph version 0.7. The second and recommended + * implementation is \c IGRAPH_PAGERANK_ALGO_PRPACK. This is using the + * PRPACK package, see https://github.com/dgleich/prpack . + * + * + * 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. + * + * + * References: + * + * + * 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. + * + * \param graph The graph object. + * \param algo The PageRank implementation to use. Possible values: + * \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). + * Must be a probability in the range [0, 1]. A commonly used value is 0.85. + * \param weights Optional edge weights. May be a \c NULL pointer, + * meaning unweighted edges, or a vector of non-negative values + * of the same length as the number of edges. + * \param options Options for the ARPACK method. See \ref igraph_arpack_options_t + * for details. Note that the function overwrites the n (number + * of vertices), nev (1), ncv (3) and which + * (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_personalized_pagerank() and \ref igraph_personalized_pagerank_vs() + * for the personalized PageRank measure. See \ref igraph_arpack_rssolve() and + * \ref igraph_arpack_rnsolve() for the underlying machinery used by + * \c IGRAPH_PAGERANK_ALGO_ARPACK. + * + * \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, igraph_arpack_options_t *options) { + return igraph_personalized_pagerank(graph, algo, vector, value, vids, + directed, damping, NULL, 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 + * when the random walk is restarted, a new starting vertex is chosen according to + * a specified distribution. + * This distribution is used both when restarting randomly with probability + * 1 - damping, and when the walker is forced to restart due to being + * stuck in a sink vertex (a vertex with no outgoing edges). + * + * + * 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. + * + * + * 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. + * + * + * + * \param graph The graph object. + * \param algo The PageRank implementation to use. Possible values: + * \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). + * Must be a probability in the range [0, 1]. A commonly used value is 0.85. + * \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 for the ARPACK method. See \ref igraph_arpack_options_t + * for details. Note that the function overwrites the n (number + * of vertices), nev (1), ncv (3) and which + * (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. + */ + +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, + igraph_arpack_options_t *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 + * when the random walk is restarted, a new starting vertex is chosen non-uniformly, + * according to the distribution specified in \p reset + * (instead of the uniform distribution in the original PageRank measure). + * The \p reset distribution is used both when restarting randomly with probability + * 1 - damping, and when the walker is forced to restart due to being + * stuck in a sink vertex (a vertex with no outgoing edges). + * + * + * 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. + * + * + * + * \param graph The graph object. + * \param algo The PageRank implementation to use. Possible values: + * \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). + * Must be a probability in the range [0, 1]. A commonly used value is 0.85. + * \param reset The probability distribution over the vertices used when + * resetting the random walk. It is either a \c 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. May be a \c NULL pointer, + * meaning unweighted edges, or a vector of non-negative values + * of the same length as the number of edges. + * \param options Options for the ARPACK method. See \ref igraph_arpack_options_t + * for details. Note that the function overwrites the n (number + * of vertices), nev (1), ncv (3) and which + * (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_personalized_pagerank_vs() for a personalized implementation + * with resetting to specific vertices. + */ +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, + const igraph_vector_t *reset, + const igraph_vector_t *weights, + igraph_arpack_options_t *options) { + + if (damping < 0.0 || damping > 1.0) { + IGRAPH_ERROR("The PageRank damping factor must be in the range [0,1].", IGRAPH_EINVAL); + } + + if (algo == IGRAPH_PAGERANK_ALGO_ARPACK) { + return igraph_i_personalized_pagerank_arpack(graph, vector, value, vids, + directed, damping, reset, + weights, options); + } else if (algo == IGRAPH_PAGERANK_ALGO_PRPACK) { + return igraph_i_personalized_pagerank_prpack(graph, vector, value, vids, + directed, damping, reset, + weights); + } + + IGRAPH_ERROR("Unknown PageRank algorithm", IGRAPH_EINVAL); +} + +/* + * ARPACK-based implementation of \c igraph_personalized_pagerank. + * + * See \c igraph_personalized_pagerank for the documentation of the parameters. + */ +static int igraph_i_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, + const 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; + igraph_vector_t normalized_reset; + + long int i; + long int no_of_nodes = igraph_vcount(graph); + long int no_of_edges = igraph_ecount(graph); + + if (reset && igraph_vector_size(reset) != no_of_nodes) { + IGRAPH_ERROR("Invalid length of reset vector when calculating personalized PageRank scores.", IGRAPH_EINVAL); + } + + if (no_of_edges == 0) { + /* Special case: graph with no edges. Result is the same as the personalization vector. */ + if (value) { + *value = 1.0; + } + if (vector) { + IGRAPH_CHECK(igraph_vector_resize(vector, no_of_nodes)); + if (reset && no_of_nodes > 0) { + for (i=0; i < no_of_nodes; ++i) { + VECTOR(*vector)[i] = VECTOR(*reset)[i]; + } + igraph_vector_scale(vector, 1.0 / igraph_vector_sum(vector)); + } else { + 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); + } + + /* Safe to call minmax, ecount == 0 case was caught earlier */ + IGRAPH_CHECK(igraph_vector_minmax(weights, &min, &max)); + if (igraph_is_nan(min)) { + IGRAPH_ERROR("Weight vector must not contain NaN values.", IGRAPH_EINVAL); + } + if (min == 0 && max == 0) { + /* Special case: all weights are zeros. Result is the same as the personalization vector. */ + if (value) { + *value = 1.0; + } + if (vector) { + IGRAPH_CHECK(igraph_vector_resize(vector, no_of_nodes)); + if (reset) { + for (i=0; i < no_of_nodes; ++i) { + VECTOR(*vector)[i] = VECTOR(*reset)[i]; + } + igraph_vector_scale(vector, 1.0 / igraph_vector_sum(vector)); + } else { + igraph_vector_fill(vector, 1.0 / no_of_nodes); + } + } + return IGRAPH_SUCCESS; + } + } + + 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, reset_min; + reset_min = igraph_vector_min(reset); + if (reset_min < 0) { + IGRAPH_ERROR("The reset vector must not contain negative elements.", IGRAPH_EINVAL); + } + if (igraph_is_nan(reset_min)) { + IGRAPH_ERROR("The reset vector must not contain NaN values.", 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_CHECK(igraph_vector_copy(&normalized_reset, reset)); + IGRAPH_FINALLY(igraph_vector_destroy, &normalized_reset); + + igraph_vector_scale(&normalized_reset, 1.0 / reset_sum); + } + + if (!weights) { + + igraph_adjlist_t adjlist; + igraph_i_pagerank_data_t data; + + data.graph = graph; + data.adjlist = &adjlist; + data.damping = damping; + data.outdegree = &outdegree; + data.tmp = &tmp; + data.reset = reset ? &normalized_reset : NULL; + + IGRAPH_CHECK(igraph_degree(graph, &outdegree, igraph_vss_all(), + directed ? IGRAPH_OUT : IGRAPH_ALL, IGRAPH_LOOPS)); + IGRAPH_CHECK(igraph_degree(graph, &indegree, igraph_vss_all(), + directed ? IGRAPH_IN : IGRAPH_ALL, IGRAPH_LOOPS)); + /* 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_LOOPS, IGRAPH_MULTIPLE + )); + 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; + + data.graph = graph; + data.inclist = &inclist; + data.weights = weights; + data.damping = damping; + data.outdegree = &outdegree; + data.tmp = &tmp; + data.reset = reset ? &normalized_reset : NULL; + + IGRAPH_CHECK(igraph_inclist_init(graph, &inclist, dirmode, IGRAPH_LOOPS)); + 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 during PageRank calculation."); + 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(); + + if (reset) { + igraph_vector_destroy(&normalized_reset); + IGRAPH_FINALLY_CLEAN(1); + } + + 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 IGRAPH_SUCCESS; +} diff --git a/src/rigraph/core/centrality/centralization.c b/src/rigraph/core/centrality/centralization.c new file mode 100644 index 0000000..308e9fd --- /dev/null +++ b/src/rigraph/core/centrality/centralization.c @@ -0,0 +1,658 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2007-2020 The igraph development 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, see . +*/ + +#include "igraph_centrality.h" + +#include "igraph_interface.h" +#include "igraph_vector.h" + +#include "core/math.h" + +/** + * \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. + * + * 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. + * + * 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. + * + * + * 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. + * + * + * There are two ways to call this function, the first is to supply a + * graph as the graph argument, and then the number of + * vertices is taken from this object, and its directedness is + * considered as well. The nodes argument is ignored in + * this case. The mode argument is also ignored if the + * supplied graph is undirected. + * + * + * The other way is to supply a null pointer as the graph + * argument. In this case the nodes and mode + * arguments are considered. + * + * + * 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 + * graph argument is not a null pointer. + * \param mode Constant, whether the calculation is based on in-degree + * (IGRAPH_IN), out-degree (IGRAPH_OUT) + * or total degree (IGRAPH_ALL). This is ignored if + * the graph 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 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_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)); + + 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. + * + * + * There are two ways to call this function, the first is to supply a + * graph as the graph argument, and then the number of + * vertices is taken from this object, and its directedness is + * considered as well. The nodes argument is ignored in + * this case. The directed argument is also ignored if the + * supplied graph is undirected. + * + * + * The other way is to supply a null pointer as the graph + * argument. In this case the nodes and directed + * arguments are considered. + * + * + * 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 + * graph argument is not a null pointer. + * \param directed Boolean scalar, whether to use directed paths in + * the betweenness calculation. This argument is ignored if + * graph 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, NULL, NULL, 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. + * + * + * There are two ways to call this function, the first is to supply a + * graph as the graph argument, and then the number of + * vertices is taken from this object, and its directedness is + * considered as well. The nodes argument is ignored in + * this case. The mode argument is also ignored if the + * supplied graph is undirected. + * + * + * The other way is to supply a null pointer as the graph + * argument. In this case the nodes and mode + * arguments are considered. + * + * + * 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 + * graph argument is not a null pointer. + * \param mode Constant, specifies what kinf of distances to consider + * to calculate closeness. See the mode argument of + * \ref igraph_closeness() for details. This argument is ignored + * if graph 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 + * n (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. + * + * + * There are two ways to call this function, the first is to supply a + * graph as the graph argument, and then the number of + * vertices is taken from this object, and its directedness is + * considered as well. The nodes argument is ignored in + * this case. The directed argument is also ignored if the + * supplied graph is undirected. + * + * + * The other way is to supply a null pointer as the graph + * argument. In this case the nodes and directed + * arguments are considered. + * + * + * 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 + * graph argument is not a null pointer. + * \param directed Boolean scalar, whether to consider edge + * directions. This argument is ignored if + * graph 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/src/rigraph/core/centrality/closeness.c b/src/rigraph/core/centrality/closeness.c new file mode 100644 index 0000000..6435321 --- /dev/null +++ b/src/rigraph/core/centrality/closeness.c @@ -0,0 +1,877 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2007-2020 The igraph development 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, see . +*/ + +#include "igraph_centrality.h" + +#include "igraph_adjlist.h" +#include "igraph_interface.h" +#include "igraph_progress.h" +#include "igraph_dqueue.h" + +#include "core/indheap.h" +#include "core/interruption.h" +#include "core/math.h" + +/***** Closeness centrality *****/ + +/** + * \ingroup structural + * \function igraph_closeness + * \brief Closeness centrality calculations for some vertices. + * + * 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 inverse of the mean distance to (or from) all other vertices. + * + * + * Closeness centrality is meaningful only for connected graphs. + * If the graph is not connected, igraph computes the inverse of the + * mean distance to (or from) all \em reachable vertices. In undirected + * graphs, this is equivalent to computing the closeness separately in + * each connected component. The optional \p all_reachable output + * parameter is provided to help detect when the graph is disconnected. + * + * + * While there is no universally adopted definition of closeness centrality + * for disconnected graphs, there have been some attempts for generalizing + * the concept to the disconnected case. One type of approach considers the mean distance + * only to reachable vertices, then re-scales the obtained certrality score + * by a factor that depends on the number of reachable vertices + * (i.e. the size of the component in the undirected case). + * To facilitate computing these generalizations of closeness centrality, + * the number of reachable vertices (not including the starting vertex) + * is returned in \p reachable_count. + * + * + * In disconnected graphs, consider using the harmonic centrality, + * computable using \ref igraph_harmonic_centrality(). + * + * + * For isolated vertices, i.e. those having no associated paths, NaN is returned. + * + * \param graph The graph object. + * \param res The result of the computation, a vector containing the + * closeness centrality scores for the given vertices. + * \param reachable_count If not \c NULL, this vector will contain the number of + * vertices reachable from each vertex for which the closeness is calculated + * (not including that vertex). + * \param all_reachable Pointer to a Boolean. If not \c NULL, it indicates if all + * vertices of the graph were reachable from each vertex in \p vids. + * If false, the graph is non-connected. If true, and the graph is undirected, + * or if the graph is directed and \p vids contains all vertices, then the + * graph is connected. + * \param vids The vertices for which the closeness centrality 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. No edge weight may be NaN. Supply a null + * pointer here for traditional, unweighted closeness. + * \param normalized If true, the inverse of the mean distance to reachable + * vetices is returned. If false, the inverse of the sum of distances + * is returned. + * \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(), + * \ref igraph_harmonic_centrality(). + * See \ref igraph_closeness_cutoff() for the range-limited closeness centrality. + */ +int igraph_closeness(const igraph_t *graph, igraph_vector_t *res, + igraph_vector_t *reachable_count, igraph_bool_t *all_reachable, + const igraph_vs_t vids, igraph_neimode_t mode, + const igraph_vector_t *weights, + igraph_bool_t normalized) { + return igraph_closeness_cutoff(graph, res, reachable_count, all_reachable, vids, mode, weights, normalized, -1); +} + +static int igraph_i_closeness_cutoff_weighted(const igraph_t *graph, + igraph_vector_t *res, + igraph_vector_t *reachable_count, + igraph_bool_t *all_reachable, + 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. */ + + 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; + + igraph_real_t mindist = 0; + + if (igraph_vector_size(weights) != no_of_edges) { + IGRAPH_ERROR("Invalid weight vector length.", IGRAPH_EINVAL); + } + + if (no_of_edges > 0) { + igraph_real_t minweight = igraph_vector_min(weights); + if (minweight <= 0) { + IGRAPH_ERROR("Weight vector must be positive.", IGRAPH_EINVAL); + } else if (igraph_is_nan(minweight)) { + IGRAPH_ERROR("Weight vector must not contain NaN values.", IGRAPH_EINVAL); + } + } + + IGRAPH_CHECK(igraph_vit_create(graph, vids, &vit)); + IGRAPH_FINALLY(igraph_vit_destroy, &vit); + + nodes_to_calc = IGRAPH_VIT_SIZE(vit); + + if (reachable_count) { + igraph_vector_resize(reachable_count, nodes_to_calc); + } + + if (all_reachable) { + *all_reachable = 1; /* be optimistic */ + } + + 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_LOOPS)); + 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_int_t *neis = igraph_lazy_inclist_get(&inclist, minnei); + long int nlen = igraph_vector_int_size(neis); + + mindist = -igraph_2wheap_delete_max(&Q); + + if (cutoff >= 0 && (mindist - 1.0) > cutoff) { + continue; /* NOT break!!! */ + } + + VECTOR(*res)[i] += (mindist - 1.0); + nodes_reached++; + + 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 (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 (curdist == 0 /* this means curdist is infinity */ || altdist < curdist) { + /* This is a shorter path */ + VECTOR(dist)[to] = altdist; + IGRAPH_CHECK(igraph_2wheap_modify(&Q, to, -altdist)); + } + } + + } /* !igraph_2wheap_empty(&Q) */ + + if (reachable_count) { + VECTOR(*reachable_count)[i] = nodes_reached - 1; + } + + if (normalized) { + /* compute the inverse of the average distance, considering only reachable nodes */ + VECTOR(*res)[i] = VECTOR(*res)[i] == 0 ? IGRAPH_NAN : ((igraph_real_t) (nodes_reached-1)) / VECTOR(*res)[i]; + } else { + /* compute the inverse of the sum of distances */ + VECTOR(*res)[i] = VECTOR(*res)[i] == 0 ? IGRAPH_NAN : 1.0 / VECTOR(*res)[i]; + } + + if (all_reachable) { + if (nodes_reached < no_of_nodes) { + *all_reachable = 0 /* false */; + } + } + } /* !IGRAPH_VIT_END(vit) */ + + 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. + * + * \deprecated-by igraph_closeness_cutoff 0.9 + * + * + * 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. + * + * + * 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. + * + * + * 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 The vertices for which the closeness centrality will be estimated. + * \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 negative, the exact closeness will be calculated (no upper + * limit on path lengths). + * \param weights An optional vector containing edge weights for + * weighted closeness. No edge weight may be NaN. 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) { + IGRAPH_WARNING("igraph_closeness_estimate is deprecated, use igraph_closeness_cutoff."); + return igraph_closeness_cutoff(graph, res, NULL, NULL, vids, mode, weights, normalized, cutoff); +} + + +/** + * \ingroup structural + * \function igraph_closeness_cutoff + * \brief Range limited closeness centrality. + * + * This function computes a range-limited version of closeness centrality + * by considering only those shortest paths whose length is no greater + * then the given cutoff value. + * + * \param graph The graph object. + * \param res The result of the computation, a vector containing the + * range-limited closeness centrality scores for the given vertices. + * \param reachable_count If not \c NULL, this vector will contain the number of + * vertices reachable within the cutoff distance from each vertex for which + * the range-limited closeness is calculated (not including that vertex). + * \param all_reachable Pointer to a Boolean. If not \c NULL, it indicates if all + * vertices of the graph were reachable from each vertex in \p vids within + * the given cutoff distance. + * \param vids The vertices for which the range limited closeness centrality + * 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. No edge weight may be NaN. Supply a null + * pointer here for traditional, unweighted closeness. + * \param normalized If true, the inverse of the mean distance to vertices + * reachable within the cutoff is returned. If false, the inverse + * of the sum of distances is returned. + * \param cutoff The maximal length of paths that will be considered. + * If negative, the exact closeness will be calculated (no upper + * limit on path lengths). + * \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 \ref igraph_closeness() to calculate the exact closeness centrality. + */ + +int igraph_closeness_cutoff(const igraph_t *graph, igraph_vector_t *res, + igraph_vector_t *reachable_count, igraph_bool_t *all_reachable, + const igraph_vs_t vids, igraph_neimode_t mode, + const igraph_vector_t *weights, + igraph_bool_t normalized, + igraph_real_t cutoff) { + + 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; + igraph_adjlist_t allneis; + + long int actdist = 0; + + igraph_dqueue_t q; + + long int nodes_to_calc; + igraph_vit_t vit; + + if (weights) { + return igraph_i_closeness_cutoff_weighted(graph, res, reachable_count, all_reachable, 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 (reachable_count) { + igraph_vector_resize(reachable_count, nodes_to_calc); + } + + if (all_reachable) { + *all_reachable = 1; /* be optimistic */ + } + + if (mode != IGRAPH_OUT && mode != IGRAPH_IN && mode != IGRAPH_ALL) { + IGRAPH_ERROR("Invalid mode for 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_LOOPS, IGRAPH_MULTIPLE)); + 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++) { + nodes_reached = 0; + + igraph_dqueue_clear(&q); + IGRAPH_CHECK(igraph_dqueue_push(&q, IGRAPH_VIT_GET(vit))); + IGRAPH_CHECK(igraph_dqueue_push(&q, 0)); + VECTOR(already_counted)[(long int)IGRAPH_VIT_GET(vit)] = i + 1; + + IGRAPH_PROGRESS("Closeness: ", 100.0 * i / nodes_to_calc, 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); + + if (cutoff >= 0 && actdist > cutoff) { + continue; /* NOT break!!! */ + } + + VECTOR(*res)[i] += actdist; + nodes_reached++; + + /* 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; + IGRAPH_CHECK(igraph_dqueue_push(&q, neighbor)); + IGRAPH_CHECK(igraph_dqueue_push(&q, actdist + 1)); + } + } + + if (reachable_count) { + VECTOR(*reachable_count)[i] = nodes_reached - 1; + } + + if (normalized) { + /* compute the inverse of the average distance, considering only reachable nodes */ + VECTOR(*res)[i] = VECTOR(*res)[i] == 0 ? IGRAPH_NAN : ((igraph_real_t) (nodes_reached-1)) / VECTOR(*res)[i]; + } else { + /* compute the inverse of the sum of distances */ + VECTOR(*res)[i] = VECTOR(*res)[i] == 0 ? IGRAPH_NAN : 1.0 / VECTOR(*res)[i]; + } + + if (all_reachable) { + if (nodes_reached < no_of_nodes) { + *all_reachable = 0 /* false */; + } + } + } + + 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 IGRAPH_SUCCESS; +} + + +/***** Harmonic centrality *****/ + +static int igraph_i_harmonic_centrality_unweighted(const igraph_t *graph, igraph_vector_t *res, + const igraph_vs_t vids, igraph_neimode_t mode, + igraph_bool_t normalized, + igraph_real_t cutoff) { + + long int no_of_nodes = igraph_vcount(graph); + igraph_vector_t already_counted; + igraph_vector_int_t *neis; + long int i, j; + igraph_adjlist_t allneis; + + long int actdist = 0; + + igraph_dqueue_t q; + + long int nodes_to_calc; + igraph_vit_t vit; + + 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("Invalid mode for harmonic centrality.", 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_LOOPS, IGRAPH_MULTIPLE)); + 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++) + { + long int source = IGRAPH_VIT_GET(vit); + + igraph_dqueue_clear(&q); + IGRAPH_CHECK(igraph_dqueue_push(&q, source)); + IGRAPH_CHECK(igraph_dqueue_push(&q, 0)); + VECTOR(already_counted)[source] = i + 1; + + IGRAPH_PROGRESS("Harmonic centrality: ", 100.0 * i / nodes_to_calc, 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); + + if (cutoff >= 0 && actdist > cutoff) { + continue; /* NOT break!!! */ + } + + /* Exclude self-distance, which is zero. */ + if (source != act) { + VECTOR(*res)[i] += 1.0/actdist; + } + + /* 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; + IGRAPH_CHECK(igraph_dqueue_push(&q, neighbor)); + IGRAPH_CHECK(igraph_dqueue_push(&q, actdist + 1)); + } + } + } + + if (normalized && no_of_nodes > 1 /* not a null graph or singleton graph */) { + igraph_vector_scale(res, 1.0 / (no_of_nodes - 1)); + } + + IGRAPH_PROGRESS("Harmonic centrality: ", 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 IGRAPH_SUCCESS; +} + + +static int igraph_i_harmonic_centrality_weighted(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, + igraph_real_t cutoff) { + + /* See igraph_shortest_paths_dijkstra() for the implementation + details and the dirty tricks. */ + + 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; + + igraph_real_t mindist = 0; + + if (igraph_vector_size(weights) != no_of_edges) { + IGRAPH_ERROR("Invalid weight vector length.", IGRAPH_EINVAL); + } + + if (no_of_edges > 0) { + igraph_real_t minweight = igraph_vector_min(weights); + if (minweight <= 0) { + IGRAPH_ERROR("Weight vector must be positive.", IGRAPH_EINVAL); + } else if (igraph_is_nan(minweight)) { + IGRAPH_ERROR("Weight vector must not contain NaN values.", 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_2wheap_init(&Q, no_of_nodes)); + IGRAPH_FINALLY(igraph_2wheap_destroy, &Q); + IGRAPH_CHECK(igraph_lazy_inclist_init(graph, &inclist, mode, IGRAPH_LOOPS)); + 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 */ + + 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_int_t *neis = igraph_lazy_inclist_get(&inclist, minnei); + long int nlen = igraph_vector_int_size(neis); + + mindist = -igraph_2wheap_delete_max(&Q); + + if (cutoff >= 0 && (mindist - 1.0) > cutoff) { + continue; /* NOT break!!! */ + } + + /* Exclude self-distance, which is zero. */ + if (source != minnei) { + VECTOR(*res)[i] += 1.0 / (mindist - 1.0); + } + + 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 (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 (curdist == 0 /* this means curdist is infinity */ || altdist < curdist) { + /* This is a shorter path */ + VECTOR(dist)[to] = altdist; + IGRAPH_CHECK(igraph_2wheap_modify(&Q, to, -altdist)); + } + } + + } /* !igraph_2wheap_empty(&Q) */ + + } /* !IGRAPH_VIT_END(vit) */ + + if (normalized && no_of_nodes > 1 /* not a null graph or singleton graph */) { + igraph_vector_scale(res, 1.0 / (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 IGRAPH_SUCCESS; +} + + +/** + * \ingroup structural + * \function igraph_harmonic_centrality_cutoff + * \brief Range limited harmonic centrality. + * + * This function computes the range limited version of harmonic centrality: + * only those shortest paths are considered whose length is not above the given cutoff. + * The inverse distance to vertices not reachable within the cutoff is considered + * to be zero. + * + * \param graph The graph object. + * \param res The result of the computation, a vector containing the + * range limited harmonic centrality scores for the given vertices. + * \param vids The vertices for which the harmonic centrality 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 harmonic centrality. No edge weight may be NaN. + * If \c NULL, all weights are considered to be one. + * \param normalized Boolean, whether to normalize the result. If true, + * the result is the mean inverse path length to other vertices. + * i.e. it is normalized by the number of vertices minus one. + * If false, the result is the sum of inverse path lengths to other + * vertices. + * \param cutoff The maximal length of paths that will be considered. + * The inverse distance to vertices that are not reachable within + * the cutoff path length is considered to be zero. + * Supply a negative value to compute the exact harmonic centrality, + * without any upper limit on the length of paths. + * \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|), where + * 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_closeness(), \ref igraph_betweenness(). + */ + +int igraph_harmonic_centrality_cutoff(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, + igraph_real_t cutoff) { + if (weights) { + return igraph_i_harmonic_centrality_weighted(graph, res, vids, mode, weights, normalized, cutoff); + } else { + return igraph_i_harmonic_centrality_unweighted(graph, res, vids, mode, normalized, cutoff); + } +} + + +/** + * \ingroup structural + * \function igraph_harmonic_centrality + * \brief Harmonic centrality for some vertices. + * + * The harmonic centrality of a vertex is the mean inverse distance to + * all other vertices. The inverse distance to an unreachable vertex + * is considered to be zero. + * + * + * References: + * + * + * M. Marchiori and V. Latora, Harmony in the small-world, Physica A 285, pp. 539-546 (2000). + * https://doi.org/10.1016/S0378-4371%2800%2900311-3 + * + * + * Y. Rochat, Closeness Centrality Extended to Unconnected Graphs: the Harmonic Centrality Index, ASNA 2009. + * https://infoscience.epfl.ch/record/200525 + * + * + * S. Vigna and P. Boldi, Axioms for Centrality, Internet Mathematics 10, (2014). + * https://doi.org/10.1080/15427951.2013.865686 + * + * \param graph The graph object. + * \param res The result of the computation, a vector containing the + * harmonic centrality scores for the given vertices. + * \param vids The vertices for which the harmonic centrality 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 harmonic centrality. No edge weight may be NaN. + * If \c NULL, all weights are considered to be one. + * \param normalized Boolean, whether to normalize the result. If true, + * the result is the mean inverse path length to other vertices, + * i.e. it is normalized by the number of vertices minus one. + * If false, the result is the sum of inverse path lengths to other + * vertices. + * \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|), where + * n is the numberof vertices for which the calculation is done and + * |E| is the number of edges in the graph. + * + * \sa Other centrality types: \ref igraph_closeness(), \ref igraph_degree(), \ref igraph_betweenness(). + */ + +int igraph_harmonic_centrality(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_harmonic_centrality_cutoff(graph, res, vids, mode, weights, normalized, /* cutoff= */ -1); +} diff --git a/src/rigraph/core/centrality/coreness.c b/src/rigraph/core/centrality/coreness.c new file mode 100644 index 0000000..28ac504 --- /dev/null +++ b/src/rigraph/core/centrality/coreness.c @@ -0,0 +1,164 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2006-2012 Gabor Csardi + 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" + +/** + * \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. + * + * + * 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; + } + + if (no_of_nodes == 0) { + igraph_vector_clear(cores); + return IGRAPH_SUCCESS; + } + + 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/src/rigraph/core/centrality/prpack.cpp b/src/rigraph/core/centrality/prpack.cpp new file mode 100644 index 0000000..ca109ca --- /dev/null +++ b/src/rigraph/core/centrality/prpack.cpp @@ -0,0 +1,124 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2007-2021 The igraph development 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, see . +*/ + +#include "igraph_error.h" + +#include "centrality/prpack_internal.h" +#include "centrality/prpack/prpack_igraph_graph.h" +#include "centrality/prpack/prpack_solver.h" +#include "core/exceptions.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_i_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, + const 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 = nullptr; + double *v = nullptr; + const prpack_result *res; + + IGRAPH_HANDLE_EXCEPTIONS( + if (reset) { + if (igraph_vector_size(reset) != no_of_nodes) { + IGRAPH_ERROR("Invalid length of reset vector when calculating personalized PageRank scores.", IGRAPH_EINVAL); + } + + /* Normalize reset vector so the sum is 1 */ + double reset_min = igraph_vector_min(reset); + if (reset_min < 0) { + IGRAPH_ERROR("The reset vector must not contain negative elements.", IGRAPH_EINVAL); + } + if (igraph_is_nan(reset_min)) { + IGRAPH_ERROR("The reset vector must not contain NaN values.", IGRAPH_EINVAL); + } + + double 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); + } + + // 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; + } + + // u is the distribution used when restarting the walk due to being stuck in a sink + // v is the distribution used when restarting due to damping + // Here we use the same distribution for both + u = v; + } + + // Since PRPACK uses the algebraic method to solve PageRank, damping factors very close to 1.0 + // may lead to numerical instability, the apperance of non-finite values, or the iteration + // never terminating. + if (damping > 0.999) { + IGRAPH_WARNINGF( + "Damping factor is %g. " + "Damping values close to 1 may lead to numerical instability when using PRPACK.", + damping); + } + + // 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 + 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/src/rigraph/core/centrality/prpack/prpack.h b/src/rigraph/core/centrality/prpack/prpack.h new file mode 100644 index 0000000..bcddf37 --- /dev/null +++ b/src/rigraph/core/centrality/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/src/rigraph/core/centrality/prpack/prpack_base_graph.cpp b/src/rigraph/core/centrality/prpack/prpack_base_graph.cpp new file mode 100644 index 0000000..b78c6fe --- /dev/null +++ b/src/rigraph/core/centrality/prpack/prpack_base_graph.cpp @@ -0,0 +1,341 @@ +#include "prpack_base_graph.h" +#include "prpack_utils.h" +#include +#include +#include +#include +#include +#include +#include +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::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) { + (void)g; // to silence an unused argument warning + initialize(); + throw std::runtime_error("not implemented yet"); +} + +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; + int retval = fscanf(f, "%d %lf %d", &num_vs, &ignore, &num_es); + if (retval != 3) { + throw std::runtime_error("error while parsing smat file"); + } + // 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) { + retval = fscanf(f, "%d %d %lf", &hs[i], &ts[i], &((weighted) ? vs[i] : ignore)); + if (retval != 3) { + throw std::runtime_error("error while parsing smat file"); + } + ++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 > 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) { + int retval = fscanf(f, "%d", &num_vs); + if (retval != 1) { + throw std::runtime_error("error while parsing ascii file"); + } + while (getc(f) != '\n'); + vector* al = new vector[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* 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 rowsums(num_vs,0.); + // the graph is in a compressed in-edge list. + for (int i=0; i +#include + +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* edges); + // destructor + ~prpack_base_graph(); + // operations + void normalize_weights(); + }; + +} + +#endif diff --git a/src/rigraph/core/centrality/prpack/prpack_csc.h b/src/rigraph/core/centrality/prpack/prpack_csc.h new file mode 100644 index 0000000..91f352d --- /dev/null +++ b/src/rigraph/core/centrality/prpack/prpack_csc.h @@ -0,0 +1,30 @@ +#ifndef PRPACK_CSC +#define PRPACK_CSC + +#if !defined(_MSC_VER) && !defined (__MINGW32__) && !defined (__MINGW64__) +# include +#else +# include +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/src/rigraph/core/centrality/prpack/prpack_csr.h b/src/rigraph/core/centrality/prpack/prpack_csr.h new file mode 100644 index 0000000..3c8c7b7 --- /dev/null +++ b/src/rigraph/core/centrality/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/src/rigraph/core/centrality/prpack/prpack_edge_list.h b/src/rigraph/core/centrality/prpack/prpack_edge_list.h new file mode 100644 index 0000000..a84466f --- /dev/null +++ b/src/rigraph/core/centrality/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/src/rigraph/core/centrality/prpack/prpack_igraph_graph.cpp b/src/rigraph/core/centrality/prpack/prpack_igraph_graph.cpp new file mode 100644 index 0000000..6554936 --- /dev/null +++ b/src/rigraph/core/centrality/prpack/prpack_igraph_graph.cpp @@ -0,0 +1,147 @@ +#include "prpack_igraph_graph.h" +#include +#include + +#include "igraph_interface.h" + +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, + bool 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 = 0; + + // 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/src/rigraph/core/centrality/prpack/prpack_igraph_graph.h b/src/rigraph/core/centrality/prpack/prpack_igraph_graph.h new file mode 100644 index 0000000..1bab84d --- /dev/null +++ b/src/rigraph/core/centrality/prpack/prpack_igraph_graph.h @@ -0,0 +1,28 @@ +#ifndef PRPACK_IGRAPH_GRAPH +#define PRPACK_IGRAPH_GRAPH + +#ifdef PRPACK_IGRAPH_SUPPORT + +#include "prpack_base_graph.h" + +struct igraph_s; +struct igraph_vector_t; + +namespace prpack { + + class prpack_igraph_graph : public prpack_base_graph { + + public: + // constructors + explicit prpack_igraph_graph(const struct igraph_s* g, + const struct igraph_vector_t* weights = 0, + bool directed = true); + }; + +} + +// PRPACK_IGRAPH_SUPPORT +#endif + +// PRPACK_IGRAPH_GRAPH +#endif diff --git a/src/rigraph/core/centrality/prpack/prpack_preprocessed_ge_graph.cpp b/src/rigraph/core/centrality/prpack/prpack_preprocessed_ge_graph.cpp new file mode 100644 index 0000000..303e30d --- /dev/null +++ b/src/rigraph/core/centrality/prpack/prpack_preprocessed_ge_graph.cpp @@ -0,0 +1,65 @@ +#include "prpack_preprocessed_ge_graph.h" +#include +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) { + matrix[inum_vs + bg->heads[j]] += bg->vals[j]; + d[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/src/rigraph/core/centrality/prpack/prpack_preprocessed_ge_graph.h b/src/rigraph/core/centrality/prpack/prpack_preprocessed_ge_graph.h new file mode 100644 index 0000000..678568f --- /dev/null +++ b/src/rigraph/core/centrality/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/src/rigraph/core/centrality/prpack/prpack_preprocessed_graph.h b/src/rigraph/core/centrality/prpack/prpack_preprocessed_graph.h new file mode 100644 index 0000000..4f5a318 --- /dev/null +++ b/src/rigraph/core/centrality/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/src/rigraph/core/centrality/prpack/prpack_preprocessed_gs_graph.cpp b/src/rigraph/core/centrality/prpack/prpack_preprocessed_gs_graph.cpp new file mode 100644 index 0000000..2cefd5d --- /dev/null +++ b/src/rigraph/core/centrality/prpack/prpack_preprocessed_gs_graph.cpp @@ -0,0 +1,80 @@ +#include "prpack_preprocessed_gs_graph.h" +#include +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/src/rigraph/core/centrality/prpack/prpack_preprocessed_gs_graph.h b/src/rigraph/core/centrality/prpack/prpack_preprocessed_gs_graph.h new file mode 100644 index 0000000..eec3fe2 --- /dev/null +++ b/src/rigraph/core/centrality/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/src/rigraph/core/centrality/prpack/prpack_preprocessed_scc_graph.cpp b/src/rigraph/core/centrality/prpack/prpack_preprocessed_scc_graph.cpp new file mode 100644 index 0000000..a34ad7e --- /dev/null +++ b/src/rigraph/core/centrality/prpack/prpack_preprocessed_scc_graph.cpp @@ -0,0 +1,201 @@ +#include "prpack_preprocessed_scc_graph.h" +#include +#include +#include +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/src/rigraph/core/centrality/prpack/prpack_preprocessed_scc_graph.h b/src/rigraph/core/centrality/prpack/prpack_preprocessed_scc_graph.h new file mode 100644 index 0000000..6584c3f --- /dev/null +++ b/src/rigraph/core/centrality/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/src/rigraph/core/centrality/prpack/prpack_preprocessed_schur_graph.cpp b/src/rigraph/core/centrality/prpack/prpack_preprocessed_schur_graph.cpp new file mode 100644 index 0000000..2a422d0 --- /dev/null +++ b/src/rigraph/core/centrality/prpack/prpack_preprocessed_schur_graph.cpp @@ -0,0 +1,120 @@ +#include "prpack_preprocessed_schur_graph.h" +#include +#include +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/src/rigraph/core/centrality/prpack/prpack_preprocessed_schur_graph.h b/src/rigraph/core/centrality/prpack/prpack_preprocessed_schur_graph.h new file mode 100644 index 0000000..a0593b1 --- /dev/null +++ b/src/rigraph/core/centrality/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/src/rigraph/core/centrality/prpack/prpack_result.cpp b/src/rigraph/core/centrality/prpack/prpack_result.cpp new file mode 100644 index 0000000..1fef884 --- /dev/null +++ b/src/rigraph/core/centrality/prpack/prpack_result.cpp @@ -0,0 +1,11 @@ +#include "prpack_result.h" +#include +using namespace prpack; + +prpack_result::prpack_result() { + x = NULL; +} + +prpack_result::~prpack_result() { + delete[] x; +} diff --git a/src/rigraph/core/centrality/prpack/prpack_result.h b/src/rigraph/core/centrality/prpack/prpack_result.h new file mode 100644 index 0000000..87da0f7 --- /dev/null +++ b/src/rigraph/core/centrality/prpack/prpack_result.h @@ -0,0 +1,29 @@ +#ifndef PRPACK_RESULT +#define PRPACK_RESULT + +#include + +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; + std::string method; + int converged; + // constructor + prpack_result(); + // destructor + ~prpack_result(); + }; + +} + +#endif diff --git a/src/rigraph/core/centrality/prpack/prpack_solver.cpp b/src/rigraph/core/centrality/prpack/prpack_solver.cpp new file mode 100644 index 0000000..b1de4af --- /dev/null +++ b/src/rigraph/core/centrality/prpack/prpack_solver.cpp @@ -0,0 +1,884 @@ +#include "prpack_solver.h" +#include "prpack_utils.h" +#include +#include +#include +#include +#include +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 { + throw invalid_argument("Unknown method specified for PRPACK: '" + m + "'."); + } + ret->method = m; + 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; +#ifdef _OPENMP + #pragma omp parallel for firstprivate(c) reduction(+:err, num_es_touched) schedule(dynamic, 64) +#endif + 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; +#ifdef _OPENMP + #pragma omp parallel for reduction(+:num_es_touched) schedule(dynamic, 64) +#endif + 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 +#ifdef _OPENMP + #pragma omp parallel for firstprivate(c) reduction(+:err, num_es_touched) schedule(dynamic, 64) +#endif + 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/src/rigraph/core/centrality/prpack/prpack_solver.h b/src/rigraph/core/centrality/prpack/prpack_solver.h new file mode 100644 index 0000000..c89d4f3 --- /dev/null +++ b/src/rigraph/core/centrality/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/src/rigraph/core/centrality/prpack/prpack_utils.cpp b/src/rigraph/core/centrality/prpack/prpack_utils.cpp new file mode 100644 index 0000000..1d425b1 --- /dev/null +++ b/src/rigraph/core/centrality/prpack/prpack_utils.cpp @@ -0,0 +1,59 @@ +/** + * @file prpack_utils.cpp + * An assortment of utility functions for reporting errors, checking time, + * and working with vectors. + */ + +#include +#include "prpack_utils.h" +#include +#include +#include +using namespace prpack; +using namespace std; + +#ifdef PRPACK_IGRAPH_SUPPORT +#include "igraph_error.h" +#endif + +#if defined(_WIN32) +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#include +#endif +double prpack_utils::get_time() { + LARGE_INTEGER t, freq; + QueryPerformanceCounter(&t); + QueryPerformanceFrequency(&freq); + return double(t.QuadPart)/double(freq.QuadPart); +} +#else +#include +#include +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", IGRAPH_FILE_BASENAME, __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/src/rigraph/core/centrality/prpack/prpack_utils.h b/src/rigraph/core/centrality/prpack/prpack_utils.h new file mode 100644 index 0000000..261370d --- /dev/null +++ b/src/rigraph/core/centrality/prpack/prpack_utils.h @@ -0,0 +1,33 @@ +#ifndef PRPACK_UTILS +#define PRPACK_UTILS +#ifdef MATLAB_MEX_FILE +#include "mex.h" +#endif +#include + +// 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/src/rigraph/core/centrality/prpack_internal.h b/src/rigraph/core/centrality/prpack_internal.h new file mode 100644 index 0000000..01a85f6 --- /dev/null +++ b/src/rigraph/core/centrality/prpack_internal.h @@ -0,0 +1,44 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2007-2012 Gabor Csardi + 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 + +#include "igraph_decls.h" +#include "igraph_types.h" +#include "igraph_datatype.h" +#include "igraph_iterators.h" + +#include "igraph_interface.h" + +__BEGIN_DECLS + +int igraph_i_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, + const igraph_vector_t *reset, + const igraph_vector_t *weights); + +__END_DECLS + +#endif diff --git a/src/rigraph/core/cliques/cliquer/cliquer.c b/src/rigraph/core/cliques/cliquer/cliquer.c new file mode 100644 index 0000000..2dd08b9 --- /dev/null +++ b/src/rigraph/core/cliques/cliquer/cliquer.c @@ -0,0 +1,1768 @@ + +/* + * 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 +#include +#include +/* +#include +#include +#include +*/ + +#include "cliquer.h" + +#include "config.h" + +/* 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 CLIQUER_LARGE_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; jtime_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 CLIQUER_LARGE_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; + CLIQUER_LARGE_INT r; + CLIQUER_LARGE_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; jtime_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 CLIQUER_LARGE_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 *newtable; + int *p1, *p2; + CLIQUER_LARGE_INT n; + CLIQUER_LARGE_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; jweights[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; jweights[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) { + IGRAPH_FATAL("CLIQUER INTERNAL ERROR: clique_list_count has negative value! Please report as a bug."); + } + 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; jtime_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(). + */ +CLIQUER_LARGE_INT clique_unweighted_find_all(graph_t *g, int min_size, int max_size, + boolean maximal, clique_options *opts) { + int i; + int *table; + CLIQUER_LARGE_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; itime_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; + CLIQUER_LARGE_INT r; + + 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--; + r=clique_unweighted_find_all(g,min_weight,max_weight,maximal, + opts); + ENTRANCE_RESTORE(); + return r; + } + + /* 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 || ioutput (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 + +/* This is an igraph-specific modification to cliquer. + * We use a 64-bit CLIQUER_LARGE_INT (even on 32-bit systems) in places + * which are prone to overflow. Since cliquer indicates interruption by + * returning -1 times the clique count, the effect of overflow is that + * it returns a partial (i.e. incorrect) result without warning. */ +#include +#ifndef CLIQUER_LARGE_INT +#define CLIQUER_LARGE_INT int64_t +#endif + +#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 CLIQUER_LARGE_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/src/rigraph/core/cliques/cliquer/cliquer_graph.c b/src/rigraph/core/cliques/cliquer/cliquer_graph.c new file mode 100644 index 0000000..c9284fd --- /dev/null +++ b/src/rigraph/core/cliques/cliquer/cliquer_graph.c @@ -0,0 +1,765 @@ + +/* + * 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 +#include +#include +#include "graph.h" + + +/* +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; iweights[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= 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=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; in; i++) + g->edges[i]=set_new(g->n); + g->weights=calloc(g->n,sizeof(int)); + for (i=0; in; 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)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; jn <= 0) { + free(g); + fprintf(stderr,"Unexpected end of file when reading graph.\n"); + return NULL; + } + + return g; +} +#endif + + +#if 0 +/* + * 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; +} + + +/* + * 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 (weightweights[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) + 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; +} + +#endif diff --git a/src/rigraph/core/cliques/cliquer/cliquerconf.h b/src/rigraph/core/cliques/cliquer/cliquerconf.h new file mode 100644 index 0000000..47d923b --- /dev/null +++ b/src/rigraph/core/cliques/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 , 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/src/rigraph/core/cliques/cliquer/graph.h b/src/rigraph/core/cliques/cliquer/graph.h new file mode 100644 index 0000000..56f92af --- /dev/null +++ b/src/rigraph/core/cliques/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) { + unsigned int i,j; + int count=0; + setelement e; + + for (i=0; iweights[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/src/rigraph/core/cliques/cliquer/misc.h b/src/rigraph/core/cliques/cliquer/misc.h new file mode 100644 index 0000000..618b5c1 --- /dev/null +++ b/src/rigraph/core/cliques/cliquer/misc.h @@ -0,0 +1,59 @@ + +#ifndef CLIQUER_MISC_H +#define CLIQUER_MISC_H + +#include "igraph_error.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 +#include + +#ifndef ASSERT +#define ASSERT IGRAPH_ASSERT +#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/src/rigraph/core/cliques/cliquer/reorder.c b/src/rigraph/core/cliques/cliquer/reorder.c new file mode 100644 index 0000000..15d197a --- /dev/null +++ b/src/rigraph/core/cliques/cliquer/reorder.c @@ -0,0 +1,424 @@ + +/* + * 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 + +#include + +#include + + +/* + * 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>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; in; i++) { + reorder_set(g->edges[i],order); + tmp_e[order[i]]=g->edges[i]; + tmp_w[order[i]]=g->weights[i]; + } + for (i=0; in; 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 {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) { + free(used); + return FALSE; + } + if (used[order[i]]) { + free(used); + return FALSE; + } + used[order[i]]=TRUE; + } + for (i=0; in); +} + +/* + * 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/src/rigraph/core/cliques/cliquer/reorder.h b/src/rigraph/core/cliques/cliquer/reorder.h new file mode 100644 index 0000000..5c06d31 --- /dev/null +++ b/src/rigraph/core/cliques/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/src/rigraph/core/cliques/cliquer/set.h b/src/rigraph/core/cliques/cliquer/set.h new file mode 100644 index 0000000..9727d30 --- /dev/null +++ b/src/rigraph/core/cliques/cliquer/set.h @@ -0,0 +1,386 @@ + +/* + * 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 +#include +#include +#include +#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)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, unsigned int size) { + unsigned 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)=0) { + * // i is in set s + * } + */ +UNUSED_FUNCTION INLINE +static int set_return_next(set_t s, unsigned int n) { + 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; in) { + 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; + + IGRAPH_UNUSED(g); + + 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; + + IGRAPH_UNUSED(g); + + 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_ERRORF("Maximum clique size (%" IGRAPH_PRId ") must not be " + "smaller than minimum clique size (%" IGRAPH_PRId ").", + IGRAPH_EINVAL, max_size, min_size); + } + + 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; + + IGRAPH_UNUSED(g); + + 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/src/rigraph/core/cliques/cliques.c b/src/rigraph/core/cliques/cliques.c new file mode 100644 index 0000000..23ce9ae --- /dev/null +++ b/src/rigraph/core/cliques/cliques.c @@ -0,0 +1,1139 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2005-2012 Gabor Csardi + 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_error.h" +#include "igraph_memory.h" +#include "igraph_constants.h" +#include "igraph_adjlist.h" +#include "igraph_interface.h" +#include "igraph_progress.h" +#include "igraph_stack.h" + +#include "cliques/cliquer_internal.h" +#include "core/interruption.h" +#include "core/set.h" + +#include /* memset */ + +static 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); +} + +static 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 (*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 we have 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. + */ +static 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 Finds all or some cliques in a graph. + * + * + * Cliques are fully connected subgraphs of a graph. + * + * + * If you are only interested in the size of the largest clique in the graph, + * use \ref igraph_clique_number() instead. + * + * 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 res Pointer to a pointer vector, the result will be stored + * here, i.e. \p res will contain pointers to \ref 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: Exponential + * + * \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 Counts cliques of each size in the graph. + * + * + * Cliques are fully connected subgraphs of a graph. + * + * 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 \p 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. + * + * + * 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 a pointer to an \ref igraph_vector_t. 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(). + * + * 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 \ref 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 Finds all cliques in a given weight range in a vertex weighted graph. + * + * + * 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. + * + * 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. You may pass \c NULL + * here to make each vertex have a weight of 1. + * \param res Pointer to a pointer vector, the result will be stored + * here, i.e. \p res will contain pointers to \ref 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) { + if (vertex_weights) { + return igraph_i_weighted_cliques(graph, vertex_weights, res, min_weight, max_weight, maximal); + } else if (maximal) { + return igraph_maximal_cliques(graph, res, min_weight, max_weight); + } else { + return igraph_cliques(graph, res, min_weight, max_weight); + } +} + + +/** + * \function igraph_largest_weighted_cliques + * \brief Finds the largest weight clique(s) in a graph. + * + * + * Finds the clique(s) having the largest weight in the graph. + * + * 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. You may pass \c NULL + * here to make each vertex have a weight of 1. + * \param res Pointer to a pointer vector, the result will be stored + * here, i.e. \p res will contain pointers to \ref 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) { + if (vertex_weights) { + return igraph_i_largest_weighted_cliques(graph, vertex_weights, res); + } else { + return igraph_largest_cliques(graph, res); + } +} + + +/** + * \function igraph_weighted_clique_number + * \brief Finds the weight of the largest weight clique in the graph. + * + * 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. You may pass \c NULL + * here to make each vertex have a weight of 1. + * \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) { + if (vertex_weights) { + return igraph_i_weighted_clique_number(graph, vertex_weights, res); + } else { + igraph_integer_t res_int; + IGRAPH_CHECK(igraph_clique_number(graph, &res_int)); + if (res) { + *res = res_int; + } + return IGRAPH_SUCCESS; + } +} + +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; + +static 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 Finds all independent vertex sets in a graph. + * + * + * A vertex set is considered independent if there are no edges between + * them. + * + * + * If you are interested in the size of the largest independent vertex set, + * use \ref igraph_independence_number() instead. + * + * + * 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, i.e. \p res will contain pointers to \ref 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. + * + * + * An independent vertex set is largest if there is no other + * independent vertex set with more vertices in the graph. + * + * + * 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; + +static 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; +} + +static 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 Finds all maximal independent vertex sets of a graph. + * + * + * A maximal independent vertex set is an independent vertex set which + * can't be extended any more by adding a new vertex to it. + * + * + * 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. + * + * + * 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. + * + * + * 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, i.e. \p res will contain pointers to \ref 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_LOOPS_TWICE, IGRAPH_MULTIPLE + )); + 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 Finds the independence number of the graph. + * + * + * The independence number of a graph is the cardinality of the largest + * independent vertex set. + * + * + * 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_LOOPS_TWICE, IGRAPH_MULTIPLE + )); + 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 */ +/*************************************************************************/ + +static igraph_bool_t igraph_i_maximal_cliques_store_max_size(igraph_vector_t* clique, void* data) { + igraph_integer_t* result = (igraph_integer_t*)data; + if (*result < igraph_vector_size(clique)) { + *result = (igraph_integer_t) igraph_vector_size(clique); + } + igraph_vector_destroy(clique); + igraph_Free(clique); + return 1; +} + +static igraph_bool_t igraph_i_largest_cliques_store(igraph_vector_t* clique, void* data) { + igraph_vector_ptr_t* result = (igraph_vector_ptr_t*)data; + long int i, n; + + /* 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])) { + igraph_vector_destroy(clique); + igraph_Free(clique); + return 1; + } + + 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); + } + } + + IGRAPH_CHECK(igraph_vector_ptr_push_back(result, clique)); + + return 1; +} + +/** + * \function igraph_largest_cliques + * \brief Finds the largest clique(s) in a graph. + * + * + * A clique is largest (quite intuitively) if there is no other clique + * in the graph which contains more vertices. + * + * + * Note that this is not necessarily the same as a maximal clique, + * i.e. the largest cliques are always maximal but a maximal clique is + * not always largest. + * + * The current implementation of this function searches + * for maximal cliques using \ref igraph_maximal_cliques_callback() and drops + * those that are not the largest. + * + * 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_maximal_cliques_callback(graph, &igraph_i_largest_cliques_store, (void*)res, 0, 0)); + IGRAPH_FINALLY_CLEAN(1); + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_clique_number + * \brief Finds the clique number of the graph. + * + * + * The clique number of a graph is the size of the largest clique. + * + * The current implementation of this function searches + * for maximal cliques using \ref igraph_maximal_cliques_callback() and keeps + * track of the size of the largest clique that was found. + * + * \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_maximal_cliques_callback(graph, &igraph_i_maximal_cliques_store_max_size, (void*)no, 0, 0); +} + +static 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_LOOPS_TWICE, IGRAPH_MULTIPLE + )); + } + 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/src/rigraph/core/cliques/glet.c b/src/rigraph/core/cliques/glet.c new file mode 100644 index 0000000..74d14ad --- /dev/null +++ b/src/rigraph/core/cliques/glet.c @@ -0,0 +1,867 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2013 Gabor Csardi + 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_conversion.h" +#include "igraph_constructors.h" +#include "igraph_cliques.h" +#include "igraph_memory.h" +#include "igraph_operators.h" +#include "igraph_qsort.h" +#include "igraph_structural.h" + +/** + * \section graphlets_intro Introduction + * + * + * 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. + * + * + * + * 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 + * + * + * + * 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(). + * + * + * + * + * 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(). + * + * + */ + +typedef struct { + igraph_vector_int_t *resultids; + igraph_t *result; + igraph_vector_t *resultweights; + int nc; +} igraph_i_subclique_next_free_t; + +static 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++) { + igraph_vector_int_destroy(&data->resultids[i]); + } + IGRAPH_FREE(data->resultids); + } + if (data->result) { + for (i = 0; i < data->nc; i++) { + igraph_destroy(&data->result[i]); + } + IGRAPH_FREE(data->result); + } + if (data->resultweights) { + for (i = 0; i < data->nc; 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. + * + */ + +static 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_int_destroy, &mark); + igraph_vector_int_init(&map, no_of_nodes); + IGRAPH_FINALLY(igraph_vector_int_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; +} + +static 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); +} + +static 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_sub = newweights + i; + igraph_vector_int_t *ids_sub = newids + i; + igraph_i_graphlets(g, w_sub, cliques, thresholds, ids_sub, 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; + +static 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; + } +} + +static 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_CHECK(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; +} + +/* TODO: not made static because it is used by the R interface */ +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_CHECK(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; + +static 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/src/rigraph/core/cliques/maximal_cliques.c b/src/rigraph/core/cliques/maximal_cliques.c new file mode 100644 index 0000000..fba4e38 --- /dev/null +++ b/src/rigraph/core/cliques/maximal_cliques.c @@ -0,0 +1,565 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2013 Gabor Csardi + 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_adjlist.h" +#include "igraph_constants.h" +#include "igraph_community.h" +#include "igraph_interface.h" +#include "igraph_memory.h" +#include "igraph_progress.h" + +#include "core/interruption.h" + +#define CONCAT2x(a,b) a ## b +#define CONCAT2(a,b) CONCAT2x(a,b) +#define FUNCTION(name,sfx) CONCAT2(name,sfx) + +static 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); + +static 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); + +static 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); + +static 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); + +static 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= sPS && avneipos <= sPE) { + if (pp != avnei) { + int tmp = *avnei; + *avnei = *pp; + *pp = tmp; + } + pp++; + } + } + } + + return IGRAPH_SUCCESS; +} + +static 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; + + IGRAPH_UNUSED(XS); + + /* 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_CHECK(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_CHECK(igraph_vector_int_push_back(nextv, vcand)); + } + } + + return IGRAPH_SUCCESS; +} + +#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) + +static 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_CHECK(igraph_vector_int_push_back(R, mynextv)); + + return IGRAPH_SUCCESS; +} + +#undef SWAP + +static 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]; + + IGRAPH_UNUSED(PS); + IGRAPH_UNUSED(XE); + IGRAPH_UNUSED(adjlist); + + VECTOR(*PX)[vpos] = tmp; + VECTOR(*PX)[*PE] = v; + VECTOR(*pos)[v] = (*PE) + 1; + VECTOR(*pos)[tmp] = vpos + 1; + (*PE)--; (*XS)--; + IGRAPH_CHECK(igraph_vector_int_push_back(H, v)); + + return IGRAPH_SUCCESS; +} + +static 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_UNUSED(PS); + IGRAPH_UNUSED(XE); + IGRAPH_UNUSED(adjlist); + + 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 Finds all maximal cliques in a graph. + * + * + * A maximal clique is a clique which can't be extended any more by + * adding a new vertex to it. + * + * + * If you are only interested in the size of the largest clique in the + * graph, use \ref igraph_clique_number() instead. + * + * + * 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. + * + * 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, i.e. \p res will contain pointers to \ref 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 + * + * + * 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 + * \brief Find maximal cliques and write them to a file. + * + * This function enumerates all maximal cliques and writes them to file. + * + * + * + * Edge directions are ignored. + * + * + * + * \param graph The input graph. + * \param outfile Pointer to the output file, it should be writable. + * \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_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 + * + * This function enumerates all maximal cliques for a subset of initial + * vertices and writes them to file. + * + * + * + * Edge directions are ignored. + * + * + * + * \param graph The input graph. + * \param subset Pointer to an \c igraph_vector_int_t containing the + * subset of initial vertices + * \param res Pointer to an \c igraph_ptr_t; the cliques will be + * stored here + * \param no Pointer to an \c igraph_integer_t; the number of maximal + * cliques will be stored here. + * \param outfile Pointer to an output file or \c NULL. + * When not \c NULL, the file should be writable. + * \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_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 a pointer to an \ref igraph_vector_t. 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(). + * + * + * + * Edge directions are ignored. + * + * + * + * \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 Counts 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. + * + * + * + * Edge directions are ignored. + * + * + * + * \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 \p 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/src/rigraph/core/cliques/maximal_cliques_template.h b/src/rigraph/core/cliques/maximal_cliques_template.h new file mode 100644 index 0000000..e3f3da7 --- /dev/null +++ b/src/rigraph/core/cliques/maximal_cliques_template.h @@ -0,0 +1,415 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2013 Gabor Csardi + 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 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 CLEANUP +#define FOR_LOOP_OVER_VERTICES for (i=0; i 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_CHECK(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_CHECK(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_CHECK(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_CHECK(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_FINALLY(&order, no_of_nodes); + IGRAPH_VECTOR_INT_INIT_FINALLY(&rank, no_of_nodes); + IGRAPH_VECTOR_INIT_FINALLY(&coreness, no_of_nodes); + IGRAPH_CHECK(igraph_coreness(graph, &coreness, /*mode=*/ IGRAPH_ALL)); + IGRAPH_CHECK(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_NO_LOOPS, IGRAPH_NO_MULTIPLE); + IGRAPH_FINALLY(igraph_adjlist_destroy, &adjlist); + + igraph_adjlist_init(graph, &fulladjlist, IGRAPH_ALL, IGRAPH_NO_LOOPS, IGRAPH_NO_MULTIPLE); + IGRAPH_FINALLY(igraph_adjlist_destroy, &fulladjlist); + + IGRAPH_VECTOR_INT_INIT_FINALLY(&PX, 20); + IGRAPH_VECTOR_INT_INIT_FINALLY(&R, 20); + IGRAPH_VECTOR_INT_INIT_FINALLY(&H, 100); + IGRAPH_VECTOR_INT_INIT_FINALLY(&pos, no_of_nodes); + IGRAPH_VECTOR_INT_INIT_FINALLY(&nextv, 100); + + 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_CHECK(igraph_vector_int_resize(&PX, vdeg)); + IGRAPH_CHECK(igraph_vector_int_resize(&R, 1)); + IGRAPH_CHECK(igraph_vector_int_resize(&H, 1)); + igraph_vector_int_null(&pos); /* TODO: makes it quadratic? */ + IGRAPH_CHECK(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_CHECK(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_CHECK(igraph_vector_int_push_back(radj, nei)); + } + } + } + + /* Reorder the adjacency lists, according to P and X. */ + IGRAPH_CHECK(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); + + CLEANUP; + + 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(9); + + return IGRAPH_SUCCESS; +} + +#undef RESTYPE +#undef RESNAME +#undef SUFFIX +#undef RECORD +#undef FINALLY +#undef CLEANUP +#undef FOR_LOOP_OVER_VERTICES +#undef FOR_LOOP_OVER_VERTICES_PREPARE diff --git a/src/rigraph/core/community/community_misc.c b/src/rigraph/core/community/community_misc.c new file mode 100644 index 0000000..4f01b2c --- /dev/null +++ b/src/rigraph/core/community/community_misc.c @@ -0,0 +1,854 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2007-2020 The igraph development 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., 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_adjlist.h" +#include "igraph_interface.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_conversion.h" +#include "igraph_centrality.h" +#include "igraph_structural.h" + +#include "core/indheap.h" +#include "core/interruption.h" + +#include "config.h" + +#include +#include + +/** + * \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. + * + * + * 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. + * + * + * This function performs \p steps merge operations as prescribed by + * the \p merges matrix and returns the current state of the network. + * + * + * 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; + igraph_vector_bool_t already_merged; + igraph_vector_t own_membership; + igraph_bool_t using_own_membership = 0; + + if (steps > igraph_matrix_nrow(merges)) { + IGRAPH_ERRORF("Number of steps is greater than number of rows in merges matrix: found %" + IGRAPH_PRId " steps, %ld rows.", IGRAPH_EINVAL, steps, igraph_matrix_nrow(merges)); + } + + if (igraph_matrix_ncol(merges) != 2) { + IGRAPH_ERRORF("The merges matrix should have two columns, but has %ld.", + IGRAPH_EINVAL, igraph_matrix_ncol(merges)); + } + if (steps < 0) { + IGRAPH_ERRORF("Number of steps should be non-negative, found %" IGRAPH_PRId ".", IGRAPH_EINVAL, steps); + } + + if (csize != 0 && membership == 0) { + /* we need a membership vector to calculate 'csize' but the user did + * not provide one; let's allocate one ourselves */ + IGRAPH_VECTOR_INIT_FINALLY(&own_membership, no_of_nodes); + using_own_membership = 1; + membership = &own_membership; + } + + 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_BOOL_INIT_FINALLY(&already_merged, steps + no_of_nodes); + 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); + + if (VECTOR(already_merged)[c1] == 0) { + VECTOR(already_merged)[c1] = 1; + } else { + IGRAPH_ERRORF("Merges matrix contains multiple merges of cluster %ld.", IGRAPH_EINVAL, c1); + } + if (VECTOR(already_merged)[c2] == 0) { + VECTOR(already_merged)[c2] = 1; + } else { + IGRAPH_ERRORF("Merges matrix contains multiple merges of cluster %ld.", IGRAPH_EINVAL, c2); + } + + /* 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) { + /* it can never happen that csize != 0 and membership == 0; we have + * handled that case above */ + 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_vector_bool_destroy(&already_merged); + IGRAPH_FINALLY_CLEAN(2); + + if (using_own_membership) { + igraph_vector_destroy(&own_membership); + 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, i.e. 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 < 0) { + IGRAPH_ERRORF("Membership indices should be non-negative. " + "Found member of cluster %ld.", IGRAPH_EINVAL, c); + } + + if (c >= n) { + IGRAPH_ERRORF("Membership indices should be less than total number of vertices. " + "Found member of cluster %ld, but only %ld vertices.", IGRAPH_EINVAL, c, n); + } + + 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; +} + +static int igraph_i_compare_communities_vi(const igraph_vector_t *v1, + const igraph_vector_t *v2, igraph_real_t* result); +static int igraph_i_compare_communities_nmi(const igraph_vector_t *v1, + const igraph_vector_t *v2, igraph_real_t* result); +static int igraph_i_compare_communities_rand(const igraph_vector_t *v1, + const igraph_vector_t *v2, igraph_real_t* result, igraph_bool_t adjust); +static 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). + * + * + * References: + * + * + * 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. + * + * + * Danon L, Diaz-Guilera A, Duch J, Arenas A: Comparing community structure + * identification. J Stat Mech P09008, 2005. + * + * + * 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. + * + * + * Rand WM: Objective criteria for the evaluation of clustering methods. + * J Am Stat Assoc 66(336):846-850, 1971. + * + * + * 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: + * + * + * 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. + * + * + * 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. + * + * + * 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. + * + * \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_ERRORF("Community membership vectors have different lengths: %ld and %ld.", + IGRAPH_EINVAL, igraph_vector_size(comm1), igraph_vector_size(comm2)); + } + + /* 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. + */ +static 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; + long int k1; + long int k2; + double *p1, *p2; + igraph_spmatrix_t m; + igraph_spmatrix_iter_t mit; + + n = igraph_vector_size(v1); + if (n == 0) { + *h1 = 0; + *h2 = 0; + *mut_inf = 0; + return IGRAPH_SUCCESS; + } + k1 = (long int)igraph_vector_max(v1) + 1; + k2 = (long int)igraph_vector_max(v2) + 1; + p1 = IGRAPH_CALLOC(k1, double); + if (p1 == 0) { + IGRAPH_ERROR("igraph_i_entropy_and_mutual_information failed", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, p1); + p2 = IGRAPH_CALLOC(k2, double); + if (p2 == 0) { + IGRAPH_ERROR("igraph_i_entropy_and_mutual_information failed", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_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); + IGRAPH_FREE(p1); IGRAPH_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(). + * + * + * Reference: Danon L, Diaz-Guilera A, Duch J, Arenas A: Comparing community + * structure identification. J Stat Mech P09008, 2005. + * + * + * Time complexity: O(n log(n)) + */ +static 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(). + * + * + * 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. + * + * + * Time complexity: O(n log(n)) + */ +static 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. + * + * + * This function assumes that the community membership vectors have already + * been normalized using igraph_reindex_communities(). + * + * + * 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. + */ +static int igraph_i_confusion_matrix(const igraph_vector_t *v1, const igraph_vector_t *v2, + igraph_spmatrix_t *m) { + long int k1; + long int k2; + long int i, n; + + n = igraph_vector_size(v1); + if (n == 0 ) { + IGRAPH_CHECK(igraph_spmatrix_resize(m, 0, 0)); + return IGRAPH_SUCCESS; + } + k1 = (long int)igraph_vector_max(v1) + 1; + k2 = (long int)igraph_vector_max(v2) + 1; + 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. + * + * + * This function assumes that the community membership vectors have already + * been normalized using igraph_reindex_communities(). + * + * + * 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. + * + * + * 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. + */ +static 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; + + if (n == 0) { + *distance12 = 0; + *distance21 = 0; + return IGRAPH_SUCCESS; + } + /* 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. + * + * + * This function assumes that the community membership vectors have already + * been normalized using igraph_reindex_communities(). + * + * + * References: + * + * + * Rand WM: Objective criteria for the evaluation of clustering methods. J Am + * Stat Assoc 66(336):846-850, 1971. + * + * + * Hubert L and Arabie P: Comparing partitions. Journal of Classification + * 2:193-218, 1985. + * + * + * 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. + */ +static 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; + + if (igraph_vector_size(v1) <= 1) { + IGRAPH_ERRORF("Rand indices not defined for only zero or one vertices. " + "Found membership vector of size %ld", IGRAPH_EINVAL, igraph_vector_size(v1)); + } + + /* 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/src/rigraph/core/community/edge_betweenness.c b/src/rigraph/core/community/edge_betweenness.c new file mode 100644 index 0000000..fc3cc97 --- /dev/null +++ b/src/rigraph/core/community/edge_betweenness.c @@ -0,0 +1,750 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2007-2020 The igraph development 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., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_community.h" + +#include "igraph_adjlist.h" +#include "igraph_components.h" +#include "igraph_dqueue.h" +#include "igraph_interface.h" +#include "igraph_memory.h" +#include "igraph_progress.h" +#include "igraph_stack.h" + +#include "core/indheap.h" +#include "core/interruption.h" + +#include + +static 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; +} + +static int igraph_i_community_eb_get_merges2(const igraph_t *graph, + const igraph_bool_t directed, + 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_bool_t use_directed = directed && igraph_is_directed(graph); + + 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, weights, + /* resolution */ 1, + use_directed, &maxmod)); + 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, (igraph_integer_t) edge); + long int to = IGRAPH_TO(graph, (igraph_integer_t) 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, weights, + /* resolution */ 1, + use_directed, &actmod)); + 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, i.e. the dendrogram for an edge betweenness community structure. + * + * + * This function is handy if you have a sequence of edges 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 directed Whether to use the directed or undirected version + * of modularity. Will be ignored for undirected graphs. + * \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 \c NULL pointers. + * \param res Pointer to an initialized matrix, if not \c 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 numbered 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, where \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 \c 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_bool_t directed, + 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; + + /* catch null graph early */ + if (no_of_nodes == 0) { + if (res) { + igraph_matrix_resize(res, 0, 2); + } + if (bridges) { + igraph_vector_clear(bridges); + } + if (modularity) { + igraph_vector_clear(modularity); + } + if (membership) { + igraph_vector_clear(membership); + } + return IGRAPH_SUCCESS; + } + + if (membership || modularity) { + return igraph_i_community_eb_get_merges2(graph, + directed && igraph_is_directed(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 IGRAPH_SUCCESS; +} + +/* Find the smallest active element in the vector */ +static 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). + * + * + * 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 \c NULL if + * the edge IDs are not needed by the caller. + * \param edge_betweenness Pointer to an initialized vector or + * \c 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 \c NULL. If not \c 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 \c 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 (i.e. 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; + double *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_LOOPS_ONCE)); + IGRAPH_FINALLY(igraph_inclist_destroy, &elist_out); + IGRAPH_CHECK(igraph_inclist_init(graph, &elist_in, IGRAPH_IN, IGRAPH_LOOPS_ONCE)); + 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_LOOPS_TWICE)); + 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, double); + 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_size(weights) != no_of_edges) { + IGRAPH_ERROR("Weight vector length must agree with number of edges.", IGRAPH_EINVAL); + } + + if (no_of_edges > 0) { + /* Must not call vector_min on empty vector */ + igraph_real_t minweight = igraph_vector_min(weights); + if (minweight <= 0) { + IGRAPH_ERROR("Weights must be strictly positive.", IGRAPH_EINVAL); + } + + if (igraph_is_nan(minweight)) { + IGRAPH_ERROR("Weights must not be NaN.", 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)); + if (no_of_edges > 0) { + 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_cutoff */ + 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(double)); + 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]; + long int neighbor= (long int) IGRAPH_OTHER(graph, edge, actnode); + 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_cutoff_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(double)); + 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, directed, result, weights, merges, + bridges, modularity, + membership)); + } + + if (result_owned) { + igraph_vector_destroy(result); + IGRAPH_FREE(result); + IGRAPH_FINALLY_CLEAN(2); + } + + return 0; +} diff --git a/src/rigraph/core/community/fast_modularity.c b/src/rigraph/core/community/fast_modularity.c new file mode 100644 index 0000000..bcf0c35 --- /dev/null +++ b/src/rigraph/core/community/fast_modularity.c @@ -0,0 +1,1078 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2007-2012 Gabor Csardi + 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_structural.h" +#include "igraph_vector_ptr.h" + +#include "core/interruption.h" + +/* #define IGRAPH_FASTCOMM_DEBUG */ + +#ifdef _MSC_VER +/* MSVC does not support variadic macros */ +#include +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. */ +static 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 */ +static 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); + } + IGRAPH_FREE(list->e); + if (list->heapindex != 0) { + IGRAPH_FREE(list->heapindex); + } + if (list->heap != 0) { + IGRAPH_FREE(list->heap); + } +} + +/* Community list heap maintenance: sift down */ +static 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 */ +static 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 */ +static 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 */ +/* +static 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. */ +/* +static 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 */ +static 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) */ +static 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 */ +static 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 */ +static 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. */ +static 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 */ +static 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. + * + * + * 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 \c 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 n+1 in the second merge, etc. + * The matrix will be resized as needed. If this argument is \c NULL + * then it is ignored completely. + * \param modularity Pointer to an initialized vector or \c 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 on undirected graphs only.", IGRAPH_UNIMPLEMENTED); + } + + total_joins = no_of_nodes > 0 ? no_of_nodes - 1 : 0; + + if (weights != 0) { + if (igraph_vector_size(weights) != no_of_edges) { + IGRAPH_ERROR("Length of weight vector must agree with number of edges.", IGRAPH_EINVAL); + } + if (no_of_edges > 0) { + igraph_real_t minweight = igraph_vector_min(weights); + if (minweight < 0) { + IGRAPH_ERROR("Weights must not be negative.", IGRAPH_EINVAL); + } + if (igraph_is_nan(minweight)) { + IGRAPH_ERROR("Weights must not be NaN.", 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 detection works only on graphs without multi-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("Insufficient memory for fast greedy community detection.", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_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("Insufficient memory for fast greedy community detection.", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, communities.heap); + communities.heapindex = (igraph_integer_t*)calloc((size_t)no_of_nodes, sizeof(igraph_integer_t)); + if (communities.heapindex == 0) { + IGRAPH_ERROR("Insufficient memory for 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("Insufficient memory for fast greedy community detection.", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_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("Insufficient memory for fast greedy community detection.", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_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 IGRAPH_FASTCOMM_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); + + /* IGRAPH_FASTCOMM_DEBUG */ + /* from=join_order[no_of_joins*2]; to=join_order[no_of_joins*2+1]; + if (to == -1) break; + for (i=0; isecond == 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++; + } + } + + p1 = 0; + 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 ? p1->first : -1, 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; + long int merges_nrow = igraph_matrix_nrow(merges); + ivec = IGRAPH_CALLOC(merges_nrow, long int); + if (ivec == 0) { + IGRAPH_ERROR("Insufficient memory for fast greedy community detection.", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_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); + IGRAPH_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"); + IGRAPH_FREE(pairs); + IGRAPH_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/src/rigraph/core/community/fluid.c b/src/rigraph/core/community/fluid.c new file mode 100644 index 0000000..e272a06 --- /dev/null +++ b/src/rigraph/core/community/fluid.c @@ -0,0 +1,268 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2007-2020 The igraph development 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., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_community.h" + +#include "igraph_adjlist.h" +#include "igraph_components.h" +#include "igraph_interface.h" +#include "igraph_random.h" +#include "igraph_structural.h" + +/** + * \ingroup communities + * \function igraph_community_fluid_communities + * \brief Community detection based on fluids interacting on the graph. + * + * The algorithm is 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 + * & 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|) + */ +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) { + if (membership) { + IGRAPH_CHECK(igraph_vector_resize(membership, no_of_nodes)); + igraph_vector_fill(membership, 0); + } + if (modularity) { + IGRAPH_CHECK(igraph_modularity(graph, membership, 0, 1, igraph_is_directed(graph), modularity)); + } + return IGRAPH_SUCCESS; + } + if ((long int) no_of_communities < 1) { + IGRAPH_ERROR("Number of requested communities must be greater than zero.", IGRAPH_EINVAL); + } + if ((long int) no_of_communities > no_of_nodes) { + IGRAPH_ERROR("Number of requested communities must not be greater than the number of nodes.", + IGRAPH_EINVAL); + } + IGRAPH_CHECK(igraph_is_simple(graph, &res)); + if (!res) { + IGRAPH_ERROR("Fluid community detection supports only simple graphs.", IGRAPH_EINVAL); + } + IGRAPH_CHECK(igraph_is_connected(graph, &res, IGRAPH_WEAK)); + if (!res) { + IGRAPH_ERROR("Fluid community detection supports only connected graphs.", IGRAPH_EINVAL); + } + if (igraph_is_directed(graph)) { + IGRAPH_WARNING("Edge directions are ignored by fluid community detection."); + } + + /* Internal variables initialization */ + max_density = 1.0; + + /* 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); + + /* 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_LOOPS_TWICE, IGRAPH_MULTIPLE)); + 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)); + } + } + + RNG_BEGIN(); + 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, NULL, + /* resolution */ 1, + /* only undirected */ 0, modularity)); + } + + 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 IGRAPH_SUCCESS; +} diff --git a/src/rigraph/core/community/infomap/infomap.cc b/src/rigraph/core/community/infomap/infomap.cc new file mode 100644 index 0000000..b1132c4 --- /dev/null +++ b/src/rigraph/core/community/infomap/infomap.cc @@ -0,0 +1,325 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2011-2012 Gabor Csardi + 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 +#include "igraph_interface.h" +#include "igraph_community.h" +#include "core/interruption.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 ] + + * + * 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.) + * + * + * Note that the graph must not contain isolated vertices. + * + * + * 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); + + IGRAPH_CHECK(igraph_reindex_membership(membership, 0, 0)); + + return IGRAPH_SUCCESS; +} diff --git a/src/rigraph/core/community/infomap/infomap_FlowGraph.cc b/src/rigraph/core/community/infomap/infomap_FlowGraph.cc new file mode 100644 index 0000000..9af7eb3 --- /dev/null +++ b/src/rigraph/core/community/infomap/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 + 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 ) + +using namespace std; + +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 sub_mem; + for (int j = 0 ; j < sub_Nnode ; j++) { + sub_mem.insert(sub_members[j]); + } + set::iterator it_mem = sub_mem.begin(); + + vector sub_renumber = vector(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 size_tmp = vector(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/src/rigraph/core/community/infomap/infomap_FlowGraph.h b/src/rigraph/core/community/infomap/infomap_FlowGraph.h new file mode 100644 index 0000000..937347c --- /dev/null +++ b/src/rigraph/core/community/infomap/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 + 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 +#include + +#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; + std::vector 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/src/rigraph/core/community/infomap/infomap_Greedy.cc b/src/rigraph/core/community/infomap/infomap_Greedy.cc new file mode 100644 index 0000000..62e2945 --- /dev/null +++ b/src/rigraph/core/community/infomap/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 + 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 +#define plogp( x ) ( (x) > 0.0 ? (x)*log(x) : 0.0 ) + +using namespace std; + +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(Nnode).swap(mod_empty); + + vector(Nnode).swap(node_index); + vector(Nnode).swap(mod_exit); + vector(Nnode).swap(mod_size); + vector(Nnode).swap(mod_danglingSize); + vector(Nnode).swap(mod_teleportWeight); + vector(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 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 redirect(Nnode, 0); + vector > > 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(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(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 modSnode; // will give ids of no-empty modules (nodes) + int Nmod = 0; + if (sort) { + multimap Msize; + for (int i = 0; i < Nnode; i++) { + if (mod_members[i] > 0) { + Nmod++; + Msize.insert(pair(mod_size[i], i)); + } + } + for (multimap::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 nodeInMod = vector(Nnode); + + // creation of new nodes + for (int i = 0; i < Nmod; i++) { + //node_tmp[i] = new Node(); + vector().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 > outFlowNtoM(Nmod); + map::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 > 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().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(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/src/rigraph/core/community/infomap/infomap_Greedy.h b/src/rigraph/core/community/infomap/infomap_Greedy.h new file mode 100644 index 0000000..9769d1d --- /dev/null +++ b/src/rigraph/core/community/infomap/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 + 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 +#include +#include +#include + +#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) + + std::vector node_index; // module number of each node + + int Nempty; + std::vector mod_empty; + + std::vector mod_exit; // version tmp de node + std::vector mod_size; + std::vector mod_danglingSize; + std::vector mod_teleportWeight; + std::vector mod_members; +}; + +void delete_Greedy(Greedy *greedy); +#endif diff --git a/src/rigraph/core/community/infomap/infomap_Node.cc b/src/rigraph/core/community/infomap/infomap_Node.cc new file mode 100644 index 0000000..21f1b31 --- /dev/null +++ b/src/rigraph/core/community/infomap/infomap_Node.cc @@ -0,0 +1,71 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2011-2012 Gabor Csardi + 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" + +using namespace std; + +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(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 >(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 >(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/src/rigraph/core/community/infomap/infomap_Node.h b/src/rigraph/core/community/infomap/infomap_Node.h new file mode 100644 index 0000000..5cd0407 --- /dev/null +++ b/src/rigraph/core/community/infomap/infomap_Node.h @@ -0,0 +1,52 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2011-2012 Gabor Csardi + 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 +#include + +#include "igraph_interface.h" + +class Node { +public: + + Node(); + Node(int modulenr, double tpweight); + + std::vector members; + std::vector< std::pair > inLinks; + std::vector< std::pair > outLinks; + double selfLink; + + double teleportWeight; + double danglingSize; + double exit; + double size; +}; + +void cpyNode(Node *newNode, Node *oldNode); + +#endif diff --git a/src/rigraph/core/community/label_propagation.c b/src/rigraph/core/community/label_propagation.c new file mode 100644 index 0000000..e4eab42 --- /dev/null +++ b/src/rigraph/core/community/label_propagation.c @@ -0,0 +1,435 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2007-2020 The igraph development 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., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_community.h" + +#include "igraph_adjlist.h" +#include "igraph_dqueue.h" +#include "igraph_interface.h" +#include "igraph_memory.h" +#include "igraph_random.h" + +/** + * \ingroup communities + * \function igraph_community_label_propagation + * \brief Community detection based on label propagation. + * + * This function implements the label propagation-based community detection + * algorithm described by Raghavan, Albert and Kumara. This version extends + * the original method by the ability to take edge weights into consideration + * and also by allowing some labels to be fixed. + * + * + * Weights are taken into account as follows: when the new label of node + * \c i is determined, the algorithm iterates over all edges incident on + * node \c i and calculate the total weight of edges leading to other + * nodes with label 0, 1, 2, ..., \c k - 1 (where \c k is the number of possible + * labels). The new label of node \c i will then be the label whose edges + * (among the ones incident on node \c i) have the highest total weight. + * + * + * Reference: + * + * + * 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). + * https://doi.org/10.1103/PhysRevE.76.036106 + * + * \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 \c 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. Unlabeled + * vertices which are not reachable from any labeled ones will remain + * unlabeled at the end of the label propagation process, and will be + * labeled in an additional step to avoid returning negative values in + * \p membership. In undirected graphs, this happens when entire connected + * components are unlabeled. Then, each unlabeled component will receive + * its own separate label. In directed graphs, the outcome of the + * additional labeling should be considered undefined and may change + * in the future; please do not rely on it. + * \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. If they are, this vector will be modified to + * make it consistent with \p initial. + * \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, + const 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, control_iteration; + igraph_bool_t unlabelled_left; + + igraph_vector_t label_counters, dominant_labels, nonzero_labels, node_order; + + /* We make a copy of 'fixed' as a pointer into 'fixed_copy' after casting + * away the constness, and promise ourselves that we will make a proper + * copy of 'fixed' into 'fixed_copy' as soon as we start mutating it */ + igraph_vector_bool_t* fixed_copy = (igraph_vector_bool_t*) fixed; + + /* 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("Fixed labeling vector length must agree with number of nodes.", IGRAPH_EINVAL); + } + if (weights) { + if (igraph_vector_size(weights) != no_of_edges) { + IGRAPH_ERROR("Length of weight vector must agree with number of edges.", IGRAPH_EINVAL); + } + if (no_of_edges > 0) { + igraph_real_t minweight = igraph_vector_min(weights); + if (minweight < 0) { + IGRAPH_ERROR("Weights must not be negative.", IGRAPH_EINVAL); + } + if (igraph_is_nan(minweight)) { + IGRAPH_ERROR("Weights must not be NaN.", 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("Initial labeling vector length must agree with number of nodes.", 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."); + + /* We cannot modify 'fixed' because it is const, so we make a copy and + * modify 'fixed_copy' instead */ + if (fixed_copy == fixed) { + fixed_copy = igraph_Calloc(1, igraph_vector_bool_t); + if (fixed_copy == 0) { + IGRAPH_ERROR("Failed to copy 'fixed' vector.", IGRAPH_ENOMEM); + } + + IGRAPH_FINALLY(igraph_free, fixed_copy); + IGRAPH_CHECK(igraph_vector_bool_copy(fixed_copy, fixed)); + IGRAPH_FINALLY(igraph_vector_bool_destroy, fixed_copy); + } + + VECTOR(*fixed_copy)[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); + } + } else { + for (i = 0; i < no_of_nodes; i++) { + VECTOR(*membership)[i] = i + 1; + } + } + + /* From this point onwards we use 'fixed_copy' instead of 'fixed' */ + + /* 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_LOOPS_ONCE)); + IGRAPH_FINALLY(igraph_inclist_destroy, &il); + } else { + IGRAPH_CHECK(igraph_adjlist_init(graph, &al, IGRAPH_IN, IGRAPH_LOOPS_ONCE, IGRAPH_MULTIPLE)); + 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)); + + /* Initialize node ordering vector with only the not fixed nodes */ + if (fixed_copy) { + IGRAPH_VECTOR_INIT_FINALLY(&node_order, no_of_not_fixed_nodes); + for (i = 0, j = 0; i < no_of_nodes; i++) { + if (!VECTOR(*fixed_copy)[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); + } + + /* There are two alternating types of iterations, one for changing labels and + the other one for checking the end condition - every vertex in the graph has + a label to which the maximum number of its neighbors belongs. If control_iteration + is true, we are just checking the end condition and not relabeling nodes. */ + control_iteration = 1; + 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; + + if (control_iteration) { + /* If we are in the control iteration, we expect in the begining of + the iterationthat all vertices meet the end condition, so running is false. + If some of them does not, running is set to true later in the code. */ + running = 0; + } else { + /* Shuffle the node ordering vector if we are in the label updating iteration */ + IGRAPH_CHECK(igraph_vector_shuffle(&node_order)); + } + + RNG_BEGIN(); + /* 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) { + if (control_iteration) { + /* 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; + } + } + else { + /* Select randomly from the dominant labels */ + k = RNG_INTEGER(0, igraph_vector_size(&dominant_labels) - 1); + VECTOR(*membership)[v1] = VECTOR(dominant_labels)[(long int)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(); + + /* Alternating between control iterations and label updating iterations */ + control_iteration = !control_iteration; + } + + if (weights) { + igraph_inclist_destroy(&il); + } else { + igraph_adjlist_destroy(&al); + } + IGRAPH_FINALLY_CLEAN(1); + + /* Shift back the membership vector, permute labels in increasing order */ + /* We recycle label_counters here :) */ + igraph_vector_fill(&label_counters, -1); + j = 0; + unlabelled_left = 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 */ + unlabelled_left = 1; + } + VECTOR(*membership)[i] = k; + } + + /* From this point on, unlabelled nodes are represented with -1 (no longer 0). */ +#define IS_UNLABELLED(x) (VECTOR(*membership)[x] < 0) + + /* If any nodes are left unlabelled, we assign the remaining labels to them, + * as well as to all unlabelled nodes reachable from them. + * + * Note that only those nodes could remain unlabelled which were unreachable + * from any labelled ones. Thus, in the undirected case, fully unlabelled + * connected components remain unlabelled. Here we label each such component + * with the same label. + */ + if (unlabelled_left) { + igraph_dqueue_t q; + igraph_vector_t neis; + + /* In the directed case, the outcome depends on the node ordering, thus we + * shuffle nodes one more time. */ + IGRAPH_CHECK(igraph_vector_shuffle(&node_order)); + + IGRAPH_VECTOR_INIT_FINALLY(&neis, 0); + + IGRAPH_CHECK(igraph_dqueue_init(&q, 0)); + IGRAPH_FINALLY(igraph_dqueue_destroy, &q); + + for (i=0; i < no_of_nodes; ++i) { + long int v = VECTOR(node_order)[i]; + + /* Is this node unlabelled? */ + if (IS_UNLABELLED(v)) { + /* If yes, we label it, and do a BFS to apply the same label + * to all other unlabelled nodes reachable from it */ + igraph_dqueue_push(&q, v); + VECTOR(*membership)[v] = j; + while (!igraph_dqueue_empty(&q)) { + long int ni, num_neis; + long int actnode = igraph_dqueue_pop(&q); + + IGRAPH_CHECK(igraph_neighbors(graph, &neis, actnode, IGRAPH_OUT)); + num_neis = igraph_vector_size(&neis); + + for (ni = 0; ni < num_neis; ++ni) { + long int neighbor = VECTOR(neis)[ni]; + if (IS_UNLABELLED(neighbor)) { + VECTOR(*membership)[neighbor] = j; + IGRAPH_CHECK(igraph_dqueue_push(&q, neighbor)); + } + } + } + j++; + } + } + + igraph_vector_destroy(&neis); + igraph_dqueue_destroy(&q); + IGRAPH_FINALLY_CLEAN(2); + } + + if (modularity) { + IGRAPH_CHECK(igraph_modularity(graph, membership, weights, + /* resolution */ 1, + /* directed */ 1, modularity)); + } + + igraph_vector_destroy(&node_order); + igraph_vector_destroy(&label_counters); + igraph_vector_destroy(&dominant_labels); + igraph_vector_destroy(&nonzero_labels); + IGRAPH_FINALLY_CLEAN(4); + + if (fixed != fixed_copy) { + igraph_vector_bool_destroy(fixed_copy); + igraph_Free(fixed_copy); + IGRAPH_FINALLY_CLEAN(2); + } + + return IGRAPH_SUCCESS; +} diff --git a/src/rigraph/core/community/leading_eigenvector.c b/src/rigraph/core/community/leading_eigenvector.c new file mode 100644 index 0000000..65f512e --- /dev/null +++ b/src/rigraph/core/community/leading_eigenvector.c @@ -0,0 +1,1063 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2007-2020 The igraph development 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., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_community.h" + +#include "igraph_adjlist.h" +#include "igraph_components.h" +#include "igraph_dqueue.h" +#include "igraph_interface.h" +#include "igraph_iterators.h" +#include "igraph_memory.h" +#include "igraph_statusbar.h" +#include "igraph_structural.h" + +#include "core/interruption.h" + +/** + * \section about_leading_eigenvector_methods + * + * + * The function documented in these section implements the + * leading eigenvector method developed by Mark Newman and + * published in MEJ Newman: Finding community structure using the + * eigenvectors of matrices, Phys Rev E 74:036104 (2006). + * + * + * 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 configuration model 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. + * + * + * 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. + * + * + * 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. + * + * + * + * \example examples/simple/igraph_community_leading_eigenvector.c + * + */ + +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; + +static 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; +} + +static 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; +} + +static 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; +} + +static 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; +} + +static 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); + } + } +} + +static 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 p communities, + * then these are numbered from zero to p-1. The + * first line of the matrix contains the first merge + * (which is in reality the last split) of two communities into + * community p, the merge in the second line forms + * community p+1, 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 + * \ref igraph_free() 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, steps 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_LOOPS_TWICE, IGRAPH_MULTIPLE)); + IGRAPH_FINALLY(igraph_adjlist_destroy, &adjlist); + } else { + IGRAPH_CHECK(igraph_inclist_init(graph, &inclist, IGRAPH_ALL, IGRAPH_LOOPS_TWICE)); + 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, weights, + /* resolution */ 1, + /* only undirected */ 0, modularity)); + } + + 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 two-column matrix containing the merge + * operations. See \ref igraph_community_walktrap() for the + * detailed syntax. 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 (no_of_nodes > 0) { + components = (long int) igraph_vector_max(membership) + 1; + } else { + components = 0; + } + if (components > no_of_nodes) { + IGRAPH_ERRORF("Invalid membership vector: number of components (%ld) must " + "not be greater than the number of nodes (%ld).", IGRAPH_EINVAL, components, no_of_nodes); + } + if (steps >= components) { + IGRAPH_ERRORF("Number of steps (%" IGRAPH_PRId ") must be smaller than number of components (%ld).", + IGRAPH_EINVAL, steps, components); + } + + IGRAPH_VECTOR_INIT_FINALLY(&fake_memb, components); + + /* Check membership vector */ + for (i = 0; i < no_of_nodes; i++) { + if (VECTOR(*membership)[i] < 0) { + IGRAPH_ERRORF("Invalid membership vector, negative ID found: %g.", IGRAPH_EINVAL, VECTOR(*membership)[i]); + } + VECTOR(fake_memb)[ (long int) VECTOR(*membership)[i] ] += 1; + } + for (i = 0; i < components; i++) { + if (VECTOR(fake_memb)[i] == 0) { + /* Ideally the empty cluster's index would be reported. + However, doing so would be confusing as some high-level interfaces + use 1-based indexing, some 0-based. */ + IGRAPH_ERROR("Invalid membership vector, empty cluster found.", 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; +} diff --git a/src/rigraph/core/community/leiden.c b/src/rigraph/core/community/leiden.c new file mode 100644 index 0000000..d7dc4f6 --- /dev/null +++ b/src/rigraph/core/community/leiden.c @@ -0,0 +1,1064 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2007-2012 Gabor Csardi + 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_adjlist.h" +#include "igraph_dqueue.h" +#include "igraph_interface.h" +#include "igraph_memory.h" +#include "igraph_random.h" +#include "igraph_stack.h" +#include "igraph_vector.h" +#include "igraph_constructors.h" + +#include "core/interruption.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. + * + */ +static 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_CHECK(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); + if (u != 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_CHECK(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. + */ +static 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. + */ +static 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 (u != v && 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 (u != v && 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 = RNG_UNIF(0, total_cum_trans_diff); + long int chosen_idx; + igraph_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. + */ +static 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_CHECK(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. + * + */ +static 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_CHECK(igraph_vector_push_back(&aggregated_edges, c)); + IGRAPH_CHECK(igraph_vector_push_back(&aggregated_edges, c2)); + + /* Add edge weight */ + IGRAPH_CHECK(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_vector_destroy(&neighbor_clusters); + igraph_vector_bool_destroy(&neighbor_cluster_added); + igraph_vector_destroy(&edge_weight_to_cluster); + igraph_vector_ptr_destroy_all(&refined_clusters); + + IGRAPH_FINALLY_CLEAN(4); + + igraph_destroy(aggregated_graph); + IGRAPH_CHECK(igraph_create(aggregated_graph, &aggregated_edges, nb_refined_clusters, + IGRAPH_UNDIRECTED)); + + igraph_vector_destroy(&aggregated_edges); + + IGRAPH_FINALLY_CLEAN(1); + + 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. + * + */ +static 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); + + 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. + */ +static int igraph_i_community_leiden( + const igraph_t *graph, + igraph_vector_t *edge_weights, 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, *i_graph; + igraph_vector_t aggregated_edge_weights, aggregated_node_weights, aggregated_membership; + igraph_vector_t *i_edge_weights, *i_node_weights, *i_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; + } + + /* Initialize refined membership */ + IGRAPH_CHECK(igraph_vector_init(&refined_membership, 0)); + IGRAPH_FINALLY(igraph_vector_destroy, &refined_membership); + + /* Initialize aggregated graph */ + IGRAPH_CHECK(igraph_empty(&aggregated_graph, 0, IGRAPH_UNDIRECTED)); + IGRAPH_FINALLY(igraph_destroy, &aggregated_graph); + + /* Initialize aggregated edge weights */ + IGRAPH_CHECK(igraph_vector_init(&aggregated_edge_weights, 0)); + IGRAPH_FINALLY(igraph_vector_destroy, &aggregated_edge_weights); + + /* Initialize aggregated node weights */ + IGRAPH_CHECK(igraph_vector_init(&aggregated_node_weights, 0)); + IGRAPH_FINALLY(igraph_vector_destroy, &aggregated_node_weights); + + /* Initialize aggregated membership */ + IGRAPH_CHECK(igraph_vector_init(&aggregated_membership, 0)); + IGRAPH_FINALLY(igraph_vector_destroy, &aggregated_membership); + + /* Set actual graph, weights and membership to be used. */ + i_graph = (igraph_t*)graph; + i_edge_weights = edge_weights; + i_node_weights = node_weights; + i_membership = membership; + + /* Clean membership and count number of *clusters */ + + IGRAPH_CHECK(igraph_reindex_membership(i_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( i_graph, &edges_per_node, IGRAPH_ALL, IGRAPH_LOOPS_TWICE)); + 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(i_graph, + &edges_per_node, + i_edge_weights, i_node_weights, + resolution_parameter, + nb_clusters, + i_membership)); + + /* We only continue clustering if not all clusters are represented by a + * single node yet + */ + continue_clustering = (*nb_clusters < igraph_vcount(i_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(*i_membership)[v_aggregate]; + } + } + + /* Get node sets for each cluster. */ + IGRAPH_CHECK(igraph_i_community_get_clusters(i_membership, &clusters)); + + /* Ensure refined membership is correct size */ + IGRAPH_CHECK(igraph_vector_resize(&refined_membership, igraph_vcount(i_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(i_graph, + &edges_per_node, + i_edge_weights, i_node_weights, + cluster, i_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(i_graph)) { + igraph_vector_update(&refined_membership, i_membership); + nb_refined_clusters = *nb_clusters; + } + + /* 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]; + } + + IGRAPH_CHECK(igraph_i_community_leiden_aggregate( + i_graph, &edges_per_node, i_edge_weights, i_node_weights, + i_membership, &refined_membership, nb_refined_clusters, + &aggregated_graph, &tmp_edge_weights, &tmp_node_weights, &tmp_membership)); + + /* On the lowest level, the actual graph and node and edge weights and + * membership are used. On higher levels, we will use the aggregated graph + * and associated vectors. + */ + if (level == 0) { + /* Set actual graph, weights and membership to be used. */ + i_graph = &aggregated_graph; + i_edge_weights = &aggregated_edge_weights; + i_node_weights = &aggregated_node_weights; + i_membership = &aggregated_membership; + } + + /* Update the aggregated administration. */ + IGRAPH_CHECK(igraph_vector_update(i_edge_weights, &tmp_edge_weights)); + IGRAPH_CHECK(igraph_vector_update(i_node_weights, &tmp_node_weights)); + IGRAPH_CHECK(igraph_vector_update(i_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); + + /* Free aggregated graph and associated vectors */ + igraph_vector_destroy(&aggregated_membership); + igraph_vector_destroy(&aggregated_node_weights); + igraph_vector_destroy(&aggregated_edge_weights); + igraph_destroy(&aggregated_graph); + IGRAPH_FINALLY_CLEAN(4); + + /* 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_CHECK(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., & 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 + * + * + * 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). + * + * + * 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). + * + * + * 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 & van Eck (2019). + * + * + * The objective function being optimized is + * + * + * 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. + * 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; + 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_FINALLY(igraph_free, i_edge_weights); + IGRAPH_CHECK(igraph_vector_init(i_edge_weights, igraph_ecount(graph))); + IGRAPH_FINALLY(igraph_vector_destroy, i_edge_weights); + igraph_vector_fill(i_edge_weights, 1); + } else { + i_edge_weights = (igraph_vector_t*)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_FINALLY(igraph_free, i_node_weights); + IGRAPH_CHECK(igraph_vector_init(i_node_weights, n)); + IGRAPH_FINALLY(igraph_vector_destroy, i_node_weights); + igraph_vector_fill(i_node_weights, 1); + } else { + i_node_weights = (igraph_vector_t*)node_weights; + } + + /* Perform actual Leiden algorithm */ + IGRAPH_CHECK(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 IGRAPH_SUCCESS; +} diff --git a/src/rigraph/core/community/louvain.c b/src/rigraph/core/community/louvain.c new file mode 100644 index 0000000..7fb59c5 --- /dev/null +++ b/src/rigraph/core/community/louvain.c @@ -0,0 +1,723 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2007-2020 The igraph development 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., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_community.h" + +#include "igraph_constructors.h" +#include "igraph_interface.h" +#include "igraph_memory.h" +#include "igraph_qsort.h" +#include "igraph_random.h" + +#include "core/interruption.h" + +/* 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 */ +static igraph_real_t igraph_i_multilevel_community_modularity( + const igraph_i_multilevel_community_list *communities, + const igraph_real_t resolution) { + 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 - resolution * 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; + +static 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| */ +static 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_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(igraph_free, links); + + for (i = 0; i < ecount; i++) { + igraph_integer_t from, to; + igraph_edge(graph, (igraph_integer_t) i, &from, &to); + links[i].from = from; + links[i].to = to; + links[i].id = i; + } + + igraph_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; + } + + IGRAPH_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; + +static 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 + */ +static 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 */ + igraph_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; +} + +static 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, + const igraph_real_t resolution) { + IGRAPH_UNUSED(vertex); + return weight_inside - + resolution * 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 */ +static 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. + * \param resolution Resolution parameter. Must be greater than or equal to 0. + * Default is 1. Lower values favor fewer, larger communities; + * higher values favor more, smaller communities. + * \return Error code. + * + * Time complexity: in average near linear on sparse graphs. + */ +static int igraph_i_community_multilevel_step( + igraph_t *graph, + igraph_vector_t *weights, + igraph_vector_t *membership, + igraph_real_t *modularity, + const igraph_real_t resolution) { + + long int i, j; + long int vcount = igraph_vcount(graph); + long int ecount = igraph_ecount(graph); + 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; + igraph_vector_t node_order; + + /* 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); + } + if (resolution < 0.0) { + IGRAPH_ERROR("The resolution parameter must be non-negative", IGRAPH_EINVAL); + } + + IGRAPH_CHECK(igraph_vector_init_seq(&node_order, 0, vcount - 1)); + IGRAPH_FINALLY(igraph_vector_destroy, &node_order); + IGRAPH_CHECK(igraph_vector_shuffle(&node_order)); + + /* 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_integer_t ffrom, fto; + 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, resolution); + 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, ni; + + ni = VECTOR(node_order)[i]; + + igraph_i_multilevel_community_links(graph, &communities, + (igraph_integer_t) ni, &edges, + &weight_all, &weight_inside, + &weight_loop, &links_community, + &links_weight); + + old_id = (long int)VECTOR(*(communities.membership))[ni]; + new_id = old_id; + + /* Update old community */ + igraph_vector_set(communities.membership, ni, -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", ni, -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) ni, + weight_all, w, resolution); + /* debug("Link %ld -> %ld weight: %lf gain: %lf\n", ni, 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", ni, new_id, (double) max_q_gain); */ + + /* Add vertex to "new" community and update it */ + igraph_vector_set(communities.membership, ni, 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, resolution); + + 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_vector_destroy(&node_order); + IGRAPH_FINALLY_CLEAN(5); + + 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 + * Blondel, V. D., Guillaume, J.-L., Lambiotte, R., & Lefebvre, E. (2008). Fast + * unfolding of communities in large networks. Journal of Statistical Mechanics: + * Theory and Experiment, 10008(10), 6. + * https://doi.org/10.1088/1742-5468/2008/10/P10008 for the details (preprint: + * http://arxiv.org/abs/0803.0476). The algorithm is sometimes known as the + * "Louvain" algorithm. + * + * + * The algorithm 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: in a random + * order, 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. + * + * + * The resolution parameter \c gamma allows finding communities at different + * resolutions. Higher values of the resolution parameter typically result in + * more, smaller communities. Lower values typically result in fewer, larger + * communities. The original definition of modularity is retrieved when setting + * gamma=1. Note that the returned modularity value is calculated using + * the indicated resolution parameter. See \ref igraph_modularity() for more 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 resolution Resolution parameter. Must be greater than or equal to 0. + * Lower values favor fewer, larger communities; + * higher values favor more, smaller communities. + * Set it to 1 to use the classical definition of modularity. + * \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, + const igraph_real_t resolution, + 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, resolution)); + + /* 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, weights, resolution, + /* only undirected */ 0, &mod)); + 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 IGRAPH_SUCCESS; +} diff --git a/src/rigraph/core/community/modularity.c b/src/rigraph/core/community/modularity.c new file mode 100644 index 0000000..e1ec616 --- /dev/null +++ b/src/rigraph/core/community/modularity.c @@ -0,0 +1,376 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2007-2020 The igraph development 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., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_community.h" + +#include "igraph_conversion.h" +#include "igraph_interface.h" +#include "igraph_structural.h" + +/** + * \function igraph_modularity + * \brief Calculate the modularity of a graph with respect to some clusters or vertex types. + * + * The modularity of a graph with respect to some clustering of the vertices + * (or assignment of vertex types) + * measures how strongly separated the different clusters are from each + * other compared to a random null model. It is defined as + * + * + * Q = 1/(2m) sum_ij (A_ij - gamma * k_i * k_j / (2m)) * d(c_i,c_j), + * + * + * where \c m is the number of edges, A_ij is the adjacency matrix, + * \c k_i is the degree of vertex \c i, \c c_i is the cluster that vertex \c i belongs to + * (or its vertex type), d(i,j)=1 if i=j and 0 otherwise, + * and the sum goes over all i, j pairs of vertices. + * + * + * The resolution parameter \c gamma allows weighting the random null model, which + * might be useful when finding partitions with a high modularity. Maximizing modularity + * with higher values of the resolution parameter typically results in more, smaller clusters + * when finding partitions with a high modularity. Lower values typically results in + * fewer, larger clusters. The original definition of modularity is retrieved + * when setting gamma=1. + * + * + * Modularity can also be calculated on directed graphs. This only requires a relatively + * modest change + * + * + * Q = 1/(m) sum_ij (A_ij - gamma * k^out_i * k^in_j / m) * d(c_i,c_j), + * + * + * where \c k^out_i is the out-degree of node \c i and \c k^in_j is the in-degree of node \c j. + * + * + * Modularity on weighted graphs is also meaningful. When taking + * edge weights into account, \c A_ij equals the weight of the corresponding edge + * (or 0 if there is no edge), \c k_i is the strength (i.e. the weighted degree) of + * vertex \c i, with similar counterparts for a directed graph, and \c m is the total + * weight of all edges. + * + * + * Note that the modularity is not well-defined for graphs with no edges. + * igraph returns \c NaN for graphs with no edges; see + * https://github.com/igraph/igraph/issues/1539 for + * a detailed discussion. + * + * + * For the original definition of modularity, see Newman, M. E. J., and Girvan, M. + * (2004). Finding and evaluating community structure in networks. + * Physical Review E 69, 026113. https://doi.org/10.1103/PhysRevE.69.026113 + * + * + * For the directed definition of modularity, see Leicht, E. A., and Newman, M. E. + * J. (2008). Community Structure in Directed Networks. Physical Review Letters 100, + * 118703. https://doi.org/10.1103/PhysRevLett.100.118703 + * + * + * For the introduction of the resolution parameter, see Reichardt, J., and + * Bornholdt, S. (2006). Statistical mechanics of community detection. Physical + * Review E 74, 016110. https://doi.org/10.1103/PhysRevE.74.016110 + * + * \param graph The input graph. + * \param membership Numeric vector of integer values which gives the type of each + * vertex, i.e. the cluster to which it belongs. + * It does not have to be consecutive, i.e. empty communities + * are allowed. + * \param weights Weight vector or \c NULL if no weights are specified. + * \param resolution Resolution parameter. Must be greater than or equal to 0. + * Set it to 1 to use the classical definition of modularity. + * \param directed Whether to use the directed or undirected version of modularity. + * Ignored for undirected graphs. + * \param modularity Pointer to a real number, the result will be + * stored here. + * \return Error code. + * + * \sa \ref igraph_modularity_matrix() + * + * 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, + const igraph_vector_t *weights, + const igraph_real_t resolution, + const igraph_bool_t directed, + igraph_real_t *modularity) { + + igraph_vector_t e, k_out, k_in; + long int types; + long int no_of_edges = igraph_ecount(graph); + long int i; + igraph_real_t m; + long int c1, c2; + /* Only consider the graph as directed if it actually is directed */ + igraph_bool_t use_directed = directed && igraph_is_directed(graph); + igraph_real_t directed_multiplier = (use_directed ? 1 : 2); + + if (igraph_vector_size(membership) != igraph_vcount(graph)) { + IGRAPH_ERROR("Membership vector size differs from number of vertices.", + IGRAPH_EINVAL); + } + if (resolution < 0.0) { + IGRAPH_ERROR("The resolution parameter must be non-negative.", IGRAPH_EINVAL); + } + + if (no_of_edges == 0) { + /* Special case: the modularity of graphs with no edges is not + * well-defined */ + if (modularity) { + *modularity = IGRAPH_NAN; + } + return IGRAPH_SUCCESS; + } + + /* At this point, the 'membership' vector does not have length zero, + thus it is safe to call igraph_vector_max() and min(). */ + + types = (long int) igraph_vector_max(membership) + 1; + + if (igraph_vector_min(membership) < 0) { + IGRAPH_ERROR("Invalid membership vector: negative entry.", IGRAPH_EINVAL); + } + + IGRAPH_VECTOR_INIT_FINALLY(&e, types); + IGRAPH_VECTOR_INIT_FINALLY(&k_out, types); + IGRAPH_VECTOR_INIT_FINALLY(&k_in, types); + + if (weights) { + if (igraph_vector_size(weights) != no_of_edges) + IGRAPH_ERROR("Vector size differs from number of edges.", + IGRAPH_EINVAL); + m = 0.0; + 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); + } + c1 = (long int) VECTOR(*membership)[ IGRAPH_FROM(graph, i) ]; + c2 = (long int) VECTOR(*membership)[ IGRAPH_TO(graph, i) ]; + if (c1 == c2) { + VECTOR(e)[c1] += directed_multiplier * w; + } + VECTOR(k_out)[c1] += w; + VECTOR(k_in)[c2] += w; + m += w; + } + } else { + m = no_of_edges; + for (i = 0; i < no_of_edges; i++) { + c1 = (long int) VECTOR(*membership)[ IGRAPH_FROM(graph, i) ]; + c2 = (long int) VECTOR(*membership)[ IGRAPH_TO(graph, i) ]; + if (c1 == c2) { + VECTOR(e)[c1] += directed_multiplier; + } + VECTOR(k_out)[c1] += 1; + VECTOR(k_in)[c2] += 1; + } + } + + if (!use_directed) { + /* Graph is undirected, simply add vectors */ + igraph_vector_add(&k_out, &k_in); + igraph_vector_update(&k_in, &k_out); + } + + /* Divide all vectors by total weight. */ + igraph_vector_scale(&k_out, 1.0/( directed_multiplier * m ) ); + igraph_vector_scale(&k_in, 1.0/( directed_multiplier * m ) ); + igraph_vector_scale(&e, 1.0/( directed_multiplier * m ) ); + + *modularity = 0.0; + if (m > 0) { + for (i = 0; i < types; i++) { + *modularity += VECTOR(e)[i]; + *modularity -= resolution * VECTOR(k_out)[i] * VECTOR(k_in)[i]; + } + } + + igraph_vector_destroy(&e); + igraph_vector_destroy(&k_out); + igraph_vector_destroy(&k_in); + IGRAPH_FINALLY_CLEAN(3); + + return IGRAPH_SUCCESS; +} + +static int igraph_i_modularity_matrix_get_adjacency( + const igraph_t *graph, igraph_matrix_t *res, + const igraph_vector_t *weights, igraph_bool_t directed) { + /* Specifically used to handle weights and/or ignore direction */ + igraph_eit_t edgeit; + long int no_of_nodes = igraph_vcount(graph); + igraph_integer_t from, to; + + 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(IGRAPH_EDGEORDER_ID), &edgeit)); + IGRAPH_FINALLY(igraph_eit_destroy, &edgeit); + + if (weights) { + for (; !IGRAPH_EIT_END(edgeit); IGRAPH_EIT_NEXT(edgeit)) { + igraph_integer_t edge = IGRAPH_EIT_GET(edgeit); + from = IGRAPH_FROM(graph, edge); + to = IGRAPH_TO(graph, edge); + MATRIX(*res, from, to) += VECTOR(*weights)[edge]; + if (!directed) { + MATRIX(*res, to, from) += VECTOR(*weights)[edge]; + } + } + } else { + for (; !IGRAPH_EIT_END(edgeit); IGRAPH_EIT_NEXT(edgeit)) { + igraph_integer_t edge = IGRAPH_EIT_GET(edgeit); + igraph_edge(graph, edge, &from, &to); + MATRIX(*res, from, to) += 1; + if (!directed) { + MATRIX(*res, to, from) += 1; + } + } + } + + igraph_eit_destroy(&edgeit); + IGRAPH_FINALLY_CLEAN(1); + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_modularity_matrix + * \brief Calculate the modularity matrix + * + * This function returns the modularity matrix defined as + * + * + * B_ij = A_ij - gamma * k_i * k_j / (2m) + * + * + * for undirected graphs, where \c A_ij is the adjacency matrix, \c gamma is the + * resolution parameter, \c k_i is the degree of vertex \c i, and \c m is the + * number of edges in the graph. When there are no edges, or the weights add up + * to zero, the result is undefined. + * + * + * For directed graphs the modularity matrix is changed to + * + * + * B_ij = A_ij - gamma * k^out_i * k^in_j / m + * where k^out_i is the out-degree of node \c i and k^in_j is the + * in-degree of node \c j. + * + * + * Note that self-loops in undirected graphs are multiplied by 2 in this + * implementation. If weights are specified, the weighted counterparts are used. + * + * \param graph The input graph. + * \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. + * \param resolution Resolution parameter. Must be greater than or equal to 0. + * Default is 1. Lower values favor fewer, larger communities; + * higher values favor more, smaller communities. + * \param modmat Pointer to an initialized matrix in which the modularity + * matrix is stored. + * \param directed For directed graphs: if the edges should be treated as + * undirected. + * For undirected graphs this is ignored. + * + * \sa \ref igraph_modularity() + */ +int igraph_modularity_matrix(const igraph_t *graph, + const igraph_vector_t *weights, + const igraph_real_t resolution, + igraph_matrix_t *modmat, + igraph_bool_t directed) { + + 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, deg_unscaled, in_deg, out_deg; + long int i, j; + igraph_real_t scaling_factor; + if (weights && igraph_vector_size(weights) != no_of_edges) { + IGRAPH_ERROR("Invalid weight vector length.", IGRAPH_EINVAL); + } + + if (resolution < 0.0) { + IGRAPH_ERROR("The resolution parameter must be non-negative.", IGRAPH_EINVAL); + } + + if (!igraph_is_directed(graph)) { + directed = 0; + } + IGRAPH_CHECK(igraph_i_modularity_matrix_get_adjacency(graph, modmat, weights, directed)); + + if (directed) { + IGRAPH_VECTOR_INIT_FINALLY(&in_deg, no_of_nodes); + IGRAPH_VECTOR_INIT_FINALLY(&out_deg, no_of_nodes); + if (!weights) { + IGRAPH_CHECK(igraph_degree(graph, &in_deg, igraph_vss_all(), IGRAPH_IN, + IGRAPH_LOOPS)); + IGRAPH_CHECK(igraph_degree(graph, &out_deg, igraph_vss_all(), IGRAPH_OUT, + IGRAPH_LOOPS)); + } else { + IGRAPH_CHECK(igraph_strength(graph, &in_deg, igraph_vss_all(), IGRAPH_IN, + IGRAPH_LOOPS, weights)); + IGRAPH_CHECK(igraph_strength(graph, &out_deg, igraph_vss_all(), IGRAPH_OUT, + IGRAPH_LOOPS, weights)); + } + /* Scaling one degree factor so every element gets scaled. */ + scaling_factor = resolution / sw; + igraph_vector_scale(&out_deg, scaling_factor); + + for (j = 0; j < no_of_nodes; j++) { + for (i = 0; i < no_of_nodes; i++) { + MATRIX(*modmat, i, j) -= VECTOR(out_deg)[i] * VECTOR(in_deg)[j]; + } + } + igraph_vector_destroy(&in_deg); + igraph_vector_destroy(&out_deg); + IGRAPH_FINALLY_CLEAN(2); + } else { + IGRAPH_VECTOR_INIT_FINALLY(°, no_of_nodes); + if (!weights) { + IGRAPH_CHECK(igraph_degree(graph, °, igraph_vss_all(), IGRAPH_ALL, + IGRAPH_LOOPS)); + } else { + IGRAPH_CHECK(igraph_strength(graph, °, igraph_vss_all(), IGRAPH_ALL, + IGRAPH_LOOPS, weights)); + } + + /* Scaling one degree factor so every element gets scaled. */ + igraph_vector_copy(°_unscaled, °); + IGRAPH_FINALLY(igraph_vector_destroy, °_unscaled); + scaling_factor = resolution / 2.0 / sw; + igraph_vector_scale(°, scaling_factor); + 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_unscaled)[j]; + } + } + igraph_vector_destroy(°); + igraph_vector_destroy(°_unscaled); + IGRAPH_FINALLY_CLEAN(2); + } + + return IGRAPH_SUCCESS; +} diff --git a/src/rigraph/core/community/optimal_modularity.c b/src/rigraph/core/community/optimal_modularity.c new file mode 100644 index 0000000..cdb9328 --- /dev/null +++ b/src/rigraph/core/community/optimal_modularity.c @@ -0,0 +1,281 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2010-2012 Gabor Csardi + 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_error.h" +#include "igraph_interface.h" +#include "igraph_structural.h" + +#include "core/interruption.h" +#include "internal/glpk_support.h" + +#include "config.h" + +#ifdef HAVE_GLPK + #include +#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. + * + * + * 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. + * + * + * 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) { + if (igraph_vector_size(weights) != no_of_edges) { + IGRAPH_ERROR("Weight vector length must agree with number of edges.", IGRAPH_EINVAL); + } + if (no_of_edges > 0) { + /* Must not call vector_min on empty vector */ + igraph_real_t minweight = igraph_vector_min(weights); + if (minweight < 0) { + IGRAPH_ERROR("Negative weights are not allowed in weight vector.", IGRAPH_EINVAL); + } + if (igraph_is_nan(minweight)) { + IGRAPH_ERROR("Weights must not be NaN.", IGRAPH_EINVAL); + } + } + } + + /* Avoid problems with the null graph */ + if (no_of_nodes < 2) { + if (membership) { + IGRAPH_CHECK(igraph_vector_resize(membership, no_of_nodes)); + igraph_vector_fill(membership, 0); + } + if (modularity) { + IGRAPH_CHECK(igraph_modularity(graph, membership, 0, 1, igraph_is_directed(graph), modularity)); + } + return IGRAPH_SUCCESS; + } + + 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)); + + IGRAPH_GLPK_SETUP(); + + ip = glp_create_prob(); + IGRAPH_FINALLY(igraph_i_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 IGRAPH_SUCCESS; + +#endif + +} diff --git a/src/rigraph/core/community/spinglass/NetDataTypes.cpp b/src/rigraph/core/community/spinglass/NetDataTypes.cpp new file mode 100644 index 0000000..3156d35 --- /dev/null +++ b/src/rigraph/core/community/spinglass/NetDataTypes.cpp @@ -0,0 +1,218 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2006-2012 Gabor Csardi + 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. * + * * + ***************************************************************************/ + +#include "NetDataTypes.h" +#include + +//################################################################################# +//############################################################################### +//Constructor +NNode::NNode(unsigned long ind, unsigned long c_ind, DLList *ll, const char *n, int states) { + index = ind; + cluster_index = c_ind; + neighbours = new DLList(); + n_links = new DLList(); + 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 iter; + NLink *l_cur, *link = NULL; + 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() { +// long neighbours1, neighbours2; + double lam = 0; + DLList_Iter 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/src/rigraph/core/community/spinglass/NetDataTypes.h b/src/rigraph/core/community/spinglass/NetDataTypes.h new file mode 100644 index 0000000..a36cafd --- /dev/null +++ b/src/rigraph/core/community/spinglass/NetDataTypes.h @@ -0,0 +1,950 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2006-2012 Gabor Csardi + 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 + +//########################################################################################### + +struct HUGE_INDEX { + unsigned int field_index; + unsigned long in_field_index; +}; + +template 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(); + ~HugeArray(); + DATA &operator[](unsigned long); + unsigned long Size() { + return max_index; + } +} ; +//############################################################################################### +template class DLList; +template class DL_Indexed_List; +template class ClusterList; +template class DLList_Iter; + +template +class DLItem { + friend class DLList ; + friend class DL_Indexed_List; + friend class DLList_Iter; +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 *p, DLItem *n); + ~DLItem(); +public: + void del() { + delete item; + } +}; + +template +class DLList { + friend class DLList_Iter; +protected: + DLItem *head; + DLItem *tail; + unsigned long number_of_items; + DLItem *pInsert(L_DATA, DLItem*); + L_DATA pDelete(DLItem*); +public: + DLList(); + ~DLList(); + unsigned long Size() { + 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(); + L_DATA Get(unsigned long); + int Enqueue(L_DATA); + L_DATA Dequeue(); + unsigned long Is_In_List(L_DATA); + void delete_items(); +}; + +template +class DL_Indexed_List : virtual public DLList { + friend class DLList_Iter; +private: + DLItem *pInsert(L_DATA, DLItem*); + L_DATA pDelete(DLItem*); + HugeArray*> array; + unsigned long last_index; +public: + DL_Indexed_List(); + ~DL_Indexed_List(); + L_DATA Push(L_DATA); + L_DATA Pop(); + L_DATA Get(unsigned long); +}; + +//##################################################################################################### + +template class DLList_Iter { +private: + DLList *list; + DLItem *current; + bool end_reached; +public: + DLList_Iter(); + ~DLList_Iter() { + end_reached = true; + }; + L_DATA Next(); + L_DATA Previous(); + L_DATA First(DLList *l); + L_DATA Last(DLList *l); + bool End() { + return end_reached; + } + DLItem *Get_Current() { + return current; + } + L_DATA Get_Current_Item() { + return current->item; + } + void Set_Current(DLItem *c) { + current = c; + } + void Set_Status(bool s) { + end_reached = s; + } + bool Swap(DLList_Iter); //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 *neighbours; //list with pointers to neighbours + DLList *n_links; + DLList *global_link_list; + char name[255]; + RGBcolor color; +public : + NNode(unsigned long, unsigned long, DLList*, const char*, int); + ~NNode(); + unsigned long Get_Index() { + return (index); + } + unsigned long Get_ClusterIndex() { + return (cluster_index); + } + unsigned long Get_Marker() { + return marker; + } + void Set_Marker(unsigned long m) { + marker = m; + } + unsigned long Get_Affiliations() { + return affiliations; + } + void Set_Affiliations(unsigned long m) { + affiliations = m; + } + void Set_ClusterIndex(unsigned long ci) { + cluster_index = ci; + } + void Set_Index(unsigned long i) { + index = i; + } + unsigned long Get_Degree() { + return (neighbours->Size()); + } + char *Get_Name() { + return name; + } + void Set_Name(char* n) { + strcpy(name, n); + } + double Get_Links_Among_Neigbours(); + double Get_Clustering(); + double Get_Weight() { + return weight; + } + double Get_Affinity() { + return affinity; + } + unsigned long *Get_StateHistory() { + return state_history; + } + void Add_StateHistory(unsigned int q); + // double Get_OldWeight() {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() { + return distance; + } + void Set_Distance(long d) { + distance = d; + } + int Connect_To(NNode*, double); + DLList *Get_Neighbours() { + return neighbours; + } + DLList *Get_Links() { + return n_links; + } + int Disconnect_From(NNode*); + int Disconnect_From_All(); + bool Is_Linked_To(NNode*); + RGBcolor Get_Color() { + 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() { + return (start->Get_Index()); + } + unsigned long Get_End_Index() { + return (end->Get_Index()); + } + NNode *Get_Start() { + return (start); + } + NNode *Get_End() { + return (end); + } + double Get_Weight() { + return weight; + } + void Set_Weight(double w) { + weight = w; + } + double Get_OldWeight() { + return old_weight; + } + void Set_OldWeight(double w) { + old_weight = w; + } + unsigned long Get_Marker() { + 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 ClusterList : public DLList { + friend class DLList_Iter; +private: + long links_out_of_cluster; + unsigned long links_inside_cluster; + unsigned long frequency; + double cluster_energy; + DLList *candidates; + long marker; +public: + ClusterList(); + ~ClusterList(); + long Get_Links_OOC() { + return (links_out_of_cluster); + } + void Set_Links_OOC(long looc) { + links_out_of_cluster = looc; + } + unsigned long Get_Links_IC() { + return (links_inside_cluster); + } + unsigned long Get_Frequency() { + return (frequency); + } + void IncreaseFrequency() { + frequency++; + } + void Set_Links_IC(unsigned long lic) { + links_inside_cluster = lic; + } + double Get_Energy() { + return (cluster_energy); + } + void Set_Energy(double e) { + cluster_energy = e; + } + DLList *Get_Candidates() { + return candidates; + } + bool operator<(ClusterList &b); + bool operator==(ClusterList &b); + long Get_Marker() { + return marker; + } + void Set_Marker(long m) { + marker = m; + } +}; +//##################################################################################################### +template +class DL_Node_List : virtual public DL_Indexed_List { + friend class DLList_Iter; +private: + DLItem *pInsert(NNode*, DLItem*); + NNode* pDelete(DLItem*); + HugeArray*> array; + unsigned long last_index; +public: + DL_Node_List(); + ~DL_Node_List(); + NNode* Push(NNode*); + NNode* Pop(); + NNode* Get(unsigned long); + int Delete(unsigned long); + +}; +//##################################################################################################### + + + +struct cluster_join_move { + ClusterList *c1; + ClusterList *c2; + double joint_energy; + long joint_looc; + unsigned long joint_lic; +} ; + +struct network { + DL_Indexed_List *node_list; + DL_Indexed_List *link_list; + DL_Indexed_List*> *cluster_list; + // DL_Indexed_List *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; + + network() { + node_list = new DL_Indexed_List(); + link_list = new DL_Indexed_List(); + cluster_list = new DL_Indexed_List*>(); + } + + ~network() { + ClusterList *cl_cur; + + while (link_list->Size()) { + delete link_list->Pop(); + } + while (node_list->Size()) { + delete node_list->Pop(); + } + while (cluster_list->Size()) { + cl_cur = cluster_list->Pop(); + while (cl_cur->Size()) { + cl_cur->Pop(); + } + delete cl_cur; + } + delete link_list; + delete node_list; + delete cluster_list; + } +}; + +/* +struct network +{ + DLList *node_list; + DLList *link_list; + DLList*> *cluster_list; + DLList *moveset; +} ; +*/ + +template +HugeArray::HugeArray() { + max_bit_left = 1UL << 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 HugeArray::~HugeArray() { + for (unsigned int i = 0; i <= highest_field_index; i++) { + data = fields[i]; + delete [] data; + } +} + +template +HUGE_INDEX HugeArray::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 = 1UL << 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 +DATA &HugeArray::Set(unsigned long int index) { + HUGE_INDEX h_index; + unsigned long data_size; + while (size < index + 1) { + highest_field_index++; + data_size = 1UL << 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 +DATA HugeArray::Get(unsigned long index) { + return (Set(index)); +} + + +template +DATA &HugeArray::operator[](unsigned long index) { + return (Set(index)); +} + + +//############################################################################### +template +DLItem::DLItem(L_DATA i, unsigned long ind) : item(i), index(ind), previous(0), next(0) { +} + +template +DLItem::DLItem(L_DATA i, unsigned long ind, DLItem *p, DLItem *n) : item(i), index(ind), previous(p), next(n) { +} + +template +DLItem::~DLItem() { +//delete item; //eigentlich muessten wir pruefen, ob item ueberhaupt ein Pointer ist... +//previous=NULL; +//next=NULL; +} + + +//###################################################################################################################### +template +DLList::DLList() { + head = tail = NULL; + number_of_items = 0; + head = new DLItem(NULL, 0); //fuer head und Tail gibt es das gleiche Array-Element!! Vorsicht!! + tail = new DLItem(NULL, 0); + if ( !head || !tail ) { + if (head) { + delete (head); + } + if (tail) { + delete (tail); + } + return; + } else { + head->next = tail; + tail->previous = head; + } +} + +template +DLList::~DLList() { + DLItem *cur = head, *next; + while (cur) { + next = cur->next; + delete (cur); + cur = next; + } + number_of_items = 0; + // printf("Liste Zerstoert!\n"); +} + +template +void DLList::delete_items() { + DLItem *cur, *next; + cur = this->head; + while (cur) { + next = cur->next; + cur->del(); + cur = next; + } + this->number_of_items = 0; +} + +//privates Insert +template +DLItem *DLList::pInsert(L_DATA data, DLItem *pos) { + DLItem *i = new DLItem(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 +L_DATA DLList::pDelete(DLItem *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 +int DLList::Insert(L_DATA data, unsigned long pos) { + if ((pos < 0) || (pos > (number_of_items))) { + return (0); + } + DLItem *cur = head; + while (pos--) { + cur = cur->next; + } + return (pInsert(data, cur) != 0); +} +//oeffentliche Delete +template +int DLList::Delete(unsigned long pos) { + if ((pos < 0) || (pos > (number_of_items))) { + return (0); + } + DLItem *cur = head; + while (pos--) { + cur = cur->next; + } + return (pDelete(cur) != 0); +} + +//oeffentliche Delete +template +int DLList::fDelete(L_DATA data) { + if ((number_of_items == 0) || (!data)) { + return (0); + } + DLItem *cur; + cur = head->next; + while ((cur != tail) && (cur->item != data)) { + cur = cur->next; + } + if (cur != tail) { + return (pDelete(cur) != 0); + } + return (0); +} + +template +L_DATA DLList::Push(L_DATA data) { + DLItem *tmp; + tmp = pInsert(data, tail); + if (tmp) { + return (tmp->item); + } + return (0); +} + +template +L_DATA DLList::Pop() { + return (pDelete(tail->previous)); +} + + +template +L_DATA DLList::Get(unsigned long pos) { + if ((pos < 1) || (pos > (number_of_items + 1))) { + return (0); + } +// return(array[pos]->item); + DLItem *cur = head; + while (pos--) { + cur = cur->next; + } + return (cur->item); +} + + +template +int DLList::Enqueue(L_DATA data) { + return (pInsert(data, tail) != 0); +} + +template +L_DATA DLList::Dequeue() { + return (pDelete(head->next)); +} + +//gibt Index des gesuchte Listenelement zurueck, besser waere eigentlich zeiger +template +unsigned long DLList::Is_In_List(L_DATA data) { + DLItem *cur = head, *next; + unsigned long pos = 0; + while (cur) { + next = cur->next; + if (cur->item == data) { + return (pos) ; + } + cur = next; + pos++; + } + return (0); +} + +//###################################################################################################################### +template +DL_Indexed_List::DL_Indexed_List() : DLList() { + last_index = 0; +} + +template +DL_Indexed_List::~DL_Indexed_List() { + /* This is already done by the DLList destructor */ + /* DLItem *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 +DLItem *DL_Indexed_List::pInsert(L_DATA data, DLItem *pos) { + DLItem *i = new DLItem(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 +L_DATA DL_Indexed_List::pDelete(DLItem *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 +L_DATA DL_Indexed_List::Push(L_DATA data) { + DLItem *tmp; + tmp = pInsert(data, this->tail); + if (tmp) { + return (tmp->item); + } + return (0); +} + +template +L_DATA DL_Indexed_List::Pop() { + return (pDelete(this->tail->previous)); +} + +template +L_DATA DL_Indexed_List::Get(unsigned long pos) { + if (pos > this->number_of_items - 1) { + return (0); + } + return (array[pos]->item); +} + +//####################################################################################### + +//************************************************************************************************************ +template +ClusterList::ClusterList() : DLList() { + links_out_of_cluster = 0; + links_inside_cluster = 0; + frequency = 1; + cluster_energy = 1e30; + candidates = new DLList(); + marker = 0; +} + +template +ClusterList::~ClusterList() { + while (candidates->Size()) { + candidates->Pop(); + } + delete candidates; +} + + +template +bool ClusterList::operator==(ClusterList &b) { + bool found = false; + L_DATA n_cur, n_cur_b; + DLList_Iter 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 +bool ClusterList::operator<(ClusterList &b) { + bool found = false; + L_DATA n_cur, n_cur_b; + DLList_Iter 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 +DLList_Iter::DLList_Iter() { + list = NULL; + current = NULL; + end_reached = true; +} + +template +L_DATA DLList_Iter::Next() { + current = current->next; + if (current == (list->tail)) { + end_reached = true; + } + return (current->item); +} + +template +L_DATA DLList_Iter::Previous() { + current = current->previous; + if (current == (list->head)) { + end_reached = true; + } + return (current->item); +} + +template +L_DATA DLList_Iter::First(DLList *l) { + list = l; + current = list->head->next; + if (current == (list->tail)) { + end_reached = true; + } else { + end_reached = false; + } + return (current->item); +} + +template +L_DATA DLList_Iter::Last(DLList *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 +bool DLList_Iter::Swap(DLList_Iter 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/src/rigraph/core/community/spinglass/NetRoutines.cpp b/src/rigraph/core/community/spinglass/NetRoutines.cpp new file mode 100644 index 0000000..5978ef6 --- /dev/null +++ b/src/rigraph/core/community/spinglass/NetRoutines.cpp @@ -0,0 +1,271 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2006-2012 Gabor Csardi + 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 "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; + char name[255]; + NNode *node1, *node2; + DLList_Iter iter; + igraph_vector_t edgelist; + long int no_of_nodes = (long int) igraph_vcount(graph); + long int no_of_edges = (long int) igraph_ecount(graph); + long int ii; + const char *empty = ""; + + IGRAPH_VECTOR_INIT_FINALLY(&edgelist, no_of_edges * 2); + IGRAPH_CHECK(igraph_get_edgelist(graph, &edgelist, 0 /* rowwise */)); + + for (ii = 0; ii < no_of_nodes; ii++) { + net->node_list->Push(new NNode(ii, 0, net->link_list, empty, states)); + } + + for (ii = 0; ii < no_of_edges; ii++) { + long int i1 = (long int)VECTOR(edgelist)[2 * ii]; + long int i2 = (long int)VECTOR(edgelist)[2 * ii + 1]; + igraph_real_t Links; + if (use_weights) { + Links = VECTOR(*weights)[ii]; + } else { + Links = 1.0; + } + + node1 = net->node_list->Get(i1); + sprintf(name, "%li", i1+1); + node1->Set_Name(name); + + node2 = net->node_list->Get(i2); + sprintf(name, "%li", i2+1); + 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; + + return IGRAPH_SUCCESS; +} + +//############################################################################################################### +void reduce_cliques(DLList*> *global_cluster_list, FILE *file) { + unsigned long size; + ClusterList *c_cur, *largest_c = NULL; + DLList*> *subsets; + DLList_Iter*> c_iter, sub_iter; + DLList_Iter 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*>(); + 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 *c_cur, *largest_c = NULL; + DLList_Iter*> 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 *iter; + maxdepth = newdepth = depth; + iter = new DLList_Iter; + 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 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/src/rigraph/core/community/spinglass/NetRoutines.h b/src/rigraph/core/community/spinglass/NetRoutines.h new file mode 100644 index 0000000..02030fb --- /dev/null +++ b/src/rigraph/core/community/spinglass/NetRoutines.h @@ -0,0 +1,60 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2006-2012 Gabor Csardi + 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*>*, FILE *file); +void reduce_cliques2(network*, bool, long ); +void clear_all_markers(network *net); + +#endif diff --git a/src/rigraph/core/community/spinglass/clustertool.cpp b/src/rigraph/core/community/spinglass/clustertool.cpp new file mode 100644 index 0000000..9829c1b --- /dev/null +++ b/src/rigraph/core/community/spinglass/clustertool.cpp @@ -0,0 +1,664 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2006-2012 Gabor Csardi + 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. * + * * + ***************************************************************************/ + +#include "NetDataTypes.h" +#include "NetRoutines.h" +#include "pottsmodel_2.h" + +#include "igraph_community.h" +#include "igraph_error.h" +#include "igraph_random.h" +#include "core/math.h" +#include "igraph_interface.h" +#include "igraph_components.h" +#include "core/interruption.h" +#include "core/exceptions.h" + +static 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); + +static 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 . + * + * + * 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 edges 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. The edge weights + * must be positive unless using the \c IGRAPH_SPINCOMM_IMP_NEG + * implementation. This condition is not verified by the function. + * \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, with the + * first cluster 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, i.e. 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 be small. + * \param parupdate A logical constant, whether to update all spins in + * parallel. The default for this argument was \c FALSE (i.e. 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 cooling 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 defines + * the null model based on which the actual clustering is done. If + * this is \c IGRAPH_SPINCOMM_UPDATE_SIMPLE then the random graph + * (i.e. 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 defines 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. + * + */ + +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) { + + IGRAPH_HANDLE_EXCEPTIONS( + 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); + } + ); +} + +static 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) { + + long int no_of_nodes = igraph_vcount(graph); + unsigned long changes, runs; + igraph_bool_t use_weights = 0; + bool zeroT; + double kT, acc, prob; + + /* Check arguments */ + + if (spins < 2) { + IGRAPH_ERROR("Number of spins must be at least 2", 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); + } + + /* The spinglass algorithm does not handle the trivial cases of the + null and singleton graphs, so we catch them here. */ + if (no_of_nodes < 2) { + if (membership) { + IGRAPH_CHECK(igraph_vector_resize(membership, no_of_nodes)); + igraph_vector_fill(membership, 0); + } + if (modularity) { + IGRAPH_CHECK(igraph_modularity(graph, membership, 0, 1, igraph_is_directed(graph), modularity)); + } + if (temperature) { + *temperature = stoptemp; + } + if (csize) { + /* 0 clusters for 0 nodes, 1 cluster for 1 node */ + IGRAPH_CHECK(igraph_vector_resize(membership, no_of_nodes)); + igraph_vector_fill(membership, 1); + } + return IGRAPH_SUCCESS; + } + + /* 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); + } + + network net; + + /* 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); + + PottsModel pm(&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(); + + 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); + + RNG_END(); + + 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 . + * + * + * 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_HANDLE_EXCEPTIONS( + igraph_bool_t use_weights = 0; + double prob; + char startnode[255]; + + /* Check arguments */ + + if (spins < 2) { + IGRAPH_ERROR("Number of spins must be at least 2", 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); + } + + network net; + + /* 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); + + PottsModel pm(&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); + + RNG_END(); + ); + + return 0; +} + +static 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) { + + long int no_of_nodes = igraph_vcount(graph); + unsigned long changes, runs; + igraph_bool_t use_weights = 0; + bool zeroT; + double kT, acc; + 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) { + IGRAPH_ERROR("Number of spins must be at least 2", 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); + } + + /* The spinglass algorithm does not handle the trivial cases of the + null and singleton graphs, so we catch them here. */ + if (no_of_nodes < 2) { + if (membership) { + IGRAPH_CHECK(igraph_vector_resize(membership, no_of_nodes)); + igraph_vector_fill(membership, 0); + } + if (modularity) { + IGRAPH_CHECK(igraph_modularity(graph, membership, 0, 1, igraph_is_directed(graph), modularity)); + } + if (temperature) { + *temperature = stoptemp; + } + if (csize) { + /* 0 clusters for 0 nodes, 1 cluster for 1 node */ + IGRAPH_CHECK(igraph_vector_resize(membership, no_of_nodes)); + igraph_vector_fill(membership, 1); + } + return IGRAPH_SUCCESS; + } + + /* 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; + + network net; + + /* Transform the igraph_t */ + IGRAPH_CHECK(igraph_i_read_network(graph, weights, + &net, use_weights, 0)); + + bool directed = igraph_is_directed(graph); + + PottsModelN pm(&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(); + + 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); + + RNG_END(); + + return 0; +} diff --git a/src/rigraph/core/community/spinglass/pottsmodel_2.cpp b/src/rigraph/core/community/spinglass/pottsmodel_2.cpp new file mode 100644 index 0000000..749aa6f --- /dev/null +++ b/src/rigraph/core/community/spinglass/pottsmodel_2.cpp @@ -0,0 +1,2236 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2006-2012 Gabor Csardi + 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 "pottsmodel_2.h" +#include "NetRoutines.h" + +#include "igraph_random.h" +#include "core/interruption.h" + +#include +#include + +using namespace std; + +//################################################################################################# +PottsModel::PottsModel(network *n, unsigned int qvalue, int m) : Qmatrix(qvalue+1), acceptance(0) +{ + DLList_Iter 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(); + previous_spins = new DL_Indexed_List(); + 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(); + } +} +//####################################################### +//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; +} +//##################################################### +//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 iter; + DLList_Iter l_iter; + NNode *n_cur; + NLink *l_cur; + double sum_weight; + double av_k_squared = 0.0; + double av_k = 0.0; + IGRAPH_UNUSED(av_k_squared); /* We mark it as unused to prevent warnings about unused-but-set-variables. */ + IGRAPH_UNUSED(av_k); /* We mark it as unused to prevent warnings about unused-but-set-variables. */ + +// 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 modularity 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() { + DLList_Iter 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 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 iter, net_iter; + DLList_Iter l_iter; + DLList_Iter 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 neighbours; + bool cyclic = false; + + 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 iter; + DLList_Iter l_iter; + DLList_Iter 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 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 iter, net_iter; + DLList_Iter l_iter; + DLList_Iter 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 neighbours; + double h, delta = 0, norm, r, beta, minweight, prefac = 0, w, degree; + bool cyclic = false, found; + unsigned long number_of_nodes; + + sweep = 0; + changes = 1; + number_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(number_of_nodes); + return 0; + } else { + acceptance = double(changes) / double(number_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 iter; + DLList_Iter l_iter; + DLList_Iter 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 neighbours; + double norm, r, beta, minweight, prefac = 0; + bool found; + long int number_of_nodes; + sweep = 0; + changes = 0; + number_of_nodes = net->node_list->Size(); + while (sweep < max_sweeps) { + sweep++; + //loop over all nodes in network + for (int n = 0; n < number_of_nodes; n++) { + rn = -1; + while ((rn < 0) || (rn > number_of_nodes - 1)) { + rn = RNG_INTEGER(0, number_of_nodes - 1); + } + /* rn=long(double(number_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(number_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 iter, iter2; + DLList_Iter l_iter; + DLList* to_do; + DLList* community; + NNode *start_node = NULL, *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; + community = new DLList; + + // 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 iter, iter2; + HugeArray inner_links; + HugeArray outer_links; + HugeArray 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\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 iter, iter2; +// DL_Indexed_List*> *cl_list, *old_clusterlist; +// ClusterList *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*>(); + +// n_cur=iter.First(net->node_list); +// while (!iter.End()) +// { +// cl_cur=new ClusterList(); +// 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 = 0.5, kT_start; + long changes; + double gamma, acc; + NNode *n_cur, *n_cur2; + DLList_Iter 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(); + 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 = gamma_start, acc; + long runs; + NNode *n_cur, *n_cur2; + DLList_Iter 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(); + 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 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) : + degree_pos_in(NULL), degree_neg_in(NULL), + degree_pos_out(NULL), degree_neg_out(NULL), + degree_community_pos_in(NULL), degree_community_neg_in(NULL), + degree_community_pos_out(NULL), degree_community_neg_out(NULL), + csize(NULL), spin(NULL), neighbours(NULL), weights(NULL) +{ + //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; +} + +void PottsModelN::assign_initial_conf(bool init_spins) { +#ifdef SPINGLASS_DEBUG + printf("Start assigning.\n"); +#endif + unsigned int s; + DLList_Iter iter; + DLList_Iter l_iter; + NNode *n_cur; + NLink *l_cur; + + + if (init_spins) { +#ifdef SPINGLASS_DEBUG + printf("Initializing spin.\n"); +#endif + // Free the arrays before (re-)allocating them + // These arrays are initialized to NULL, so it is safe to delete even before allocation + delete [] degree_pos_in; + delete [] degree_neg_in; + delete [] degree_pos_out; + delete [] degree_neg_out; + + delete [] spin; + + //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 SPINGLASS_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 SPINGLASS_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 SPINGLASS_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 SPINGLASS_DEBUG + printf("Done assigning.\n"); +#endif +} +//############################################################## +// 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 SPINGLASS_DEBUG + printf("Starting sweep at temperature %f.\n", t); +#endif + DLList_Iter iter; + DLList_Iter l_iter; + DLList_Iter 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 SPINGLASS_DEBUG + printf("Done %d sweeps.\n", max_sweeps); + printf("%ld 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 SPINGLASS_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++) { + unsigned int s = spin[i]; + if (cluster_assign[s] == 0) { + num_clusters++; + cluster_assign[s] = num_clusters; +#ifdef SPINGLASS_DEBUG + printf("Setting cluster %d to %d.\n", s, num_clusters); +#endif + } + } + + + /* + DLList_Iter 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 SPINGLASS_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 SPINGLASS_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 = NULL; + double **num_links_neg = NULL; + //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 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 SPINGLASS_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 SPINGLASS_DEBUG + printf("Finished writing cluster.\n"); +#endif + return num_nodes; +} diff --git a/src/rigraph/core/community/spinglass/pottsmodel_2.h b/src/rigraph/core/community/spinglass/pottsmodel_2.h new file mode 100644 index 0000000..7d0ee28 --- /dev/null +++ b/src/rigraph/core/community/spinglass/pottsmodel_2.h @@ -0,0 +1,179 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2006-2012 Gabor Csardi + 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" + +// Simple matrix class with heap allocation, allowing mat[i][j] indexing. +class SimpleMatrix { + double *data; + size_t n; + +public: + explicit SimpleMatrix(size_t n_) : n(n_) { data = new double[n*n]; } + ~SimpleMatrix() { delete [] data; } + + // Return a pointer to the i'th column, which can be indexed into using a second [] operator. + // We assume column-major storage. + double *operator [] (size_t i) { return &(data[n*i]); } +}; + +class PottsModel { +private: + // HugeArray neg_gammalookup; + // HugeArray pos_gammalookup; + DL_Indexed_List *new_spins; + DL_Indexed_List *previous_spins; + HugeArray*> correlation; + network *net; + unsigned int q; + unsigned int operation_mode; + // FILE *Qfile, *Magfile; + SimpleMatrix Qmatrix; + 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(); + double calculate_Q(); + 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() const { + 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 neg_gammalookup; + // HugeArray pos_gammalookup; + // DL_Indexed_List *new_spins; + // DL_Indexed_List *previous_spins; + HugeArray*> 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/src/rigraph/core/community/walktrap/walktrap.cpp b/src/rigraph/core/community/walktrap/walktrap.cpp new file mode 100644 index 0000000..12f71ea --- /dev/null +++ b/src/rigraph/core/community/walktrap/walktrap.cpp @@ -0,0 +1,177 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2007-2012 Gabor Csardi + 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 "igraph_community.h" +#include "igraph_components.h" +#include "igraph_interface.h" + +#include "core/exceptions.h" +#include "core/interruption.h" + +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, + * https://arxiv.org/abs/physics/0512106 + * + * + * Currently the original C++ implementation is used in igraph, + * see https://www-complexnetworks.lip6.fr/~latapy/PP/walktrap.html + * We are grateful to Matthieu Latapy and Pascal Pons for providing this + * source code. + * + * + * 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. + * Typically, good results are obtained with values between + * 3-8 with 4-5 being a reasonable default. + * \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) { + + IGRAPH_HANDLE_EXCEPTIONS( + long int no_of_nodes = (long int)igraph_vcount(graph); + int length = steps; + long max_memory = -1; + + if (steps <= 0) { + IGRAPH_ERROR("Length of random walks must be positive for walktrap community detection.", IGRAPH_EINVAL); + } + + if (membership && !(modularity && merges)) { + IGRAPH_ERROR("Cannot calculate membership without modularity or merges", + IGRAPH_EINVAL); + } + + Graph G; + 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(); + } + + if (membership) { + long int m; + m = no_of_nodes > 0 ? igraph_vector_which_max(modularity) : 0; + IGRAPH_CHECK(igraph_community_to_membership(merges, no_of_nodes, + /*steps=*/ m, + membership, + /*csize=*/ NULL)); + } + + return IGRAPH_SUCCESS; + ) +} diff --git a/src/rigraph/core/community/walktrap/walktrap_communities.cpp b/src/rigraph/core/community/walktrap/walktrap_communities.cpp new file mode 100644 index 0000000..846cb95 --- /dev/null +++ b/src/rigraph/core/community/walktrap/walktrap_communities.cpp @@ -0,0 +1,946 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2007-2012 Gabor Csardi + 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 "config.h" +#include +#include + +using namespace std; + +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 << "."; */ + /* } */ + /* } */ + } + + double 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; + } + } + + if (modularity) { + VECTOR(*modularity)[mergeidx] = Q; + } +} + +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/src/rigraph/core/community/walktrap/walktrap_communities.h b/src/rigraph/core/community/walktrap/walktrap_communities.h new file mode 100644 index 0000000..690d7aa --- /dev/null +++ b/src/rigraph/core/community/walktrap/walktrap_communities.h @@ -0,0 +1,175 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2007-2012 Gabor Csardi + 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 WALKTRAP_COMMUNITIES_H +#define WALKTRAP_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 // WALKTRAP_COMMUNITIES_H diff --git a/src/rigraph/core/community/walktrap/walktrap_graph.cpp b/src/rigraph/core/community/walktrap/walktrap_graph.cpp new file mode 100644 index 0000000..3f728f8 --- /dev/null +++ b/src/rigraph/core/community/walktrap/walktrap_graph.cpp @@ -0,0 +1,240 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2007-2012 Gabor Csardi + 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 "walktrap_graph.h" +#include "igraph_interface.h" +#include +#include // strlen + +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/src/rigraph/core/community/walktrap/walktrap_graph.h b/src/rigraph/core/community/walktrap/walktrap_graph.h new file mode 100644 index 0000000..7fe9574 --- /dev/null +++ b/src/rigraph/core/community/walktrap/walktrap_graph.h @@ -0,0 +1,104 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2007-2012 Gabor Csardi + 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 WALKTRAP_GRAPH_H +#define WALKTRAP_GRAPH_H + +#include "igraph_community.h" + +namespace igraph { + +namespace walktrap { + +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 // WALKTRAP_GRAPH_H diff --git a/src/rigraph/core/community/walktrap/walktrap_heap.cpp b/src/rigraph/core/community/walktrap/walktrap_heap.cpp new file mode 100644 index 0000000..983cb7b --- /dev/null +++ b/src/rigraph/core/community/walktrap/walktrap_heap.cpp @@ -0,0 +1,241 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2007-2012 Gabor Csardi + 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" + +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/src/rigraph/core/community/walktrap/walktrap_heap.h b/src/rigraph/core/community/walktrap/walktrap_heap.h new file mode 100644 index 0000000..b0702bd --- /dev/null +++ b/src/rigraph/core/community/walktrap/walktrap_heap.h @@ -0,0 +1,133 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2007-2012 Gabor Csardi + 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 WALKTRAP_HEAP_H +#define WALKTRAP_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 // WALKTRAP_HEAP_H diff --git a/src/rigraph/core/connectivity/cohesive_blocks.c b/src/rigraph/core/connectivity/cohesive_blocks.c new file mode 100644 index 0000000..62811f0 --- /dev/null +++ b/src/rigraph/core/connectivity/cohesive_blocks.c @@ -0,0 +1,602 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2010-2012 Gabor Csardi + 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_constructors.h" +#include "igraph_dqueue.h" +#include "igraph_flow.h" +#include "igraph_interface.h" +#include "igraph_memory.h" +#include "igraph_operators.h" +#include "igraph_separators.h" +#include "igraph_statusbar.h" +#include "igraph_structural.h" + +#include "core/interruption.h" + +static void igraph_i_cohesive_blocks_free_graphs(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); + } + } +} + +static void igraph_i_cohesive_blocks_free_vectors(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. + */ + +static 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 ik. 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. + * + * 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_graphs, &Q); + + IGRAPH_CHECK(igraph_vector_ptr_init(&Qmapping, 1)); + IGRAPH_FINALLY(igraph_vector_ptr_destroy, &Qmapping); + IGRAPH_FINALLY(igraph_i_cohesive_blocks_free_vectors, &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] = NULL; /* 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_free_vectors, &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_free_vectors(&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_free_vectors(&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/src/rigraph/core/connectivity/components.c b/src/rigraph/core/connectivity/components.c new file mode 100644 index 0000000..1f04d6b --- /dev/null +++ b/src/rigraph/core/connectivity/components.c @@ -0,0 +1,1423 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2003-2012 Gabor Csardi + 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_adjlist.h" +#include "igraph_dqueue.h" +#include "igraph_interface.h" +#include "igraph_memory.h" +#include "igraph_operators.h" +#include "igraph_progress.h" +#include "igraph_stack.h" +#include "igraph_structural.h" +#include "igraph_vector.h" + +#include "core/interruption.h" +#include "operators/subgraph.h" + +#include + +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); + } + + IGRAPH_ERROR("Cannot calculate clusters", IGRAPH_EINVAL); +} + +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_LOOPS_ONCE, IGRAPH_MULTIPLE)); + 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_LOOPS_ONCE, IGRAPH_MULTIPLE)); + 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; +} + +static 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 is considered connected when any of its vertices is reachable + * from any other. A directed graph with this property is called + * \em strongly connected. A directed graph that would be connected when + * ignoring the directions of its edges is called \em weakly connected. + * + * + * A graph with zero vertices (i.e. the null graph) is \em not connected by + * definition. This behaviour changed in igraph 0.9; earlier versions assumed + * that the null graph is connected. See the following issue on Github for the + * argument that led us to change the definition: + * https://github.com/igraph/igraph/issues/1539 + * + * \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) { + + long int no_of_nodes = igraph_vcount(graph); + + if (no_of_nodes == 0) { + /* Changed in igraph 0.9; see https://github.com/igraph/igraph/issues/1539 + * for the reasoning behind the change */ + *res = 0; + return IGRAPH_SUCCESS; + } + + if (no_of_nodes == 1) { + *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; + + /* A strongly connected graph has at least as many edges as vertices, + * except for the singleton graph, which is handled above. */ + if (igraph_ecount(graph) < no_of_nodes) { + *res = 0; + return IGRAPH_SUCCESS; + } + + retval = igraph_i_clusters_strong(graph, NULL, NULL, &no); + *res = (no == 1); + return retval; + } + + IGRAPH_ERROR("Invalid connectedness mode.", IGRAPH_EINVAL); +} + +static int igraph_is_connected_weak(const igraph_t *graph, igraph_bool_t *res) { + + long int no_of_nodes = igraph_vcount(graph), no_of_edges = igraph_ecount(graph); + long int added_count; + char *already_added; + igraph_vector_t neis = IGRAPH_VECTOR_NULL; + igraph_dqueue_t q = IGRAPH_DQUEUE_NULL; + + /* By convention, the null graph is not considered connected. + * See https://github.com/igraph/igraph/issues/1538 */ + if (no_of_nodes == 0) { + *res = 0; + return IGRAPH_SUCCESS; + } + + /* A connected graph has at least |V| - 1 edges. */ + if (no_of_edges < no_of_nodes - 1) { + *res = 0; + return IGRAPH_SUCCESS; + } + + already_added = IGRAPH_CALLOC(no_of_nodes, char); + if (already_added == 0) { + IGRAPH_ERROR("Weak connectedness check failed.", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, already_added); + + 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)); + + added_count = 1; + while ( !igraph_dqueue_empty(&q)) { + IGRAPH_ALLOW_INTERRUPTION(); + + long int actnode = (long int) igraph_dqueue_pop(&q); + + IGRAPH_CHECK(igraph_neighbors(graph, &neis, (igraph_integer_t) actnode, IGRAPH_ALL)); + long int nei_count = igraph_vector_size(&neis); + + for (long int i = 0; i < nei_count; i++) { + long int neighbor = (long int) VECTOR(neis)[i]; + if (already_added[neighbor]) { + continue; + } + + IGRAPH_CHECK(igraph_dqueue_push(&q, neighbor)); + added_count++; + already_added[neighbor] = 1; + + if (added_count == no_of_nodes) { + /* We have already reached all nodes: the graph is connected. + * We can stop the traversal now. */ + igraph_dqueue_clear(&q); + break; + } + } + } + + /* Connected? */ + *res = (added_count == no_of_nodes); + + IGRAPH_FREE(already_added); + igraph_dqueue_destroy(&q); + igraph_vector_destroy(&neis); + IGRAPH_FINALLY_CLEAN(3); + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_decompose_destroy + * \brief Free the memory allocated by \ref igraph_decompose(). + * + * This function destroys and frees all igraph_t + * objects held in \p complist. However, it does not destroy + * \p complist itself, as it was not allocated by \ref igraph_decompose(). + * Use \ref igraph_vector_ptr_destroy() to destroy \p complist. + * + * \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 \ref igraph_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. + * + * 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); + } + + IGRAPH_ERROR("Cannot decompose graph", IGRAPH_EINVAL); +} + +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; + igraph_vector_t vids_old2new; + 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); + IGRAPH_VECTOR_INIT_FINALLY(&vids_old2new, no_of_nodes); + + /* vids_old2new would have been created internally in igraph_induced_subgraph(), + but it is slow if the graph is large and consists of many small components, + so we create it once here and then re-use it */ + + /* 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_i_induced_subgraph_map( + graph, newg, igraph_vss_vector(&verts), + IGRAPH_SUBGRAPH_AUTO, &vids_old2new, + /* invmap = */ 0, /* map_is_prepared = */ 1 + )); + resco++; + + /* vids_old2new does not have to be cleaned up here; since we are doing + * weak decomposition, each vertex will appear in only one of the + * connected components so we won't ever touch an item in vids_old2new + * if it was already set to a non-zero value in a previous component */ + + } /* for actstart++ */ + + igraph_vector_destroy(&vids_old2new); + igraph_vector_destroy(&neis); + igraph_vector_destroy(&verts); + igraph_dqueue_destroy(&q); + IGRAPH_FREE(already_added); + IGRAPH_FINALLY_CLEAN(6); /* + 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 = 0; + 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_vector_t vids_old2new; + igraph_t *newg; + + if (maxcompno < 0) { + maxcompno = LONG_MAX; + } + + igraph_vector_ptr_clear(components); + IGRAPH_FINALLY(igraph_decompose_destroy, components); + + /* The result */ + + IGRAPH_VECTOR_INIT_FINALLY(&vids_old2new, no_of_nodes); + 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_LOOPS_ONCE, IGRAPH_MULTIPLE)); + IGRAPH_FINALLY(igraph_adjlist_destroy, &adjlist); + + /* vids_old2new would have been created internally in igraph_induced_subgraph(), + but it is slow if the graph is large and consists of many small components, + so we create it once here and then re-use it */ + + /* 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_LOOPS_ONCE, IGRAPH_MULTIPLE)); + 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) && no_of_clusters < maxcompno) { + /* 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_i_induced_subgraph_map( + graph, newg, igraph_vss_vector(&verts), + IGRAPH_SUBGRAPH_AUTO, &vids_old2new, + /* invmap = */ 0, /* map_is_prepared = */ 1 + )); + + /* vids_old2new has to be cleaned up here because a vertex may appear + * in multiple strongly connected components. Simply calling + * igraph_vector_fill() would be an O(n) operation where n is the number + * of vertices in the large graph so we cannot do that; we have to + * iterate over 'verts' instead */ + n = igraph_vector_size(&verts); + for (i = 0; i < n; i++) { + VECTOR(vids_old2new)[(igraph_integer_t) VECTOR(verts)[i]] = 0; + } + + no_of_clusters++; + } + + IGRAPH_PROGRESS("Strongly connected components: ", 100.0, NULL); + + /* Clean up, return */ + + igraph_vector_destroy(&vids_old2new); + igraph_vector_destroy(&verts); + igraph_adjlist_destroy(&adjlist); + igraph_vector_destroy(&out); + igraph_dqueue_destroy(&q); + igraph_vector_destroy(&next_nei); + IGRAPH_FINALLY_CLEAN(7); /* + 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. + * + * + * 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. + * + * + * Somewhat arbitrarily, igraph does not consider components 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 \ref igraph_free() 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_LOOPS_TWICE)); + 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 */ +/* The algorithm is based on https://www.geeksforgeeks.org/bridge-in-a-graph/ + but instead of keeping track of the parent of each vertex in the DFS tree + we keep track of its incoming edge. This is necessary to support multigraphs. */ + +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 *incoming_edge) +{ + 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(*incoming_edge)[v] = edge; + IGRAPH_CHECK(igraph_i_bridges_rec(graph, il, v, time, bridges, visited, disc, low, incoming_edge)); + + 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 (edge != VECTOR(*incoming_edge)[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 incoming_edge; + long n; + long i; + igraph_integer_t time; + + n = igraph_vcount(graph); + + IGRAPH_CHECK(igraph_inclist_init(graph, &il, IGRAPH_ALL, IGRAPH_LOOPS_TWICE)); + 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(&incoming_edge, n)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &incoming_edge); + for (i = 0; i < n; ++i) { + VECTOR(incoming_edge)[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, &incoming_edge)); + } + + igraph_vector_int_destroy(&incoming_edge); + 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; +} + +/** + * \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_induced_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, vsize; + igraph_vector_t tmp = IGRAPH_VECTOR_NULL; + + if (!IGRAPH_FINITE(vertex) || vertex < 0 || vertex >= no_of_nodes) { + IGRAPH_ERROR("Vertex id out of range.", 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(igraph_free, already_added); + + 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)); + vsize = igraph_vector_size(&tmp); + for (i = 0; i < vsize; 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 IGRAPH_SUCCESS; +} diff --git a/src/rigraph/core/connectivity/separators.c b/src/rigraph/core/connectivity/separators.c new file mode 100644 index 0000000..bdc7a24 --- /dev/null +++ b/src/rigraph/core/connectivity/separators.c @@ -0,0 +1,853 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2010-2012 Gabor Csardi + 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_adjlist.h" +#include "igraph_components.h" +#include "igraph_dqueue.h" +#include "igraph_flow.h" +#include "igraph_interface.h" +#include "igraph_memory.h" +#include "igraph_operators.h" +#include "igraph_structural.h" +#include "igraph_vector.h" + +#include "core/interruption.h" + +static 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 + * \brief Would removing this set of vertices disconnect the graph? + * + * \param graph The input graph. It may be directed, but edge + * directions are ignored. + * \param candidate 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 + * \brief 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. + * + * 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) + +static 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; +} + +static 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; +} + +static 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; +} + +static 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 + * \brief 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. + * + * + * Note that some vertex sets returned by this function may not be minimal + * with respect to disconnecting the graph (or increasing the number of + * connected components). Take for example the 5-vertex graph with edges + * 0-1-2-3-4-1. This function returns the vertex sets + * {1}, {2,4} and {1,3}. + * Notice that {1,3} is not minimal with respect to disconnecting + * the graph, as {1} would be sufficient for that. However, it is + * minimal with respect to separating vertices \c 2 and \c 4. + * + * + * 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. + * https://doi.org/10.1007/3-540-46784-X_17 + * + * \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 \p 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. + * + * \sa \ref igraph_minimum_size_separators() + * + * 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_LOOPS_TWICE, IGRAPH_MULTIPLE)); + 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 + +static 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; +} + +static 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(°, no_of_nodes); + IGRAPH_VECTOR_INIT_FINALLY(&order, no_of_nodes); + IGRAPH_CHECK(igraph_degree(graph, °, igraph_vss_all(), IGRAPH_ALL, + /*loops=*/ 0)); + + IGRAPH_CHECK(igraph_vector_order1(°, &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(°); + IGRAPH_FINALLY_CLEAN(2); + + return 0; +} + +static 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 + * \brief 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. + * + * + * 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); + IGRAPH_CHECK(igraph_simplify(&graph_copy, /* multiple */ 1, /* loops */ 1, NULL)); + + /* ---------------------------------------------------------------- */ + /* 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_copy, &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_CHECK(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 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 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, const 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; +} + +static int igraph_i_weighted_adjacency_directed( + const igraph_matrix_t *adjmatrix, + igraph_vector_t *edges, + igraph_vector_t *weights, + igraph_bool_t loops); +static int igraph_i_weighted_adjacency_plus( + const igraph_matrix_t *adjmatrix, + igraph_vector_t *edges, + igraph_vector_t *weights, + igraph_bool_t loops); +static int igraph_i_weighted_adjacency_max( + const igraph_matrix_t *adjmatrix, + igraph_vector_t *edges, + igraph_vector_t *weights, + igraph_bool_t loops); +static int igraph_i_weighted_adjacency_upper( + const igraph_matrix_t *adjmatrix, + igraph_vector_t *edges, + igraph_vector_t *weights, + igraph_bool_t loops); +static int igraph_i_weighted_adjacency_lower( + const igraph_matrix_t *adjmatrix, + igraph_vector_t *edges, + igraph_vector_t *weights, + igraph_bool_t loops); +static int igraph_i_weighted_adjacency_min( + const igraph_matrix_t *adjmatrix, + igraph_vector_t *edges, + igraph_vector_t *weights, + igraph_bool_t loops); + +static 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; +} + +static 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; +} + +static 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; +} + +static 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; +} + +static 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; +} + +static 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 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, const 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; +} + +/** + * \function igraph_adjlist + * \brief Creates 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; +} diff --git a/src/rigraph/core/constructors/atlas-edges.h b/src/rigraph/core/constructors/atlas-edges.h new file mode 100644 index 0000000..229e8f1 --- /dev/null +++ b/src/rigraph/core/constructors/atlas-edges.h @@ -0,0 +1,1296 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2006-2012 Gabor Csardi + 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/src/rigraph/core/constructors/atlas.c b/src/rigraph/core/constructors/atlas.c new file mode 100644 index 0000000..ffa2eae --- /dev/null +++ b/src/rigraph/core/constructors/atlas.c @@ -0,0 +1,82 @@ +/* -*- mode: C -*- */ +/* + IGraph R package. + Copyright (C) 2006-2012 Gabor Csardi + 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 "constructors/atlas-edges.h" + +/** + * \function igraph_atlas + * \brief Create a small graph from the \quote Graph Atlas \endquote. + * + * + * 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 < 112222; + * \oli for fixed degree sequence, in increasing number of + * automorphisms. + * \endolist + * + * + * The data was converted from the NetworkX software package, + * see http://networkx.github.io . + * + * + * 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. + * + * 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/src/rigraph/core/constructors/basic_constructors.c b/src/rigraph/core/constructors/basic_constructors.c new file mode 100644 index 0000000..ff5e59c --- /dev/null +++ b/src/rigraph/core/constructors/basic_constructors.c @@ -0,0 +1,149 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2005-2021 The igraph development 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., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_constructors.h" + +#include "igraph_interface.h" + +/** + * \section about_generators + * + * Graph generators create graphs. + * + * Almost all functions which create graph objects are documented + * here. The exceptions are \ref igraph_induced_subgraph() and alike, these + * create graphs based on another graph. + */ + + +/** + * \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; +} + +/** + * \function igraph_small + * \brief Shorthand to create a small graph, giving the edges as arguments. + * + * + * 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. + * + * 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; +} diff --git a/src/rigraph/core/constructors/de_bruijn.c b/src/rigraph/core/constructors/de_bruijn.c new file mode 100644 index 0000000..1a1abe0 --- /dev/null +++ b/src/rigraph/core/constructors/de_bruijn.c @@ -0,0 +1,99 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2005-2021 The igraph development 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., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_constructors.h" + +#include "igraph_interface.h" + +/** + * \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. + * + * + * 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. + * + * + * De Bruijn graphs have some interesting properties, please see another source, + * e.g. 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; +} diff --git a/src/rigraph/core/constructors/famous.c b/src/rigraph/core/constructors/famous.c new file mode 100644 index 0000000..101878d --- /dev/null +++ b/src/rigraph/core/constructors/famous.c @@ -0,0 +1,498 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2005-2021 The igraph development 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., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include + +#include "igraph_constructors.h" + +#include "internal/hacks.h" + +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 +}; + +static 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. + * + * + * 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, e.g. \ref + * igraph_full() for creating a full graph. + * + * + * 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, i.e. it is + * non-hamiltonian but removing any single vertex from it makes it + * Hamiltonian. + * \cli Robertson + * The unique (4,5)-cage graph, i.e. 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, \c 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); + } + + IGRAPH_ERRORF("%s is not a known graph. See the documentation for valid graph names.", IGRAPH_EINVAL, name); +} diff --git a/src/rigraph/core/constructors/full.c b/src/rigraph/core/constructors/full.c new file mode 100644 index 0000000..bddcfb5 --- /dev/null +++ b/src/rigraph/core/constructors/full.c @@ -0,0 +1,154 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2005-2021 The igraph development 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., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_constructors.h" + +#include "igraph_interface.h" + +/** + * \ingroup generators + * \function igraph_full + * \brief Creates a full graph (directed or undirected, with or without loops). + * + * + * 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, 2 * 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, 2 * 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))); + 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))); + 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 i->j edge is + * present if and only if j<i. + * 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; +} diff --git a/src/rigraph/core/constructors/kautz.c b/src/rigraph/core/constructors/kautz.c new file mode 100644 index 0000000..017d05c --- /dev/null +++ b/src/rigraph/core/constructors/kautz.c @@ -0,0 +1,186 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2005-2021 The igraph development 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., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_constructors.h" + +#include "igraph_interface.h" + +/** + * \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. For string length 1 the new letter + * cannot equal the old letter, so there are no loops. + * + * + * Kautz graphs have some interesting properties, see e.g. Wikipedia + * for details. + * + * + * 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; +} diff --git a/src/rigraph/core/constructors/lcf.c b/src/rigraph/core/constructors/lcf.c new file mode 100644 index 0000000..585c673 --- /dev/null +++ b/src/rigraph/core/constructors/lcf.c @@ -0,0 +1,141 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2005-2021 The igraph development 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., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_constructors.h" + +#include "igraph_operators.h" + +/** + * \function igraph_lcf_vector + * \brief Creates 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 Creates a graph from LCF notation. + * + * + * 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; +} diff --git a/src/rigraph/core/constructors/linegraph.c b/src/rigraph/core/constructors/linegraph.c new file mode 100644 index 0000000..13f69e0 --- /dev/null +++ b/src/rigraph/core/constructors/linegraph.c @@ -0,0 +1,164 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2005-2021 The igraph development 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., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_constructors.h" + +#include "igraph_interface.h" + +#include "core/interruption.h" + +/* 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. */ +static 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; +} + +static 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 different vertices in L(G) + * are connected by an edge if their corresponding edges share an end point. + * In a multigraph, if two end points are shared, two edges are created. + * The vertex of a loop is counted as two end points. + * + * + * 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. + * + * + * Edge \em i in the original graph will correspond to vertex \em i + * in the line graph. + * + * + * 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); + } +} diff --git a/src/rigraph/core/constructors/prufer.c b/src/rigraph/core/constructors/prufer.c new file mode 100644 index 0000000..40fa3ea --- /dev/null +++ b/src/rigraph/core/constructors/prufer.c @@ -0,0 +1,119 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2005-2021 The igraph development 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., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_constructors.h" + +#include "igraph_interface.h" + +/** + * \ingroup generators + * \function igraph_from_prufer + * \brief Generates a tree from a Prüfer sequence. + * + * A Prü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č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üfer sequence + * \return Error code: + * \clist + * \cli IGRAPH_ENOMEM + * there is not enough memory to perform the operation. + * \cli IGRAPH_EINVAL + * invalid Prüfer sequence given + * \endclist + * + * \sa \ref igraph_to_prufer(), \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(°ree, 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(°ree); + IGRAPH_FINALLY_CLEAN(2); + + return IGRAPH_SUCCESS; +} diff --git a/src/rigraph/core/constructors/regular.c b/src/rigraph/core/constructors/regular.c new file mode 100644 index 0000000..ec87710 --- /dev/null +++ b/src/rigraph/core/constructors/regular.c @@ -0,0 +1,511 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2005-2021 The igraph development 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., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_constructors.h" + +#include "igraph_interface.h" +#include "igraph_memory.h" +#include "igraph_operators.h" + +#include "core/interruption.h" + +/** + * \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 Arbitrary dimensional square lattices. + * + * Creates d-dimensional square lattices of the given size. Optionally, + * the lattice can be made periodic, and the neighbors within a given + * graph distance can be connected. + * + * + * In the zero-dimensional case, the singleton graph is returned. + * + * + * The vertices of the resulting graph are ordered such that the + * index of the vertex at position (i_0, i_1, i_2, ..., i_d) + * in a lattice of size (n_0, n_1, ..., n_d) will be + * i_0 + n_0 * i_1 + n_0 * n_1 * i_2 + .... + * + * \param graph An uninitialized graph object. + * \param dimvector Vector giving the sizes of the lattice in each of + * its dimensions. 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. + * If the \c mutual and \c circular arguments are not set to true, + * edges will be directed from lower-index vertices towards + * higher-index ones. + * \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^k+|E|), d + * is the average degree of the graph, k 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 creation failed", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, coords); + weights = IGRAPH_CALLOC(dims, long int); + if (weights == 0) { + IGRAPH_ERROR("Lattice creation failed", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_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= 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 cycle graph or a \em path graph. + * + * A circular ring on \c n vertices is commonly known in graph + * theory as the cycle graph, and often denoted by C_n. + * Removing a single edge from the cycle graph C_n results + * in the path graph P_n. This function can generate both. + * + * + * This function is a convenience wrapper for the one-dimensional case of + * \ref igraph_lattice(). + * + * \param graph Pointer to an uninitialized graph object. + * \param n The number of vertices in the graph. + * \param directed Logical, whether to create a directed graph. + * All edges will be oriented in the same direction along + * the cycle or path. + * \param mutual Logical, whether to create mutual edges in directed + * graphs. It is ignored for undirected graphs. + * \param circular Logical, whether to create a closed ring (a cycle) + * or an open path. + * \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_ERRORF("The number of vertices must be non-negative, got %" IGRAPH_PRId ".", IGRAPH_EINVAL, n); + } + + 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 IGRAPH_SUCCESS; +} + +/** + * \ingroup generators + * \function igraph_tree + * \brief Creates a tree in which almost all vertices have the same number of children. + * + * To obtain a completely symmetric tree with \c l layers, where each + * vertex has precisely \p children descendants, use + * n = (children^(l+1) - 1) / (children - 1). + * Such trees are often called k-ary trees, where \c k refers + * to the number of children. + * + * + * Note that for n=0, the null graph is returned, + * which is not considered to be a tree by \ref igraph_is_tree(). + * + * \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) { + IGRAPH_ERROR("Number of vertices cannot be negative.", IGRAPH_EINVAL); + } + if (children <= 0) { + IGRAPH_ERROR("Number of children must be positive.", IGRAPH_EINVAL); + } + if (type != IGRAPH_TREE_OUT && type != IGRAPH_TREE_IN && + type != IGRAPH_TREE_UNDIRECTED) { + IGRAPH_ERROR("Invalid tree orientation type.", IGRAPH_EINVMODE); + } + + IGRAPH_VECTOR_INIT_FINALLY(&edges, n > 0 ? 2 * (n - 1) : 0); + + 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; +} + +/** + * \function igraph_extended_chordal_ring + * \brief 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 + * L[(i mod p)] 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 + * (i + L[(i mod p)]) mod nodes. If multiple edges are + * defined in this way, this will output a non-simple graph. The result + * can be simplified using \ref igraph_simplify(). + * + * + * See also Kotsis, G: Interconnection Topologies for Parallel Processing + * Systems, PARS Mitteilungen 11, 1-6, 1993. The igraph extended chordal + * rings are not identical to the ones in the paper. In igraph + * the matrix specifies which edges to add. In the paper, a condition is + * specified which should simultaneously hold between two endpoints and + * the reverse endpoints. + * + * \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. The elements + * are allowed to be negative. + * \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; +} diff --git a/src/rigraph/core/core/array.c b/src/rigraph/core/core/array.c new file mode 100644 index 0000000..4d9904a --- /dev/null +++ b/src/rigraph/core/core/array.c @@ -0,0 +1,50 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2006-2012 Gabor Csardi + 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/src/rigraph/core/core/array.pmt b/src/rigraph/core/core/array.pmt new file mode 100644 index 0000000..6d5c735 --- /dev/null +++ b/src/rigraph/core/core/array.pmt @@ -0,0 +1,90 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2006-2012 Gabor Csardi + 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/src/rigraph/core/core/buckets.c b/src/rigraph/core/core/buckets.c new file mode 100644 index 0000000..67be507 --- /dev/null +++ b/src/rigraph/core/core/buckets.c @@ -0,0 +1,196 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2006-2012 Gabor Csardi + 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 "core/buckets.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/src/rigraph/core/core/buckets.h b/src/rigraph/core/core/buckets.h new file mode 100644 index 0000000..31635f1 --- /dev/null +++ b/src/rigraph/core/core/buckets.h @@ -0,0 +1,81 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2009-2020 The igraph development 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., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#ifndef IGRAPH_CORE_BUCKETS_H +#define IGRAPH_CORE_BUCKETS_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" + +__BEGIN_DECLS + +/* 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); + +__END_DECLS + +#endif diff --git a/src/rigraph/core/core/cutheap.c b/src/rigraph/core/core/cutheap.c new file mode 100644 index 0000000..1ab0713 --- /dev/null +++ b/src/rigraph/core/core/cutheap.c @@ -0,0 +1,170 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2003-2020 The igraph development 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., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_types.h" + +#include "core/cutheap.h" + +#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 + +static 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; + } +} + +static 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)); + } + } +} + +static 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; +} diff --git a/src/rigraph/core/core/cutheap.h b/src/rigraph/core/core/cutheap.h new file mode 100644 index 0000000..d7cb4c7 --- /dev/null +++ b/src/rigraph/core/core/cutheap.h @@ -0,0 +1,63 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2009-2020 The igraph development 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., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#ifndef IGRAPH_CORE_CUTHEAP_H +#define IGRAPH_CORE_CUTHEAP_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" + +__BEGIN_DECLS + +/* 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; + +IGRAPH_PRIVATE_EXPORT int igraph_i_cutheap_init(igraph_i_cutheap_t *ch, igraph_integer_t nodes); +IGRAPH_PRIVATE_EXPORT void igraph_i_cutheap_destroy(igraph_i_cutheap_t *ch); +IGRAPH_PRIVATE_EXPORT igraph_bool_t igraph_i_cutheap_empty(igraph_i_cutheap_t *ch); +IGRAPH_PRIVATE_EXPORT igraph_integer_t igraph_i_cutheap_active_size(igraph_i_cutheap_t *ch); +IGRAPH_PRIVATE_EXPORT igraph_integer_t igraph_i_cutheap_size(igraph_i_cutheap_t *ch); +IGRAPH_PRIVATE_EXPORT igraph_real_t igraph_i_cutheap_maxvalue(igraph_i_cutheap_t *ch); +IGRAPH_PRIVATE_EXPORT igraph_integer_t igraph_i_cutheap_popmax(igraph_i_cutheap_t *ch); +IGRAPH_PRIVATE_EXPORT int igraph_i_cutheap_update(igraph_i_cutheap_t *ch, igraph_integer_t index, + igraph_real_t add); +IGRAPH_PRIVATE_EXPORT int igraph_i_cutheap_reset_undefine(igraph_i_cutheap_t *ch, long int vertex); + +__END_DECLS + +#endif diff --git a/src/rigraph/core/core/dqueue.c b/src/rigraph/core/core/dqueue.c new file mode 100644 index 0000000..212b242 --- /dev/null +++ b/src/rigraph/core/core/dqueue.c @@ -0,0 +1,55 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2007-2012 Gabor Csardi + 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/src/rigraph/core/core/dqueue.pmt b/src/rigraph/core/core/dqueue.pmt new file mode 100644 index 0000000..6ef7b67 --- /dev/null +++ b/src/rigraph/core/core/dqueue.pmt @@ -0,0 +1,383 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2003-2012 Gabor Csardi + 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 /* memcpy & co. */ +#include + +/** + * \section igraph_dqueue + * + * 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. + * + * + * + * \example examples/simple/dqueue.c + * + */ + +/** + * \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) { + IGRAPH_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) { + IGRAPH_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) { + IGRAPH_ASSERT(q != 0); + IGRAPH_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) { + IGRAPH_ASSERT(q != 0); + IGRAPH_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) { + IGRAPH_ASSERT(q != 0); + IGRAPH_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) { + IGRAPH_ASSERT(q != 0); + IGRAPH_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) { + IGRAPH_ASSERT(q != 0); + IGRAPH_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) { + IGRAPH_ASSERT(q != 0); + IGRAPH_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); + IGRAPH_ASSERT(q != 0); + IGRAPH_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; + IGRAPH_ASSERT(q != 0); + IGRAPH_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) { + IGRAPH_ASSERT(q != 0); + IGRAPH_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/src/rigraph/core/core/error.c b/src/rigraph/core/core/error.c new file mode 100644 index 0000000..ff3047a --- /dev/null +++ b/src/rigraph/core/core/error.c @@ -0,0 +1,385 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2005-2012 Gabor Csardi + 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 +#include +#include + +/* Detecting ASan with GCC: + * https://gcc.gnu.org/onlinedocs/cpp/Common-Predefined-Macros.html + * Detecting ASan with Clang: + * https://clang.llvm.org/docs/AddressSanitizer.html#conditional-compilation-with-has-feature-address-sanitizer + */ +#if defined(__SANITIZE_ADDRESS__) +# define IGRAPH_SANITIZER_AVAILABLE 1 +#elif defined(__has_feature) +# if __has_feature(address_sanitizer) +# define IGRAPH_SANITIZER_AVAILABLE 1 +# endif +#endif + +#ifdef IGRAPH_SANITIZER_AVAILABLE +#include +#endif + +#ifdef USING_R +#include +#endif + +/***** Helper functions *****/ + +/* All calls to abort() in this compilation unit must go through igraph_abort(), + * in order to make it easy for igraph's R interface to not have any reference to abort(), + * which is disallowed by CRAN. + * + * Since the R interface sets its own error / fatal error handlers, this function + * is never actually called by it. + * + * Note that some of the other #ifndef USING_R's in this file are still needed + * to avoid references to fprintf and stderr. + */ +static IGRAPH_NORETURN void igraph_abort() { +#ifndef USING_R +#ifdef IGRAPH_SANITIZER_AVAILABLE + fprintf(stderr, "\nStack trace:\n"); + __sanitizer_print_stack_trace(); +#endif + abort(); +#else + /* R's error() function is declared 'noreturn'. We use it here to satisfy the compiler that igraph_abort() does indeed not return. */ + error("igraph_abort() was called. This should never happen. Please report this as an igraph bug, along with steps to reproduce it."); +#endif +} + + +/***** Handling errors *****/ + +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]; +static IGRAPH_THREAD_LOCAL char igraph_i_fatalmsg_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 in 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 || + ((unsigned long)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)); + igraph_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) { + fprintf(stderr, "Error at %s:%i : %s - %s.\n", + file, line, reason, igraph_strerror(igraph_errno)); + IGRAPH_FINALLY_FREE(); +} +#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; +} + + +/***** "Finally" stack *****/ + +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; + IGRAPH_ASSERT(no < 100); + IGRAPH_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) { + int left = igraph_i_finally_stack[0].all + minus; + /* Set to zero in case fatal error handler does a longjmp instead of terminating the process: */ + igraph_i_finally_stack[0].all = 0; + IGRAPH_FATALF("Corrupt finally stack: trying to pop %d element(s) when only %d left.", minus, left); + } + /* 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; +} + + +/***** Handling warnings *****/ + +static IGRAPH_THREAD_LOCAL igraph_warning_handler_t *igraph_i_warning_handler = 0; + +/** + * \function igraph_warning_handler_ignore + * \brief Ignores 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 + * \brief Prints all warnings 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 at %s:%i : %s\n", file, line, reason); +} +#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; +} + + +/***** Handling fatal errors *****/ + +static IGRAPH_THREAD_LOCAL igraph_fatal_handler_t *igraph_i_fatal_handler = NULL; + +igraph_fatal_handler_t *igraph_set_fatal_handler(igraph_fatal_handler_t *new_handler) { + igraph_fatal_handler_t *previous_handler = igraph_i_fatal_handler; + igraph_i_fatal_handler = new_handler; + return previous_handler; +} + +#ifndef USING_R +void igraph_fatal_handler_abort(const char *reason, const char *file, int line) { + fprintf(stderr, "Fatal error at %s:%i : %s\n", file, line, reason); + igraph_abort(); +} +#endif + +void igraph_fatal(const char *reason, const char *file, int line) { + if (igraph_i_fatal_handler) { + igraph_i_fatal_handler(reason, file, line); +#ifndef USING_R + } else { + igraph_fatal_handler_abort(reason, file, line); +#endif + } + /* The following line should never be reached, as fatal error handlers are not supposed to return. + It is here to satisfy the compiler that this function indeed does not return. */ + igraph_abort(); +} + +void igraph_fatalf(const char *reason, const char *file, int line, ...) { + va_list ap; + va_start(ap, line); + vsnprintf(igraph_i_fatalmsg_buffer, sizeof(igraph_i_fatalmsg_buffer) / sizeof(char), reason, ap); + va_end(ap); + igraph_fatal(igraph_i_fatalmsg_buffer, file, line); +} diff --git a/src/rigraph/core/core/estack.c b/src/rigraph/core/core/estack.c new file mode 100644 index 0000000..41a62ed --- /dev/null +++ b/src/rigraph/core/core/estack.c @@ -0,0 +1,67 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2006-2012 Gabor Csardi + 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 "core/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/src/rigraph/core/core/estack.h b/src/rigraph/core/core/estack.h new file mode 100644 index 0000000..65178ba --- /dev/null +++ b/src/rigraph/core/core/estack.h @@ -0,0 +1,47 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2010-2012 Gabor Csardi + 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; + +IGRAPH_PRIVATE_EXPORT int igraph_estack_init(igraph_estack_t *s, long int setsize, + long int stacksize); +IGRAPH_PRIVATE_EXPORT void igraph_estack_destroy(igraph_estack_t *s); + +IGRAPH_PRIVATE_EXPORT int igraph_estack_push(igraph_estack_t *s, long int elem); +IGRAPH_PRIVATE_EXPORT long int igraph_estack_pop(igraph_estack_t *s); +IGRAPH_PRIVATE_EXPORT igraph_bool_t igraph_estack_iselement(const igraph_estack_t *s, + long int elem); +IGRAPH_PRIVATE_EXPORT long int igraph_estack_size(const igraph_estack_t *s); + +IGRAPH_PRIVATE_EXPORT int igraph_estack_print(const igraph_estack_t *s); + +#endif diff --git a/src/rigraph/core/core/exceptions.h b/src/rigraph/core/core/exceptions.h new file mode 100644 index 0000000..f1cb6ec --- /dev/null +++ b/src/rigraph/core/core/exceptions.h @@ -0,0 +1,19 @@ +#ifndef IGRAPH_HANDLE_EXCEPTIONS_H +#define IGRAPH_HANDLE_EXCEPTIONS_H + +#include +#include + +/* igraph functions which may be called from C code must not throw C++ exceptions. + * This includes all public functions. This macro is meant to handle exceptions thrown + * by C++ libraries used by igraph (such as bliss). Wrap the entire body + * of public functions implemented in C++ in IGRAPH_HANDLE_EXCEPTIONS(). + */ +#define IGRAPH_HANDLE_EXCEPTIONS(code) \ + try { code; } \ + catch (const std::bad_alloc &e) { IGRAPH_ERROR(e.what(), IGRAPH_ENOMEM); } \ + catch (const std::exception &e) { IGRAPH_ERROR(e.what(), IGRAPH_FAILURE); } \ + catch (...) { IGRAPH_ERROR("Unknown exception caught.", IGRAPH_FAILURE); } + + +#endif // IGRAPH_HANDLE_EXCEPTIONS_H diff --git a/src/rigraph/core/core/fixed_vectorlist.c b/src/rigraph/core/core/fixed_vectorlist.c new file mode 100644 index 0000000..adc482e --- /dev/null +++ b/src/rigraph/core/core/fixed_vectorlist.c @@ -0,0 +1,81 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2010-2012 Gabor Csardi + 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 "core/fixed_vectorlist.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_vector_ptr_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/src/leidenalg/igraph-R/igraph_mixing.h b/src/rigraph/core/core/fixed_vectorlist.h similarity index 57% rename from src/leidenalg/igraph-R/igraph_mixing.h rename to src/rigraph/core/core/fixed_vectorlist.h index c9d2dc6..4a0907f 100644 --- a/src/leidenalg/igraph-R/igraph_mixing.h +++ b/src/rigraph/core/core/fixed_vectorlist.h @@ -1,59 +1,50 @@ /* -*- mode: C -*- */ -/* +/* IGraph library. Copyright (C) 2009-2012 Gabor Csardi 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 + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#ifndef IGRAPH_MIXING_H -#define IGRAPH_MIXING_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 +#ifndef IGRAPH_TYPES_INTERNAL_H +#define IGRAPH_TYPES_INTERNAL_H +#include "igraph_decls.h" #include "igraph_types.h" -#include "igraph_datatype.h" #include "igraph_vector.h" +#include "igraph_vector_ptr.h" __BEGIN_DECLS -int igraph_assortativity_nominal(const igraph_t *graph, - const igraph_vector_t *types, - igraph_real_t *res, - igraph_bool_t directed); +/* -------------------------------------------------- */ +/* Vectorlist, fixed length */ +/* -------------------------------------------------- */ -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); +typedef struct igraph_fixed_vectorlist_t { + igraph_vector_t *vecs; + igraph_vector_ptr_t v; + long int length; +} igraph_fixed_vectorlist_t; -int igraph_assortativity_degree(const igraph_t *graph, - igraph_real_t *res, - igraph_bool_t directed); +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 diff --git a/src/rigraph/core/core/grid.c b/src/rigraph/core/core/grid.c new file mode 100644 index 0000000..3426556 --- /dev/null +++ b/src/rigraph/core/core/grid.c @@ -0,0 +1,348 @@ +/* -*- mode: C -*- */ +/* + IGraph R package. + Copyright (C) 2006-2012 Gabor Csardi + 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 "core/grid.h" + +#include + +/* 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; +} + +static 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; +} diff --git a/src/rigraph/core/core/grid.h b/src/rigraph/core/core/grid.h new file mode 100644 index 0000000..d362ab7 --- /dev/null +++ b/src/rigraph/core/core/grid.h @@ -0,0 +1,80 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2009-2020 The igraph development 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., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#ifndef IGRAPH_CORE_GRID_H +#define IGRAPH_CORE_GRID_H + +#include "igraph_decls.h" +#include "igraph_types.h" +#include "igraph_matrix.h" +#include "igraph_vector.h" + +__BEGIN_DECLS + +/** + * 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); + +__END_DECLS + +#endif diff --git a/src/rigraph/core/core/heap.c b/src/rigraph/core/core/heap.c new file mode 100644 index 0000000..b94a8d1 --- /dev/null +++ b/src/rigraph/core/core/heap.c @@ -0,0 +1,64 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2007-2012 Gabor Csardi + 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/src/rigraph/core/core/heap.pmt b/src/rigraph/core/core/heap.pmt new file mode 100644 index 0000000..2501c8b --- /dev/null +++ b/src/rigraph/core/core/heap.pmt @@ -0,0 +1,355 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2007-2012 Gabor Csardi + 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 /* memcpy & co. */ +#include + +#define PARENT(x) (((x)+1)/2-1) +#define LEFTCHILD(x) (((x)+1)*2-1) +#define RIGHTCHILD(x) (((x)+1)*2) + +/* Declare internal functions */ +static void FUNCTION(igraph_heap, i_build)(BASE* arr, long int size, long int head); +static void FUNCTION(igraph_heap, i_shift_up)(BASE* arr, long int size, long int elem); +static void FUNCTION(igraph_heap, i_sink)(BASE* arr, long int size, long int head); +static void FUNCTION(igraph_heap, i_switch)(BASE* arr, long int e1, long int e2); + +/** + * \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) { + IGRAPH_ASSERT(h != NULL); + IGRAPH_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) { + IGRAPH_ASSERT(h != NULL); + IGRAPH_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) { + IGRAPH_ASSERT(h != NULL); + IGRAPH_ASSERT(h->stor_begin != NULL); + IGRAPH_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; + + IGRAPH_ASSERT(h != NULL); + IGRAPH_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) { + IGRAPH_ASSERT(h != NULL); + IGRAPH_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; + IGRAPH_ASSERT(h != NULL); + IGRAPH_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/src/rigraph/core/core/indheap.c b/src/rigraph/core/core/indheap.c new file mode 100644 index 0000000..67592b0 --- /dev/null +++ b/src/rigraph/core/core/indheap.c @@ -0,0 +1,953 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2003-2020 The igraph development 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., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_types.h" +#include "igraph_memory.h" +#include "igraph_error.h" + +#include "core/indheap.h" + +#include /* memcpy & co. */ +#include + +/* -------------------------------------------------- */ +/* Indexed heap */ +/* -------------------------------------------------- */ + +#define PARENT(x) (((x)+1)/2-1) +#define LEFTCHILD(x) (((x)+1)*2-1) +#define RIGHTCHILD(x) (((x)+1)*2) + +static void igraph_indheap_i_build(igraph_indheap_t* h, long int head); +static void igraph_indheap_i_shift_up(igraph_indheap_t* h, long int elem); +static void igraph_indheap_i_sink(igraph_indheap_t* h, long int head); +static void igraph_indheap_i_switch(igraph_indheap_t* h, long int e1, long int e2); + +/** + * \ingroup indheap + * \brief Initializes an indexed heap (constructor). + * + * @return Error code: + * - IGRAPH_ENOMEM: 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: + * - IGRAPH_ENOMEM: 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) { + IGRAPH_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) { + IGRAPH_ASSERT(h != 0); + IGRAPH_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) { + IGRAPH_ASSERT(h != 0); + IGRAPH_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) { + IGRAPH_ASSERT(h != 0); + IGRAPH_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; + + IGRAPH_ASSERT(h != 0); + IGRAPH_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) { + IGRAPH_ASSERT(h != NULL); + IGRAPH_ASSERT(h->stor_begin != NULL); + IGRAPH_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; + + IGRAPH_ASSERT(h != NULL); + IGRAPH_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) { + IGRAPH_ASSERT(h != 0); + IGRAPH_ASSERT(h->stor_begin != 0); + return h->end - h->stor_begin; +} + +/** + * \ingroup indheap + * \brief Reserves more memory for an indexed heap. + * + * @return Error code: + * - IGRAPH_ENOMEM: 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; + IGRAPH_ASSERT(h != 0); + IGRAPH_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(igraph_free, tmp1); + tmp2 = IGRAPH_CALLOC(size, long int); + if (tmp2 == 0) { + IGRAPH_ERROR("indheap reserve failed", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_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) { + IGRAPH_ASSERT(h != 0); + IGRAPH_ASSERT(h->stor_begin != 0); + return h->index_begin[0]; +} + +/** + * \ingroup indheap + * \brief Builds an indexed heap, this function should not be called + * directly. + */ + +static 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. + */ + +static 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. + */ + +static 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. + */ + +static 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; + } +} + +/*************************************************/ + +/* -------------------------------------------------- */ +/* Doubly indexed heap */ +/* -------------------------------------------------- */ + +/* static void igraph_d_indheap_i_build(igraph_d_indheap_t* h, long int head); */ /* Unused function */ +static void igraph_d_indheap_i_shift_up(igraph_d_indheap_t* h, long int elem); +static void igraph_d_indheap_i_sink(igraph_d_indheap_t* h, long int head); +static void igraph_d_indheap_i_switch(igraph_d_indheap_t* h, long int e1, long int e2); + +/** + * \ingroup doubleindheap + * \brief Initializes an empty doubly indexed heap object (constructor). + * + * @return Error code: + * - IGRAPH_ENOMEM: 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) { + IGRAPH_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) { + IGRAPH_ASSERT(h != 0); + IGRAPH_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) { + IGRAPH_ASSERT(h != 0); + IGRAPH_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) { + IGRAPH_ASSERT(h != NULL); + IGRAPH_ASSERT(h->stor_begin != NULL); + IGRAPH_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; + + IGRAPH_ASSERT(h != NULL); + IGRAPH_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) { + IGRAPH_ASSERT(h != 0); + IGRAPH_ASSERT(h->stor_begin != 0); + return h->end - h->stor_begin; +} + +/** + * \ingroup doubleindheap + * \brief Allocates memory for a heap. + * + * @return Error code: + * - IGRAPH_ENOMEM: 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; + IGRAPH_ASSERT(h != 0); + IGRAPH_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(igraph_free, tmp1); + tmp2 = IGRAPH_CALLOC(size, long int); + if (tmp2 == 0) { + IGRAPH_ERROR("d_indheap reserve failed", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, tmp2); + tmp3 = IGRAPH_CALLOC(size, long int); + if (tmp3 == 0) { + IGRAPH_ERROR("d_indheap reserve failed", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, tmp3); + + 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) { + IGRAPH_ASSERT(h != 0); + IGRAPH_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. + */ + +/* Unused function, temporarily disabled */ +#if 0 +static 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 */ + } +} +#endif + +/** + * \ingroup doubleindheap + * \brief Moves an element up in the heap, don't call it directly. + */ + +static 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. + */ + +static 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. + */ + +static 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; + } +} + +/*************************************************/ + +/* -------------------------------------------------- */ +/* 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.... */ + +static 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; + } +} + +static 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)); + } +} + +static 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/src/rigraph/core/core/indheap.h b/src/rigraph/core/core/indheap.h new file mode 100644 index 0000000..16977f9 --- /dev/null +++ b/src/rigraph/core/core/indheap.h @@ -0,0 +1,140 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2009-2020 The igraph development 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., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#ifndef IGRAPH_CORE_INDHEAP_H +#define IGRAPH_CORE_INDHEAP_H + +#include "igraph_decls.h" +#include "igraph_types.h" +#include "igraph_vector.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); + + +/* -------------------------------------------------- */ +/* 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 } + +IGRAPH_PRIVATE_EXPORT int igraph_d_indheap_init(igraph_d_indheap_t *h, long int size); +IGRAPH_PRIVATE_EXPORT void igraph_d_indheap_destroy(igraph_d_indheap_t *h); +IGRAPH_PRIVATE_EXPORT igraph_bool_t igraph_d_indheap_empty(igraph_d_indheap_t *h); +IGRAPH_PRIVATE_EXPORT int igraph_d_indheap_push(igraph_d_indheap_t *h, igraph_real_t elem, + long int idx, long int idx2); +IGRAPH_PRIVATE_EXPORT igraph_real_t igraph_d_indheap_max(igraph_d_indheap_t *h); +IGRAPH_PRIVATE_EXPORT igraph_real_t igraph_d_indheap_delete_max(igraph_d_indheap_t *h); +IGRAPH_PRIVATE_EXPORT long int igraph_d_indheap_size(igraph_d_indheap_t *h); +IGRAPH_PRIVATE_EXPORT int igraph_d_indheap_reserve(igraph_d_indheap_t *h, long int size); +IGRAPH_PRIVATE_EXPORT void igraph_d_indheap_max_index(igraph_d_indheap_t *h, long int *idx, long int *idx2); + +/* -------------------------------------------------- */ +/* 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; + +IGRAPH_PRIVATE_EXPORT int igraph_2wheap_init(igraph_2wheap_t *h, long int size); +IGRAPH_PRIVATE_EXPORT void igraph_2wheap_destroy(igraph_2wheap_t *h); +IGRAPH_PRIVATE_EXPORT int igraph_2wheap_clear(igraph_2wheap_t *h); +IGRAPH_PRIVATE_EXPORT int igraph_2wheap_push_with_index(igraph_2wheap_t *h, + long int idx, igraph_real_t elem); +IGRAPH_PRIVATE_EXPORT igraph_bool_t igraph_2wheap_empty(const igraph_2wheap_t *h); +IGRAPH_PRIVATE_EXPORT long int igraph_2wheap_size(const igraph_2wheap_t *h); +IGRAPH_PRIVATE_EXPORT long int igraph_2wheap_max_size(const igraph_2wheap_t *h); +IGRAPH_PRIVATE_EXPORT igraph_real_t igraph_2wheap_max(const igraph_2wheap_t *h); +IGRAPH_PRIVATE_EXPORT long int igraph_2wheap_max_index(const igraph_2wheap_t *h); +IGRAPH_PRIVATE_EXPORT igraph_real_t igraph_2wheap_deactivate_max(igraph_2wheap_t *h); +IGRAPH_PRIVATE_EXPORT igraph_bool_t igraph_2wheap_has_elem(const igraph_2wheap_t *h, long int idx); +IGRAPH_PRIVATE_EXPORT igraph_bool_t igraph_2wheap_has_active(const igraph_2wheap_t *h, long int idx); +IGRAPH_PRIVATE_EXPORT igraph_real_t igraph_2wheap_get(const igraph_2wheap_t *h, long int idx); +IGRAPH_PRIVATE_EXPORT igraph_real_t igraph_2wheap_delete_max(igraph_2wheap_t *h); +IGRAPH_PRIVATE_EXPORT igraph_real_t igraph_2wheap_delete_max_index(igraph_2wheap_t *h, long int *idx); +IGRAPH_PRIVATE_EXPORT int igraph_2wheap_modify(igraph_2wheap_t *h, long int idx, igraph_real_t elem); +IGRAPH_PRIVATE_EXPORT int igraph_2wheap_check(igraph_2wheap_t *h); + +__END_DECLS + +#endif diff --git a/src/rigraph/core/core/interruption.c b/src/rigraph/core/core/interruption.c new file mode 100644 index 0000000..d4bf3c9 --- /dev/null +++ b/src/rigraph/core/core/interruption.c @@ -0,0 +1,42 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2005-2012 Gabor Csardi + 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" + +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/src/rigraph/core/core/interruption.h b/src/rigraph/core/core/interruption.h new file mode 100644 index 0000000..6afda32 --- /dev/null +++ b/src/rigraph/core/core/interruption.h @@ -0,0 +1,59 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2003-2012 Gabor Csardi + 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 "igraph_decls.h" +#include "igraph_interrupt.h" +#include "config.h" + +__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/src/rigraph/core/core/marked_queue.c b/src/rigraph/core/core/marked_queue.c new file mode 100644 index 0000000..8166793 --- /dev/null +++ b/src/rigraph/core/core/marked_queue.c @@ -0,0 +1,115 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2010-2012 Gabor Csardi + 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 "core/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/src/rigraph/core/core/marked_queue.h b/src/rigraph/core/core/marked_queue.h new file mode 100644 index 0000000..be36e28 --- /dev/null +++ b/src/rigraph/core/core/marked_queue.h @@ -0,0 +1,70 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2010-2012 Gabor Csardi + 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 + +/* 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; + +IGRAPH_PRIVATE_EXPORT int igraph_marked_queue_init(igraph_marked_queue_t *q, + long int size); +IGRAPH_PRIVATE_EXPORT void igraph_marked_queue_destroy(igraph_marked_queue_t *q); +IGRAPH_PRIVATE_EXPORT void igraph_marked_queue_reset(igraph_marked_queue_t *q); + +IGRAPH_PRIVATE_EXPORT igraph_bool_t igraph_marked_queue_empty(const igraph_marked_queue_t *q); +IGRAPH_PRIVATE_EXPORT long int igraph_marked_queue_size(const igraph_marked_queue_t *q); + +IGRAPH_PRIVATE_EXPORT int igraph_marked_queue_print(const igraph_marked_queue_t *q); +IGRAPH_PRIVATE_EXPORT int igraph_marked_queue_fprint(const igraph_marked_queue_t *q, FILE *file); + +IGRAPH_PRIVATE_EXPORT igraph_bool_t igraph_marked_queue_iselement(const igraph_marked_queue_t *q, + long int elem); +IGRAPH_PRIVATE_EXPORT int igraph_marked_queue_push(igraph_marked_queue_t *q, long int elem); + +IGRAPH_PRIVATE_EXPORT int igraph_marked_queue_start_batch(igraph_marked_queue_t *q); +IGRAPH_PRIVATE_EXPORT void igraph_marked_queue_pop_back_batch(igraph_marked_queue_t *q); + +IGRAPH_PRIVATE_EXPORT int igraph_marked_queue_as_vector(const igraph_marked_queue_t *q, + igraph_vector_t *vec); + +#endif diff --git a/src/rigraph/core/core/math.h b/src/rigraph/core/core/math.h new file mode 100644 index 0000000..c50bd13 --- /dev/null +++ b/src/rigraph/core/core/math.h @@ -0,0 +1,88 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2008-2012 Gabor Csardi + 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 "igraph_decls.h" + +#include "config.h" + +#include +#include + +__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); +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_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 + +IGRAPH_PRIVATE_EXPORT int igraph_almost_equals(double a, double b, double eps); +IGRAPH_PRIVATE_EXPORT int igraph_cmp_epsilon(double a, double b, double eps); + +__END_DECLS + +#endif diff --git a/src/rigraph/core/core/matrix.c b/src/rigraph/core/core/matrix.c new file mode 100644 index 0000000..80bd31e --- /dev/null +++ b/src/rigraph/core/core/matrix.c @@ -0,0 +1,158 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2007-2012 Gabor Csardi + 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/src/rigraph/core/core/matrix.pmt b/src/rigraph/core/core/matrix.pmt new file mode 100644 index 0000000..27071ac --- /dev/null +++ b/src/rigraph/core/core/matrix.pmt @@ -0,0 +1,1641 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2003-2012 Gabor Csardi + 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 /* memcpy & co. */ +#include + +/** + * \section about_igraph_matrix_t_objects About \type igraph_matrix_t objects + * + * This type is just an interface to \type igraph_vector_t. + * + * The \type igraph_matrix_t type usually stores n + * elements in O(n) space, but not always. See the documentation of + * the vector type. + */ + +/** + * \section igraph_matrix_constructor_and_destructor Matrix constructors and + * destructors + */ + +/** + * \ingroup matrix + * \function igraph_matrix_init + * \brief Initializes a matrix. + * + * + * 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) { + IGRAPH_CHECK(FUNCTION(igraph_vector, init)(&m->data, nrow * ncol)); + m->nrow = nrow; + m->ncol = ncol; + return IGRAPH_SUCCESS; +} + +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. + * + * + * 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. + * + * + * 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) { + IGRAPH_CHECK(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. + * + * + * Note that this function might fail if there is not enough memory + * available. + * + * + * 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. + * + * + * 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) { + IGRAPH_CHECK(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; + IGRAPH_CHECK(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); + IGRAPH_CHECK(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; + } + IGRAPH_CHECK(FUNCTION(igraph_matrix, resize)(m, m->nrow - nremove, m->ncol)); + + return 0; +} + +/** + * \ingroup matrix + * \function igraph_matrix_copy + * \brief Copies a matrix. + * + * + * 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) { + IGRAPH_CHECK(FUNCTION(igraph_vector, copy)(&to->data, &from->data)); + to->nrow = from->nrow; + to->ncol = from->ncol; + return IGRAPH_SUCCESS; +} + +#ifndef NOTORDERED + +/** + * \function igraph_matrix_max + * \brief Largest element of a matrix. + * + * + * If the matrix is empty, an arbitrary number is returned. + * \param m The matrix object. + * \return The maximum element of \p m, or NaN if any element is NaN. + * + * Added in version 0.2. + * + * Time complexity: O(mn), 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. + * + * 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; + IGRAPH_CHECK(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. + * \param m1 The first matrix. + * \param m2 The second matrix. + * \return Error code. + * + * Time complexity: O(1). + */ + +int FUNCTION(igraph_matrix, swap)(TYPE(igraph_matrix) *m1, TYPE(igraph_matrix) *m2) { + 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 + * \brief Smallest element of a matrix. + * + * The matrix must be non-empty. + * \param m The input matrix. + * \return The smallest element of \p m, or NaN if any element is NaN. + * + * Time complexity: O(mn), the number of elements in the matrix. + */ + +igraph_real_t FUNCTION(igraph_matrix, min)(const TYPE(igraph_matrix) *m) { + return FUNCTION(igraph_vector, min)(&m->data); +} + +/** + * \function igraph_matrix_which_min + * \brief Indices of the smallest element. + * + * + * The matrix must be non-empty. If the smallest element is not unique, + * then the indices of the first such element are returned. If the matrix contains + * NaN values, the indices of the first NaN value are returned. + * \param m The matrix. + * \param i Pointer to a long int. The row index of the + * minimum is stored here. + * \param j Pointer to a long int. 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 + * \brief Indices of the largest element. + * + * + * The matrix must be non-empty. If the largest element is not unique, + * then the indices of the first such element are returned. If the matrix contains + * NaN values, the indices of the first NaN value are returned. + * \param m The matrix. + * \param i Pointer to a long int. The row index of the + * maximum is stored here. + * \param j Pointer to a long int. 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 + * \brief Minimum and maximum elements of a matrix. + * + * Handy if you want to have both the smallest and largest element of + * a matrix. The matrix is only traversed once. The matrix must be non-empty. + * If a matrix contains at least one NaN, both \c min and \c max will be NaN. + * \param m The input matrix. 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(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 + * \brief Indices of the minimum and maximum elements + * + * + * Handy if you need the indices of the smallest and largest + * elements. The matrix is traversed only once. The matrix must be + * non-empty. If the minimum or maximum is not unique, the index + * of the first minimum or the first maximum is returned, respectively. + * If a matrix contains at least one NaN, both \c which_min and \c which_max + * will point to the first NaN value. + * \param m The input matrix. + * \param imin Pointer to a long int, the row index of + * the minimum is stored here. + * \param jmin Pointer to a long int, the column index of + * the minimum is stored here. + * \param imax Pointer to a long int, the row index of + * the maximum is stored here. + * \param jmax Pointer to a long int, 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 long int. If the element is + * found, then this is set to the position of its first appearance. + * \param row Pointer to a long int. If the element is + * found, then this is set to its row index. + * \param col Pointer to a long int. 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--; + IGRAPH_CHECK(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/src/rigraph/core/core/memory.c b/src/rigraph/core/core/memory.c new file mode 100644 index 0000000..a59afe7 --- /dev/null +++ b/src/rigraph/core/core/memory.c @@ -0,0 +1,73 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2003-2012 Gabor Csardi + 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" + +/** + * \function igraph_free + * \brief 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 when the user does not need them any more. This can be done + * by calling igraph_free on them. + * + * + * Here is a complete example on how to use \c igraph_free properly. + * + * \example examples/simple/igraph_free.c + * + * \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() + */ + +void igraph_free(void *p) { + IGRAPH_FREE(p); +} + + +/** + * \function igraph_malloc + * \brief 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/src/rigraph/core/core/printing.c b/src/rigraph/core/core/printing.c new file mode 100644 index 0000000..7399973 --- /dev/null +++ b/src/rigraph/core/core/printing.c @@ -0,0 +1,146 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2012 Gabor Csardi + 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 + +#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/src/rigraph/core/core/progress.c b/src/rigraph/core/core/progress.c new file mode 100644 index 0000000..436d403 --- /dev/null +++ b/src/rigraph/core/core/progress.c @@ -0,0 +1,154 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2006-2012 Gabor Csardi + 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. + * + * 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 + * \brief 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 same \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", percent); + return IGRAPH_SUCCESS; +} +#endif + +/** + * \function igraph_set_progress_handler + * \brief 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/src/rigraph/core/core/psumtree.c b/src/rigraph/core/core/psumtree.c new file mode 100644 index 0000000..b61f0d6 --- /dev/null +++ b/src/rigraph/core/core/psumtree.c @@ -0,0 +1,253 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2006-2012 Gabor Csardi + 334 Harvard street, Cambridge, MA 02139 USA + Copyright (C) 2006 Elliot Paquette + 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 + +static double igraph_i_log2(double f) { + return log(f) / log(2.0); +} + +/** + * \ingroup psumtree + * \section igraph_psumtree + * + * The \type igraph_psumtree_t data type represents a partial prefix sum + * tree. A partial prefix sum tree is a data structure that can be used to draw + * samples from a discrete probability distribution with dynamic probabilities + * that are updated frequently. This is achieved by creating a binary tree where + * the leaves are the items. Each leaf contains the probability corresponding to + * the items. Intermediate nodes of the tree always contain the sum of its two + * children. When the value of a leaf node is updated, the values of its + * ancestors are also updated accordingly. + * + * Samples can be drawn from the probability distribution represented by + * the tree by generating a uniform random number between 0 (inclusive) and the + * value of the root of the tree (exclusive), and then following the branches + * of the tree as follows. In each step, the value in the current node is + * compared with the generated number. If the value in the node is larger, + * the left branch of the tree is taken; otherwise the generated number is + * decreased by the value in the node and the right branch of the tree is + * taken, until a leaf node is reached. + * + * Note that the sampling process works only if all the values in the tree + * are non-negative. This is enforced by the object; in particular, trying to + * set a negative value for an item will produce an igraph error. + */ + +/* + * Internally, a partial prefix sum tree is stored in a contiguous chunk of + * memory which we treat as a vector v. 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. + * + * More precisely: the part between (offset, offset + size - 1) of vector v + * contains the values (not necessarily probabilities) corresponding to the + * individual items. For the part in front of it, it holds that the value at + * index i (zero-based) is the sum of values at index (2*i + 1) and index + * (2*i + 2). The item at index zero contains the sum of all values in the + * slice between (offset, offset + size - 1). + */ + +/** + * \ingroup psumtree + * \function igraph_psumtree_init + * \brief Initializes a partial prefix sum tree. + * + * + * The tree is initialized with a fixed number of elements. After initialization, + * the value corresponding to each element is zero. + * + * \param t The tree to initialize + * \param size The number of elements in the tree + * \return Error code, typically \c IGRAPH_ENOMEM if there is not enough memory + * + * Time complexity: O(n) for a tree containing n elements + */ +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(&t->v, t->offset + t->size)); + return 0; +} + +/** + * \ingroup psumtree + * \function igraph_psumtree_reset + * \brief Resets all the values in the tree to zero. + * + * \param t The tree to reset. + */ +void igraph_psumtree_reset(igraph_psumtree_t *t) { + igraph_vector_fill(&(t->v), 0); +} + +/** + * \ingroup psumtree + * \function igraph_psumtree_destroy + * \brief Destroys a partial prefix sum tree. + * + * + * All partial prefix sum trees initialized by \ref igraph_psumtree_init() + * should be properly destroyed by this function. A destroyed tree needs to be + * reinitialized by \ref igraph_psumtree_init() if you want to use it again. + * + * \param t Pointer to the (previously initialized) tree to destroy. + * + * Time complexity: operating system dependent. + */ +void igraph_psumtree_destroy(igraph_psumtree_t *t) { + igraph_vector_destroy(&(t->v)); +} + +/** + * \ingroup psumtree + * \function igraph_psumtree_get + * \brief Retrieves the value corresponding to an item in the tree. + * + * + * + * \param t The tree to query. + * \param idx The index of the item whose value is to be retrieved. + * \return The value corresponding to the item with the given index. + * + * Time complexity: O(1) + */ +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]; +} + +/** + * \ingroup psumtree + * \function igraph_psumtree_search + * \brief Finds an item in the tree, given a value. + * + * This function finds the item with the lowest index where it holds that the + * sum of all the items with a \em lower index is less than or equal to the given + * value and that the sum of all the items with a lower index plus the item + * itself is larger than the given value. + * + * + * If you think about the partial prefix sum tree as a tool to sample from a + * discrete probability distribution, then calling this function repeatedly + * with uniformly distributed random numbers in the range 0 (inclusive) to the + * sum of all values in the tree (exclusive) will sample the items in the tree + * with a probability that is proportional to their associated values. + * + * \param t The tree to query. + * \param idx The index of the item is returned here. + * \param search The value to use for the search. + * \return Error code; currently the search always succeeds. + * + * Time complexity: O(log n), where n is the number of items in the tree. + */ +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; +} + +/** + * \ingroup psumtree + * \function igraph_psumtree_update + * \brief Updates the value associated to an item in the tree. + * + * \param t The tree to query. + * \param idx The index of the item to update. + * \param new_value The new value of the item. + * \return Error code, \c IGRAPH_EINVAL if the new value is negative or NaN, + * \c IGRAPH_SUCCESS if the operation was successful. + * + * Time complexity: O(log n), where n is the number of items in the tree. + */ +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; + + if (new_value >= 0) { + 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; + } else { + /* caters for negative values and NaN */ + return IGRAPH_EINVAL; + } +} + +/** + * \ingroup psumtree + * \function igraph_psumtree_size + * \brief Returns the size of the tree. + * + * \param t The tree object + * \return The number of discrete items in the tree. + * + * Time complexity: O(1). + */ +long int igraph_psumtree_size(const igraph_psumtree_t *t) { + return t->size; +} + +/** + * \ingroup psumtree + * \function igraph_psumtree_sum + * \brief Returns the sum of the values of the leaves in the tree. + * + * \param t The tree object + * \return The sum of the values of the leaves in the tree. + * + * Time complexity: O(1). + */ +igraph_real_t igraph_psumtree_sum(const igraph_psumtree_t *t) { + return VECTOR(t->v)[0]; +} diff --git a/src/rigraph/core/core/set.c b/src/rigraph/core/core/set.c new file mode 100644 index 0000000..7ce264d --- /dev/null +++ b/src/rigraph/core/core/set.c @@ -0,0 +1,320 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2006-2012 Gabor Csardi + 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 "core/set.h" + +#include /* 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; + + if (size < 0) { + size = 0; + } + alloc_size = size > 0 ? size : 1; + 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) { + IGRAPH_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; + IGRAPH_ASSERT(set != NULL); + IGRAPH_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) { + IGRAPH_ASSERT(set != NULL); + IGRAPH_ASSERT(set->stor_begin != NULL); + return set->stor_begin == set->end; +} + +/** + * \ingroup set + * \function igraph_set_clear + * \brief Removes all elements from a set. + * + * + * 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) { + IGRAPH_ASSERT(set != NULL); + IGRAPH_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) { + IGRAPH_ASSERT(set != NULL); + IGRAPH_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; + IGRAPH_ASSERT(set != NULL); + IGRAPH_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. + */ +igraph_bool_t igraph_set_contains(igraph_set_t* set, igraph_integer_t e) { + long int left, right, middle; + + IGRAPH_ASSERT(set != NULL); + IGRAPH_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) { + IGRAPH_ASSERT(set != 0); + IGRAPH_ASSERT(set->stor_begin != 0); + IGRAPH_ASSERT(state != 0); + IGRAPH_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/src/rigraph/core/core/set.h b/src/rigraph/core/core/set.h new file mode 100644 index 0000000..95697c0 --- /dev/null +++ b/src/rigraph/core/core/set.h @@ -0,0 +1,65 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2009-2020 The igraph development 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., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#ifndef IGRAPH_CORE_SET_H +#define IGRAPH_CORE_SET_H + +#include "igraph_decls.h" +#include "igraph_types.h" + +__BEGIN_DECLS + +/* -------------------------------------------------- */ +/* 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) + +IGRAPH_PRIVATE_EXPORT int igraph_set_init(igraph_set_t* set, long int size); +IGRAPH_PRIVATE_EXPORT void igraph_set_destroy(igraph_set_t* set); +IGRAPH_PRIVATE_EXPORT igraph_bool_t igraph_set_inited(igraph_set_t* set); +IGRAPH_PRIVATE_EXPORT int igraph_set_reserve(igraph_set_t* set, long int size); +IGRAPH_PRIVATE_EXPORT igraph_bool_t igraph_set_empty(const igraph_set_t* set); +IGRAPH_PRIVATE_EXPORT void igraph_set_clear(igraph_set_t* set); +IGRAPH_PRIVATE_EXPORT long int igraph_set_size(const igraph_set_t* set); +IGRAPH_PRIVATE_EXPORT int igraph_set_add(igraph_set_t* v, igraph_integer_t e); +IGRAPH_PRIVATE_EXPORT igraph_bool_t igraph_set_contains(igraph_set_t* set, igraph_integer_t e); +IGRAPH_PRIVATE_EXPORT igraph_bool_t igraph_set_iterate(igraph_set_t* set, long int* state, + igraph_integer_t* element); + +__END_DECLS + +#endif diff --git a/src/rigraph/core/core/sparsemat.c b/src/rigraph/core/core/sparsemat.c new file mode 100644 index 0000000..c7c836e --- /dev/null +++ b/src/rigraph/core/core/sparsemat.c @@ -0,0 +1,3273 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2009-2012 Gabor Csardi + 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 + +#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 + +/** + * \section about_sparsemat About sparse matrices + * + * + * The igraph_sparsemat_t data type stores sparse matrices, + * i.e. matrices in which the majority of the elements are zero. + * + * + * 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 + * + * + * + * 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(). + * + * + * + * Both formats are dynamic, in the sense that new elements can be + * added to them, possibly resulting the allocation of more memory. + * + * + * + * Row and column indices follow the C convention and are zero-based. + * + * + * + * \example examples/simple/igraph_sparsemat.c + * \example examples/simple/igraph_sparsemat3.c + * \example examples/simple/igraph_sparsemat4.c + * \example examples/simple/igraph_sparsemat6.c + * \example examples/simple/igraph_sparsemat7.c + * \example examples/simple/igraph_sparsemat8.c + * + */ + +/** + * \function igraph_sparsemat_init + * \brief Initializes 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. + * + * 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 + * \brief Copies a sparse matrix. + * + * Create a sparse matrix object, by copying another one. The source + * matrix can be either in triplet or column-compressed format. + * + * + * 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) { + + CS_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 + * \brief Deallocates 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 + * \brief Allocates 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) { + if (!cs_sprealloc(A->cs, nzmax)) { + IGRAPH_ERROR("Could not allocate more memory for sparse matrix.", IGRAPH_ENOMEM); + } + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_sparsemat_nrow + * \brief 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 + * \brief 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 + * \brief 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 + * \brief 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 + * \brief 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 + * \brief Permutes 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) { + + CS_INT nrow = A->cs->m, ncol = A->cs->n; + igraph_vector_int_t pinv; + CS_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 */ + res->cs = cs_permute(A->cs, VECTOR(pinv), VECTOR(*q), /*values=*/ 1); + if (!res->cs) { + IGRAPH_ERROR("Cannot index sparse matrix", IGRAPH_FAILURE); + } + + igraph_vector_int_destroy(&pinv); + IGRAPH_FINALLY_CLEAN(1); + + return 0; +} + +static 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; + CS_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; +} + +static 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; + CS_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 + * \brief Extracts a submatrix or a single element. + * + * This function indexes into a sparse matrix. + * It 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; + CS_INT nrow = A->cs->m; + CS_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 + * \brief Adds 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(). If you add + * multiple entries in the same position, they will all be saved, and + * the resulting value is the sum of all entries in that position. + * + * \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: O(1) on average. + */ + +int igraph_sparsemat_entry(igraph_sparsemat_t *A, int row, int col, + igraph_real_t elem) { + if (!igraph_sparsemat_is_triplet(A)) { + IGRAPH_ERROR("Entries can only be added to sparse matrices that are in triplet format.", + IGRAPH_EINVAL); + } + + if (!cs_entry(A->cs, row, col, elem)) { + IGRAPH_ERROR("Cannot add entry to sparse matrix.", + IGRAPH_FAILURE); + } + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_sparsemat_compress + * \brief Converts a sparse matrix to column-compressed format. + * + * Converts a sparse matrix from triplet format to column-compressed 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: O(nz) where \c nz is the number of non-zero elements. + */ + +int igraph_sparsemat_compress(const igraph_sparsemat_t *A, + igraph_sparsemat_t *res) { + + if (! igraph_sparsemat_is_triplet(A)) { + IGRAPH_ERROR("Sparse matrix to compress is not in triplet format.", IGRAPH_EINVAL); + } + res->cs = cs_compress(A->cs); + if (!res->cs) { + IGRAPH_ERROR("Cannot compress sparse matrix", IGRAPH_FAILURE); + } + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_sparsemat_transpose + * \brief Transposes 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 */ + res->cs = cs_transpose(A->cs, values); + if (!res->cs) { + IGRAPH_ERROR("Cannot transpose sparse matrix", IGRAPH_FAILURE); + } + } else { + /* triplets */ + CS_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; +} + +static int igraph_i_sparsemat_is_symmetric_cc(const igraph_sparsemat_t *A, igraph_bool_t *result) { + 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); + + *result = res; + + return IGRAPH_SUCCESS; +} + +static int igraph_i_sparsemat_is_symmetric_triplet(const igraph_sparsemat_t *A, igraph_bool_t *result) { + igraph_sparsemat_t tmp; + + IGRAPH_CHECK(igraph_sparsemat_compress(A, &tmp)); + IGRAPH_FINALLY(igraph_sparsemat_destroy, &tmp); + IGRAPH_CHECK(igraph_i_sparsemat_is_symmetric_cc(&tmp, result)); + igraph_sparsemat_destroy(&tmp); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + +igraph_bool_t igraph_sparsemat_is_symmetric(const igraph_sparsemat_t *A) { + igraph_bool_t res = 0; + + if (A->cs->m != A->cs->n) { + return 0; + } + + /* TODO(ntamas): return values from igraph_i_sparsemat_is_symmetric_... are + * ignored here; this should be fixed. Right now these functions don't + * change 'res' if they fail so we will report matrices as not being + * symmetric if an error happens */ + if (A->cs->nz < 0) { + igraph_i_sparsemat_is_symmetric_cc(A, &res); + } else { + igraph_i_sparsemat_is_symmetric_triplet(A, &res); + } + + return res; +} + +/** + * \function igraph_sparsemat_dupl + * \brief Removes 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 + * \brief Filters 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, + igraph_integer_t (*fkeep)(igraph_integer_t, igraph_integer_t, igraph_real_t, void*), + void *other +) { + + IGRAPH_ASSERT(A); + IGRAPH_ASSERT(fkeep); + if (!igraph_sparsemat_is_cc(A)) { + IGRAPH_ERROR("The sparse matrix is not in compressed format.", IGRAPH_EINVAL); + } + if (cs_fkeep(A->cs, fkeep, other) < 0) { + IGRAPH_ERROR("External function cs_keep has returned an unknown error while filtering the matrix.", IGRAPH_FAILURE); + } + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_sparsemat_dropzeros + * \brief Drops 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 + * \brief Drops the almost zero elements from 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) { + + IGRAPH_ASSERT(A); + if (!igraph_sparsemat_is_cc(A)) { + IGRAPH_ERROR("The sparse matrix is not in compressed format.", IGRAPH_EINVAL); + } + if (cs_droptol(A->cs, tol) < 0) { + IGRAPH_ERROR("External function cs_droptol has returned an unknown error.", IGRAPH_FAILURE); + } + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_sparsemat_multiply + * \brief 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) { + + res->cs = cs_multiply(A->cs, B->cs); + if (!res->cs) { + IGRAPH_ERROR("Cannot multiply matrices", IGRAPH_FAILURE); + } + + return 0; +} + +/** + * \function igraph_sparsemat_add + * \brief 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) { + + res->cs = cs_add(A->cs, B->cs, alpha, beta); + if (!res->cs) { + IGRAPH_ERROR("Cannot add matrices", IGRAPH_FAILURE); + } + + return 0; +} + +/** + * \function igraph_sparsemat_gaxpy + * \brief 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 + * \brief Solves 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 + * \brief Solves 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 + * \brief Solves 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 + * \brief Solves 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 + * \brief Solves 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 + * \brief Solves 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; +} + +static int igraph_i_sparsemat_cc(igraph_t *graph, const igraph_sparsemat_t *A, + igraph_bool_t directed) { + + igraph_vector_t edges; + CS_INT no_of_nodes = A->cs->m; + CS_INT no_of_edges = A->cs->p[A->cs->n]; + CS_INT *p = A->cs->p; + CS_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; +} + +static int igraph_i_sparsemat_triplet(igraph_t *graph, const igraph_sparsemat_t *A, + igraph_bool_t directed) { + + igraph_vector_t edges; + CS_INT no_of_nodes = A->cs->m; + CS_INT no_of_edges = A->cs->nz; + CS_INT *i = A->cs->p; + CS_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 + * \brief Creates 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)); + } +} + +static 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) { + + CS_INT no_of_edges = A->cs->p[A->cs->n]; + CS_INT *p = A->cs->p; + CS_INT *i = A->cs->i; + CS_ENTRY *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; +} + +static 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); +} + +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; + CS_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; + CS_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 + * \brief Converts 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 + * \brief Prints 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 */ + CS_INT j, p; + for (j = 0; j < A->cs->n; j++) { + CHECK(fprintf(outstream, "col " CS_ID ": locations " CS_ID " to " CS_ID "\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, CS_ID " : %g\n", A->cs->i[p], A->cs->x[p])); + } + } + } else { + /* Triplet */ + CS_INT p; + for (p = 0; p < A->cs->nz; p++) { + CHECK(fprintf(outstream, CS_ID " " CS_ID " : %g\n", + A->cs->i[p], A->cs->p[p], A->cs->x[p])); + } + } + + return 0; +} + +#undef CHECK + +static 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; +} + +static int igraph_i_sparsemat_eye_cc(igraph_sparsemat_t *A, int n, + igraph_real_t value) { + CS_INT i; + + A->cs = cs_spalloc(n, n, n, /*values=*/ 1, /*triplet=*/ 0); + if (!A->cs) { + 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] = value; + } + A->cs->p [n] = n; + + return 0; +} + +/** + * \function igraph_sparsemat_eye + * \brief Creates 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)); + } +} + +static 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; + +} + +static int igraph_i_sparsemat_diag_cc(igraph_sparsemat_t *A, + const igraph_vector_t *values) { + + CS_INT i, n = igraph_vector_size(values); + + A->cs = cs_spalloc(n, n, n, /*values=*/ 1, /*triplet=*/ 0); + if (!A->cs) { + 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 + * \brief Creates 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)); + } +} + +static 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; + +static 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 + * \brief 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 = # + 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 + * \brief 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 + * \brief 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 + * \brief 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 + * \brief 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 + * \brief 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 + * \brief Solves a 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 + * \brief Solves 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 + * \brief Deallocates memory after 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 + * \brief Deallocates memory after 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 + * \brief Converts 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; +} + +static int igraph_i_sparsemat_as_matrix_cc(igraph_matrix_t *res, + const igraph_sparsemat_t *spmat) { + + long int nrow = igraph_sparsemat_nrow(spmat); + long int ncol = igraph_sparsemat_ncol(spmat); + CS_INT from = 0, to = 0; + CS_INT *p = spmat->cs->p; + CS_INT *i = spmat->cs->i; + CS_ENTRY *x = spmat->cs->x; + CS_INT nzmax = spmat->cs->nzmax; + + 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; +} + +static int igraph_i_sparsemat_as_matrix_triplet(igraph_matrix_t *res, + const igraph_sparsemat_t *spmat) { + long int nrow = igraph_sparsemat_nrow(spmat); + long int ncol = igraph_sparsemat_ncol(spmat); + CS_INT *i = spmat->cs->p; + CS_INT *j = spmat->cs->i; + CS_ENTRY *x = spmat->cs->x; + CS_INT nz = spmat->cs->nz; + CS_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 + * \brief Converts 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 + * \brief 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) { + CS_INT i, n; + CS_ENTRY *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 + * \brief 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) { + CS_INT i, n; + CS_ENTRY *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 + * \brief 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) { + CS_INT i, n; + CS_ENTRY *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 + * \brief Counts 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) { + CS_INT i, n; + CS_ENTRY *ptr; + int res = 0; + + 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 + * \brief Counts 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) { + CS_INT i, n; + CS_ENTRY *ptr; + int res = 0; + + 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; +} + +static int igraph_i_sparsemat_rowsums_triplet(const igraph_sparsemat_t *A, + igraph_vector_t *res) { + CS_INT i; + CS_INT *pi = A->cs->i; + CS_ENTRY *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; +} + +static int igraph_i_sparsemat_rowsums_cc(const igraph_sparsemat_t *A, + igraph_vector_t *res) { + CS_INT ne = A->cs->p[A->cs->n]; + CS_ENTRY *px = A->cs->x; + CS_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 + * \brief 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); + } +} + +static int igraph_i_sparsemat_rowmins_triplet(const igraph_sparsemat_t *A, + igraph_vector_t *res) { + CS_INT i; + CS_INT *pi = A->cs->i; + CS_ENTRY *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; +} + +static int igraph_i_sparsemat_rowmins_cc(igraph_sparsemat_t *A, + igraph_vector_t *res) { + CS_INT ne; + CS_ENTRY *px; + CS_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); + } +} + + +static int igraph_i_sparsemat_rowmaxs_triplet(const igraph_sparsemat_t *A, + igraph_vector_t *res) { + CS_INT i; + CS_INT *pi = A->cs->i; + CS_ENTRY *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; +} + +static int igraph_i_sparsemat_rowmaxs_cc(igraph_sparsemat_t *A, + igraph_vector_t *res) { + CS_INT ne; + CS_ENTRY *px; + CS_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); + } +} + +static int igraph_i_sparsemat_colmins_triplet(const igraph_sparsemat_t *A, + igraph_vector_t *res) { + CS_INT i; + CS_INT *pp = A->cs->p; + CS_ENTRY *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; +} + +static int igraph_i_sparsemat_colmins_cc(igraph_sparsemat_t *A, + igraph_vector_t *res) { + CS_INT n; + CS_ENTRY *px; + CS_INT *pp; + CS_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); + } +} + +static int igraph_i_sparsemat_colmaxs_triplet(const igraph_sparsemat_t *A, + igraph_vector_t *res) { + CS_INT i; + CS_INT *pp = A->cs->p; + CS_ENTRY *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; +} + +static int igraph_i_sparsemat_colmaxs_cc(igraph_sparsemat_t *A, + igraph_vector_t *res) { + CS_INT n; + CS_ENTRY *px; + CS_INT *pp; + CS_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); + } +} + +static int igraph_i_sparsemat_which_min_rows_triplet(igraph_sparsemat_t *A, + igraph_vector_t *res, + igraph_vector_int_t *pos) { + CS_INT i; + CS_INT *pi = A->cs->i; + CS_INT *pp = A->cs->p; + CS_ENTRY *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; +} + +static int igraph_i_sparsemat_which_min_rows_cc(igraph_sparsemat_t *A, + igraph_vector_t *res, + igraph_vector_int_t *pos) { + CS_INT n; + CS_ENTRY *px; + CS_INT *pp; + CS_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); + } +} + +static int igraph_i_sparsemat_which_min_cols_triplet(igraph_sparsemat_t *A, + igraph_vector_t *res, + igraph_vector_int_t *pos) { + + CS_INT i; + CS_INT *pi = A->cs->i; + CS_INT *pp = A->cs->p; + CS_ENTRY *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; +} + +static int igraph_i_sparsemat_which_min_cols_cc(igraph_sparsemat_t *A, + igraph_vector_t *res, + igraph_vector_int_t *pos) { + CS_INT n, j, p; + CS_ENTRY *px; + double *pr; + igraph_integer_t *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); + } +} + +static int igraph_i_sparsemat_colsums_triplet(const igraph_sparsemat_t *A, + igraph_vector_t *res) { + CS_INT i; + CS_INT *pp = A->cs->p; + CS_ENTRY *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; +} + +static int igraph_i_sparsemat_colsums_cc(const igraph_sparsemat_t *A, + igraph_vector_t *res) { + CS_INT n = A->cs->n; + CS_ENTRY *px = A->cs->x; + CS_INT *pp = A->cs->p; + CS_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 + * \brief 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 + * \brief Scales 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) { + + CS_ENTRY *px = A->cs->x; + CS_INT n = A->cs->nz == -1 ? A->cs->p[A->cs->n] : A->cs->nz; + CS_ENTRY *stop = px + n; + + for (; px < stop; px++) { + *px *= by; + } + + return 0; +} + +/** + * \function igraph_sparsemat_add_rows + * \brief Adds 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 + * \brief Adds 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 { + CS_INT realloc_ok = 0, i; + CS_INT *newp = cs_realloc(A->cs->p, (A->cs->n + n + 1), sizeof(int), &realloc_ok); + if (!realloc_ok) { + 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 + * \brief Resizes 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; +} + +/** + * \function igraph_sparsemat_nonzero_storage + * \brief Returns number of stored entries of a sparse matrix. + * + * This function will return the number of stored entries of a sparse + * matrix. These entries can be zero, and multiple entries can be + * at the same position. Use \ref igraph_sparsemat_dupl() to sum + * duplicate entries, and \ref igraph_sparsemat_dropzeros() to remove + * zeros. + * + * \param A A sparse matrix in either triplet or compressed form. + * \return Number of stored entries. + * + * Time complexity: O(1). + */ + +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) { + CS_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) { + CS_INT *i = A->cs->i; + CS_ENTRY *x = A->cs->x; + CS_INT no_of_edges = A->cs->nz < 0 ? A->cs->p[A->cs->n] : A->cs->nz; + CS_INT e; + + for (e = 0; e < no_of_edges; e++, x++, i++) { + igraph_real_t f = VECTOR(*fact)[*i]; + (*x) *= f; + } + + return 0; +} + +static int igraph_i_sparsemat_scale_cols_cc(igraph_sparsemat_t *A, + const igraph_vector_t *fact) { + CS_INT *i = A->cs->i; + CS_ENTRY *x = A->cs->x; + CS_INT no_of_edges = A->cs->p[A->cs->n]; + CS_INT e; + CS_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; +} + +static int igraph_i_sparsemat_scale_cols_triplet(igraph_sparsemat_t *A, + const igraph_vector_t *fact) { + CS_INT *j = A->cs->p; + CS_ENTRY *x = A->cs->x; + CS_INT no_of_edges = A->cs->nz; + CS_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; + CS_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; +} + +/** + * \function igraph_sparsemat_view + * \brief Initialize a sparse matrix and set all parameters. + * + * This function can be used to temporarily handle existing sparse matrix data, + * usually created by another software library, as an \c igraph_sparsemat_t object, + * and thus avoid unnecessary copying. It supports data stored in either the + * compressed sparse column format, or the (i, j, x) triplet format + * where \c i and \c j are the matrix indices of a non-zero element, and \c x + * is its value. + * + * + * The compressed sparse column (or row) format is commonly used to represent + * sparse matrix data. It consists of three vectors, the \p p column pointers, the + * \p i row indices, and the \p x values. p[k] is the number + * of non-zero entires in matrix columns k-1 and lower. + * p[0] is always zero and p[n] is always the total + * number of non-zero entires in the matrix. i[l] is the row index + * of the \c l-th stored element, while x[l] is its value. + * If a matrix element with indices (j, k) is explicitly stored, + * it must be located between positions p[k] and p[k+1] - 1 + * (inclusive) in the \p i and \p x vectors. + * + * + * Do not call \ref igraph_sparsemat_destroy() on a sparse matrix created with + * this function. Instead, \ref igraph_free() must be called on the \c cs + * field of \p A to free the storage allocated by this function. + * + * + * Warning: Matrices created with this function must not be used with functions + * that may reallocate the underlying storage, such as \ref igraph_sparsemat_entry(). + * + * \param A The non-initialized sparse matrix. + * \param nzmax The maximum number of entries, typically the actual number of entries. + * \param m The number of matrix rows. + * \param n The number of matrix columns. + * \param p For a compressed matrix, this is the column pointer vector, and + * must be of size n+1. For a triplet format matrix, it + * is a vector of column indices and must be of size \p nzmax. + * \param i The row vector. This should contain the row indices of the + * elements in \p x. It must be of size \p nzmax. + * \param x The values of the non-zero elements of the sparse matrix. + * It must be of size \p nzmax. + * \param nz For a compressed matrix, is must be -1. For a triplet format + * matrix, is must contain the number of entries. + * \return Error code. + * + * Time complexity: O(1). + */ + +int igraph_sparsemat_view(igraph_sparsemat_t *A, int nzmax, int m, int n, + int *p, int *i, double *x, int nz) { + + A->cs = IGRAPH_CALLOC(1, 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 IGRAPH_SUCCESS; +} + +int igraph_i_sparsemat_view(igraph_sparsemat_t *A, int nzmax, int m, int n, + int *p, int *i, double *x, int nz) { + IGRAPH_WARNING("igraph_i_sparsemat_view() is deprecated, use igraph_sparsemat_view()"); + return igraph_sparsemat_view(A, nzmax, m, n, p, i, x, nz); +} + +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; +} + +/** + * \function igraph_sparsemat_getelements_sorted + * \brief Returns the sorted elements of a sparse matrix. + * + * This function will sort a sparse matrix and return the elements in + * 3 vectors. Two vectors will indicate where the elements are located, + * and one will give the elements. + * + * \param A A sparse matrix in either triplet or compressed form. + * \param i An initialized int vector. This will store the rows of the + * returned elements. + * \param j An initialized int vector. For a triplet matrix this will + * store the columns of the returned elements. For a compressed + * matrix, if the column index is \c k, then j[k] + * is the index in \p x of the start of the \c k-th column, and + * the last element of \c j is the total number of elements. + * The total number of elements in the \c k-th column is + * therefore j[k+1] - j[k]. For example, if there + * is one element in the first column, and five in the second, + * \c j will be set to {0, 1, 6}. + * \param x An initialized vector. The elements will be placed here. + * \return Error code. + * + * Time complexity: O(n), the number of stored elements in the sparse matrix. + */ + +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 IGRAPH_SUCCESS; +} + +int igraph_sparsemat_nzmax(const igraph_sparsemat_t *A) { + return A->cs->nzmax; +} + +int igraph_sparsemat_neg(igraph_sparsemat_t *A) { + CS_INT i, nz = A->cs->nz == -1 ? A->cs->p[A->cs->n] : A->cs->nz; + CS_ENTRY *px = A->cs->x; + + for (i = 0; i < nz; i++, px++) { + *px = - (*px); + } + + return 0; +} + +/** + * \function igraph_sparsemat_iterator_init + * \brief Initialize a sparse matrix iterator. + * + * \param it A pointer to an uninitialized sparse matrix iterator. + * \param sparsemat Pointer to the sparse matrix. + * \return Error code. This will always return \c IGRAPH_SUCCESS + * + * Time complexity: O(n), the number of columns of the sparse matrix. + */ + +int igraph_sparsemat_iterator_init(igraph_sparsemat_iterator_t *it, + igraph_sparsemat_t *sparsemat) { + + it->mat = sparsemat; + igraph_sparsemat_iterator_reset(it); + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_sparsemat_iterator_reset + * \brief Reset a sparse matrix iterator to the first element. + * + * \param it A pointer to the sparse matrix iterator. + * \return Error code. This will always return \c IGRAPH_SUCCESS + * + * Time complexity: O(n), the number of columns of the sparse matrix. + */ + +int igraph_sparsemat_iterator_reset(igraph_sparsemat_iterator_t *it) { + it->pos = 0; + it->col = 0; + if (!igraph_sparsemat_is_triplet(it->mat)) { + while (it->col < it->mat->cs->n && + it->mat->cs->p[it->col + 1] == it->pos) { + it->col ++; + } + } + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_sparsemat_iterator_end + * \brief Query if the iterator is past the last element. + * + * \param it A pointer to the sparse matrix iterator. + * \return true if the iterator is past the last element, false if it + * points to an element in a sparse matrix. + * + * Time complexity: O(1). + */ + +igraph_bool_t +igraph_sparsemat_iterator_end(const igraph_sparsemat_iterator_t *it) { + CS_INT nz = it->mat->cs->nz == -1 ? it->mat->cs->p[it->mat->cs->n] : + it->mat->cs->nz; + return it->pos >= nz; +} + +/** + * \function igraph_sparsemat_iterator_row + * \brief Return the row of the iterator. + * + * \param it A pointer to the sparse matrix iterator. + * \return The row of the element at the current iterator position. + * + * Time complexity: O(1). + */ + +int igraph_sparsemat_iterator_row(const igraph_sparsemat_iterator_t *it) { + return it->mat->cs->i[it->pos]; +} + +/** + * \function igraph_sparsemat_iterator_col + * \brief Return the column of the iterator. + * + * \param it A pointer to the sparse matrix iterator. + * \return The column of the element at the current iterator position. + * + * Time complexity: O(1). + */ + +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; + } +} + +/** + * \function igraph_sparsemat_iterator_get + * \brief Return the element at the current iterator position. + * + * \param it A pointer to the sparse matrix iterator. + * \return The value of the element at the current iterator position. + * + * Time complexity: O(1). + */ + +igraph_real_t +igraph_sparsemat_iterator_get(const igraph_sparsemat_iterator_t *it) { + return it->mat->cs->x[it->pos]; +} + +/** + * \function igraph_sparsemat_iterator_next + * \brief Let a sparse matrix iterator go to the next element. + * + * \param it A pointer to the sparse matrix iterator. + * \return The position of the iterator in the element vector. + * + * Time complexity: O(n), the number of columns of the sparse matrix. + */ + +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; +} + +/** + * \function igraph_sparsemat_iterator_idx + * \brief Returns the element vector index of a sparse matrix iterator. + * + * \param it A pointer to the sparse matrix iterator. + * \return The position of the iterator in the element vector. + * + * Time complexity: O(1). + */ + +int igraph_sparsemat_iterator_idx(const igraph_sparsemat_iterator_t *it) { + return it->pos; +} diff --git a/src/rigraph/core/core/spmatrix.c b/src/rigraph/core/core/spmatrix.c new file mode 100644 index 0000000..7d2a7ec --- /dev/null +++ b/src/rigraph/core/core/spmatrix.c @@ -0,0 +1,1066 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et */ +/* + IGraph library. + Copyright (C) 2003-2012 Gabor Csardi + 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_error.h" + +#include /* memcpy & co. */ + +/** + * \section igraph_spmatrix_constructor_and_destructor Sparse matrix constructors + * and destructors. + */ + +/** + * \ingroup matrix + * \function igraph_spmatrix_init + * \brief Initializes a sparse matrix. + * + * + * 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) { + IGRAPH_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. + * + * + * 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) { + IGRAPH_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. + * + * + * 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) { + IGRAPH_ASSERT(from != NULL); + IGRAPH_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; + + IGRAPH_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; + + IGRAPH_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; + + IGRAPH_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; + if (to < 0 || to >= m->ncol) { + IGRAPH_ERROR("The 'to' column does not exist.", IGRAPH_EINVAL); + } + if (from < 0 || from >= m->ncol) { + IGRAPH_ERROR("The 'from' column does not exist.", IGRAPH_EINVAL); + } + /* 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 IGRAPH_SUCCESS; +} + + +/** + * \ingroup matrix + * \function igraph_spmatrix_resize + * \brief Resizes a sparse matrix. + * + * + * 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; + IGRAPH_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) { + IGRAPH_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) { + IGRAPH_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) { + IGRAPH_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) { + IGRAPH_ASSERT(m != NULL); + return m->ncol; +} + +/** + * \ingroup matrix + * \brief Copies a sparse matrix to a regular C array. + * + * + * 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) { + IGRAPH_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. + * \return Error code. + * + * Time complexity: O(n), the number of nonzero elements in the matrix. + */ + +int igraph_spmatrix_clear_row(igraph_spmatrix_t *m, long int row) { + if (row < 0 || row >= m->nrow) { + IGRAPH_ERROR("The row does not exist.", IGRAPH_EINVAL); + } + long int ci, ei, i, j, nremove = 0, nremove_old = 0; + igraph_vector_t permvec; + + IGRAPH_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 IGRAPH_SUCCESS; +} + +/* Unused local functions---temporarily disabled */ +#if 0 +static int igraph_i_spmatrix_clear_row_fast(igraph_spmatrix_t *m, long int row) { + long int ei, n; + + IGRAPH_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; +} + +static int igraph_i_spmatrix_cleanup(igraph_spmatrix_t *m) { + long int ci, ei, i, j, nremove = 0, nremove_old = 0; + igraph_vector_t permvec; + + IGRAPH_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; +} +#endif + +/** + * \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. + * + * Time complexity: TODO + */ + +int igraph_spmatrix_clear_col(igraph_spmatrix_t *m, long int col) { + if (col < 0 || col >= m->ncol) { + IGRAPH_ERROR("The column does not exist.", IGRAPH_EINVAL); + } + long int i, n; + IGRAPH_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 IGRAPH_SUCCESS; +} + +/** + * \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) { + IGRAPH_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; + IGRAPH_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; + IGRAPH_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; + + IGRAPH_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; + + IGRAPH_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; +} + + +/* Unused function, temporarily disabled */ +/* +static int igraph_i_spmatrix_get_col_nonzero_indices(const igraph_spmatrix_t *m, + igraph_vector_t *res, long int col) { + long int i, n; + IGRAPH_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 + * + * 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. + * + * 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. + */ + +/** + * \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. + * + * + * 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) { + IGRAPH_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. + * + * + * 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. + * + * + * 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. + * + * + * 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/src/rigraph/core/core/stack.c b/src/rigraph/core/core/stack.c new file mode 100644 index 0000000..66ce729 --- /dev/null +++ b/src/rigraph/core/core/stack.c @@ -0,0 +1,88 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2007-2012 Gabor Csardi + 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_error.h" +#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; + IGRAPH_ASSERT(v != 0); + IGRAPH_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) { + IGRAPH_ASSERT(v != 0); + IGRAPH_ASSERT(v->stor_begin != 0); + igraph_stack_ptr_free_all(v); + igraph_stack_ptr_destroy(v); +} diff --git a/src/rigraph/core/core/stack.pmt b/src/rigraph/core/core/stack.pmt new file mode 100644 index 0000000..dc57fcc --- /dev/null +++ b/src/rigraph/core/core/stack.pmt @@ -0,0 +1,293 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2003-2012 Gabor Csardi + 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 /* memcpy & co. */ +#include + +/** + * \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; + IGRAPH_ASSERT(s != NULL); + if (size < 0) { + size = 0; + } + alloc_size = size > 0 ? size : 1; + 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) { + IGRAPH_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; + IGRAPH_ASSERT(s != NULL); + IGRAPH_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) { + IGRAPH_ASSERT(s != NULL); + IGRAPH_ASSERT(s->stor_begin != NULL); + IGRAPH_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) { + IGRAPH_ASSERT(s != NULL); + IGRAPH_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) { + IGRAPH_ASSERT(s != NULL); + IGRAPH_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) { + IGRAPH_ASSERT(s != NULL); + IGRAPH_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), 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); + 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) { + + IGRAPH_ASSERT(s != NULL); + IGRAPH_ASSERT(s->stor_begin != NULL); + IGRAPH_ASSERT(s->end != NULL); + IGRAPH_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) { + + IGRAPH_ASSERT(s != NULL); + IGRAPH_ASSERT(s->stor_begin != NULL); + IGRAPH_ASSERT(s->end != NULL); + IGRAPH_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/src/rigraph/core/core/statusbar.c b/src/rigraph/core/core/statusbar.c new file mode 100644 index 0000000..07cdad2 --- /dev/null +++ b/src/rigraph/core/core/statusbar.c @@ -0,0 +1,130 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2010-2012 Gabor Csardi + 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_statusbar.h" +#include "igraph_error.h" + +#include "config.h" + +#include +#include + +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/src/rigraph/core/core/strvector.c b/src/rigraph/core/core/strvector.c new file mode 100644 index 0000000..080b356 --- /dev/null +++ b/src/rigraph/core/core/strvector.c @@ -0,0 +1,603 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2003-2012 Gabor Csardi + 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_error.h" + +#include /* memcpy & co. */ +#include + +/** + * \section igraph_strvector_t + * The igraph_strvector_t 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. + * + * + * \example examples/simple/igraph_strvector.c + * + */ + +/** + * \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; + IGRAPH_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 char*, 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) { + IGRAPH_ASSERT(sv != 0); + IGRAPH_ASSERT(sv->data != 0); + IGRAPH_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) { + size_t value_len; + + IGRAPH_ASSERT(sv != 0); + IGRAPH_ASSERT(sv->data != 0); + + value_len = strlen(value); + if (sv->data[idx] == 0) { + sv->data[idx] = IGRAPH_CALLOC(value_len + 1, char); + if (sv->data[idx] == 0) { + IGRAPH_ERROR("strvector set failed", IGRAPH_ENOMEM); + } + } else { + char *tmp = IGRAPH_REALLOC(sv->data[idx], value_len + 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) { + if (idx < 0 || idx >= sv->len) { + IGRAPH_ERROR("String vector index out of bounds.", IGRAPH_EINVAL); + } + IGRAPH_ASSERT(sv != 0); + IGRAPH_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 IGRAPH_SUCCESS; +} + +/** + * \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; */ + + IGRAPH_ASSERT(v != 0); + IGRAPH_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 v 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) { + IGRAPH_ASSERT(v != 0); + IGRAPH_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; + IGRAPH_ASSERT(v != 0); + IGRAPH_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; + IGRAPH_ASSERT(from != 0); + /* IGRAPH_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; + + IGRAPH_ASSERT(v != 0); + IGRAPH_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) { + IGRAPH_ASSERT(sv != 0); + IGRAPH_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); + long int value_len = strlen(value); + char **tmp; + IGRAPH_ASSERT(v != 0); + IGRAPH_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(value_len + 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; + IGRAPH_ASSERT(v != 0); + IGRAPH_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; + IGRAPH_ASSERT(v != 0); + IGRAPH_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; +} + +/** + * \ingroup strvector + * \function igraph_strvector_print + * \brief Prints a string vector. + * + * \param v The string vector. + * \param file The file to write to. + * \param sep The separator to print between strings. + * \return Error code. + */ +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 IGRAPH_SUCCESS; +} + +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/src/rigraph/core/core/trie.c b/src/rigraph/core/core/trie.c new file mode 100644 index 0000000..1ea9687 --- /dev/null +++ b/src/rigraph/core/core/trie.c @@ -0,0 +1,395 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2003-2012 Gabor Csardi + 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_random.h" +#include "igraph_error.h" + +#include "core/trie.h" + +#include "config.h" + +#include + +/** + * \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. + */ + +static 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; +} + +static void igraph_i_trie_destroy_node(igraph_trie_node_t *t); + +/** + * \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, (igraph_trie_node_t *) 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). + */ + +static void igraph_i_trie_destroy_node_helper(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_helper(child, 1); + } + } + igraph_vector_ptr_destroy(&t->children); + igraph_vector_destroy(&t->values); + if (sfree) { + IGRAPH_FREE(t); + } +} + +static void igraph_i_trie_destroy_node(igraph_trie_node_t *t) { + igraph_i_trie_destroy_node_helper(t, 0); +} + +/** + * \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); +} + + +/** + * \ingroup igraphtrie + * \brief Internal helping function for igraph_trie_t + */ + +static 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: + * - IGRAPH_ENOMEM: 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(igraph_free, str2); + IGRAPH_CHECK(igraph_strvector_set(&t->strs, i, str2)); + IGRAPH_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(igraph_free, str2); + IGRAPH_CHECK(igraph_strvector_set(&t->strs, i, str2)); + IGRAPH_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: + * - IGRAPH_ENOMEM: 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(igraph_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/src/rigraph/core/core/trie.h b/src/rigraph/core/core/trie.h new file mode 100644 index 0000000..d5f87ff --- /dev/null +++ b/src/rigraph/core/core/trie.h @@ -0,0 +1,72 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2009-2020 The igraph development 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., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#ifndef IGRAPH_CORE_TRIE_H +#define IGRAPH_CORE_TRIE_H + +#include "igraph_decls.h" +#include "igraph_types.h" +#include "igraph_strvector.h" +#include "igraph_vector.h" +#include "igraph_vector_ptr.h" + +__BEGIN_DECLS + +/** + * 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) + +IGRAPH_PRIVATE_EXPORT int igraph_trie_init(igraph_trie_t *t, igraph_bool_t storekeys); +IGRAPH_PRIVATE_EXPORT void igraph_trie_destroy(igraph_trie_t *t); +IGRAPH_PRIVATE_EXPORT int igraph_trie_get(igraph_trie_t *t, const char *key, long int *id); +IGRAPH_PRIVATE_EXPORT int igraph_trie_check(igraph_trie_t *t, const char *key, long int *id); +IGRAPH_PRIVATE_EXPORT int igraph_trie_get2(igraph_trie_t *t, const char *key, long int length, + long int *id); +IGRAPH_PRIVATE_EXPORT void igraph_trie_idx(igraph_trie_t *t, long int idx, char **str); +IGRAPH_PRIVATE_EXPORT int igraph_trie_getkeys(igraph_trie_t *t, const igraph_strvector_t **strv); +IGRAPH_PRIVATE_EXPORT long int igraph_trie_size(igraph_trie_t *t); + +__END_DECLS + +#endif diff --git a/src/rigraph/core/core/vector.c b/src/rigraph/core/core/vector.c new file mode 100644 index 0000000..68512db --- /dev/null +++ b/src/rigraph/core/core/vector.c @@ -0,0 +1,534 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2007-2012 Gabor Csardi + 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_error.h" +#include "igraph_types.h" +#include "igraph_vector.h" +#include "igraph_complex.h" + +#include + +#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 + +#include "core/math.h" + +#include "core/indheap.h" + +/** + * \ingroup vector + * \function igraph_vector_floor + * \brief Transform a real vector to a long vector by flooring each element. + * + * + * Flooring means rounding down to the nearest integer. + * + * \param from The original real vector object. + * \param to Pointer to an initialized long vector. The result will + * be stored here. + * \return Error code: + * \c IGRAPH_ENOMEM: out of memory + * + * Time complexity: O(n), where n is the number of elements in the vector. + */ +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 IGRAPH_SUCCESS; +} + +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. + * + * + * 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; + + IGRAPH_ASSERT(v != NULL); + IGRAPH_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; + + IGRAPH_ASSERT(v != NULL); + IGRAPH_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; + + IGRAPH_ASSERT(v != NULL); + IGRAPH_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) { + long int i, n = 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) { + long int i, n = 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) { + long int i, n = 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) { + long int i, n = 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) { + long int i, n = 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; + IGRAPH_ASSERT(lhs != 0); + IGRAPH_ASSERT(rhs != 0); + IGRAPH_ASSERT(lhs->stor_begin != 0); + IGRAPH_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) { + long 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; +} + +/** + * \ingroup vector + * \function igraph_vector_is_nan + * \brief Check for each element if it is NaN. + * + * + * \param v The \type igraph_vector_t object to check. + * \param is_nan The resulting boolean vector indicating for each element + * whether it is NaN or not. + * \return Error code, + * \c IGRAPH_ENOMEM if there is not enough + * memory. Note that this function \em never returns an error + * if the vector \p is_nan will already be large enough. + * Time complexity: O(n), the number of elements. + */ +int igraph_vector_is_nan(const igraph_vector_t *v, igraph_vector_bool_t *is_nan) +{ + igraph_real_t *ptr; + igraph_bool_t *ptr_nan; + IGRAPH_ASSERT(v != NULL); + IGRAPH_ASSERT(v->stor_begin != NULL); + IGRAPH_ASSERT(is_nan != NULL); + IGRAPH_ASSERT(is_nan->stor_begin != NULL); + IGRAPH_CHECK(igraph_vector_bool_resize(is_nan, igraph_vector_size(v))); + for (ptr = v->stor_begin, ptr_nan = is_nan->stor_begin; ptr < v->end; ptr++, ptr_nan++) { + *ptr_nan = igraph_is_nan(*ptr) ? 1 : 0; + } + return IGRAPH_SUCCESS; +} + +/** + * \ingroup vector + * \function igraph_vector_is_any_nan + * \brief Check if any element is NaN. + * + * + * \param v The \type igraph_vector_t object to check. + * \return 1 if any element is NaN, 0 otherwise. + * + * Time complexity: O(n), the number of elements. + */ +igraph_bool_t igraph_vector_is_any_nan(const igraph_vector_t *v) +{ + igraph_real_t *ptr; + IGRAPH_ASSERT(v != NULL); + IGRAPH_ASSERT(v->stor_begin != NULL); + ptr = v->stor_begin; + while (ptr < v->end) { + if (igraph_is_nan(*ptr)) { + return 1; + } + ptr++; + } + return 0; +} diff --git a/src/rigraph/core/core/vector.pmt b/src/rigraph/core/core/vector.pmt new file mode 100644 index 0000000..169a1e5 --- /dev/null +++ b/src/rigraph/core/core/vector.pmt @@ -0,0 +1,2946 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2003-2012 Gabor Csardi + 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 /* memcpy & co. */ +#include +#include /* va_start & co */ +#include + +/** + * \ingroup vector + * \section about_igraph_vector_t_objects About \type igraph_vector_t objects + * + * 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. + * + * Vectors are used extensively in \a igraph, all + * functions which expect or return a list of numbers use + * igraph_vector_t to achieve this. + * + * 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. + * + * The elements in an \type igraph_vector_t + * object are indexed from zero, we follow the usual C convention + * here. + * + * 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. + */ + +/** + * \ingroup vector + * \section igraph_vector_constructors_and_destructors Constructors and + * Destructors + * + * \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. + * + * \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. + * + * + * 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(). + * + * Note that vectors created by \ref igraph_vector_view() are special, + * you mustn't call \ref igraph_vector_destroy() on these. + */ + +/** + * \ingroup vector + * \function igraph_vector_init + * \brief Initializes a vector object (constructor). + * + * + * 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(). + * + * + * 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; + IGRAPH_ASSERT(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. + * + * + * 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; + + IGRAPH_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. + * + * + * 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(&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(&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. + * + * + * 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, + double 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. + * + * + * 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. + * + * + * 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. + * + * + * 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) { + IGRAPH_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. + * + * + * \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. + * + * + * 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; + IGRAPH_ASSERT(v != NULL); + IGRAPH_ASSERT(v->stor_begin != NULL); + if (size <= actual_size) { + 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) { + IGRAPH_ASSERT(v != NULL); + IGRAPH_ASSERT(v->stor_begin != NULL); + return v->stor_begin == v->end; +} + +/** + * \ingroup vector + * \function igraph_vector_size + * \brief Returns 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) { + IGRAPH_ASSERT(v != NULL); + IGRAPH_ASSERT(v->stor_begin != NULL); + return v->end - v->stor_begin; +} + +/** + * \ingroup vector + * \function igraph_vector_clear + * \brief Removes all elements from a vector. + * + * + * 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) { + IGRAPH_ASSERT(v != NULL); + IGRAPH_ASSERT(v->stor_begin != NULL); + v->end = v->stor_begin; +} + +/** + * \ingroup vector + * \function igraph_vector_push_back + * \brief Appends one element to a vector. + * + * + * 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) { + IGRAPH_ASSERT(v != NULL); + IGRAPH_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); + if (pos < 0) { + return IGRAPH_EINVAL; + } + IGRAPH_CHECK(FUNCTION(igraph_vector, resize)(v, (long) size + 1)); + if (((unsigned long)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 + * + * 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. + * + * \ref igraph_vector_tail() returns the last element of a non-empty + * vector. There is no igraph_vector_head() function + * however, as it is easy to write VECTOR(v)[0] + * instead. + */ + +/** + * \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) { + IGRAPH_ASSERT(v != NULL); + IGRAPH_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) { + IGRAPH_ASSERT(v != NULL); + IGRAPH_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) { + IGRAPH_ASSERT(v != NULL); + IGRAPH_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. + * + * + * 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) { + IGRAPH_ASSERT(v != NULL); + IGRAPH_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; + IGRAPH_ASSERT(v != NULL); + IGRAPH_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. + * + * + * 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) { + IGRAPH_ASSERT(v != NULL); + IGRAPH_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. + * + * + * 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; + IGRAPH_ASSERT(v != NULL); + IGRAPH_ASSERT(v->stor_begin != NULL); + IGRAPH_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(). + */ + +static 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_reverse_sort_cmp + * \brief Internal comparison function of vector elements, used by + * \ref igraph_vector_reverse_sort(). + */ + +static int FUNCTION(igraph_vector, reverse_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. + * + * + * If the vector contains any NaN values, the resulting ordering of + * NaN values is undefined and may appear anywhere in the vector. + * \param v Pointer to an initialized vector object. + * + * Time complexity: + * O(n log n) for n elements. + */ + +void FUNCTION(igraph_vector, sort)(TYPE(igraph_vector) *v) { + IGRAPH_ASSERT(v != NULL); + IGRAPH_ASSERT(v->stor_begin != NULL); + igraph_qsort(v->stor_begin, (size_t) FUNCTION(igraph_vector, size)(v), + sizeof(BASE), FUNCTION(igraph_vector, sort_cmp)); +} + +/** + * \ingroup vector + * \function igraph_vector_reverse_sort + * \brief Sorts the elements of the vector into descending order. + * + * + * If the vector contains any NaN values, the resulting ordering of + * NaN values is undefined and may appear anywhere in the vector. + * \param v Pointer to an initialized vector object. + * + * Time complexity: + * O(n log n) for n elements. + */ + +void FUNCTION(igraph_vector, reverse_sort)(TYPE(igraph_vector) *v) { + IGRAPH_ASSERT(v != NULL); + IGRAPH_ASSERT(v->stor_begin != NULL); + igraph_qsort(v->stor_begin, (size_t) FUNCTION(igraph_vector, size)(v), + sizeof(BASE), FUNCTION(igraph_vector, reverse_sort_cmp)); +} + +/** + * Ascending comparison function passed to qsort from igraph_vector_qsort_ind + */ +static 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 + */ +static 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. If the vector contains any NaN values, the ordering of + * NaN values is undefined. + * + * \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 igraph's built-in 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. + */ + +long int FUNCTION(igraph_vector, qsort_ind)(TYPE(igraph_vector) *v, + igraph_vector_t *inds, igraph_bool_t descending) { + unsigned 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; +} + +/** + * \function igraph_vector_lex_cmp + * \brief Lexicographical comparison of two vectors. + * + * + * If the elements of two vectors match but one is shorter, the shorter + * one comes first. Thus {1, 3} comes after {1, 2, 3}, but before {1, 3, 4}. + * + * + * This function is typically used together with \ref igraph_vector_ptr_sort(). + * + * \param lhs Pointer to a pointer to the first vector (interpreted as an igraph_vector_t **). + * \param rhs Pointer to a pointer to the second vector (interpreted as an igraph_vector_t **). + * \return -1 if \p lhs is lexicographically smaller, + * 0 if \p lhs and \p rhs are equal, else 1. + * \sa \ref igraph_vector_colex_cmp() to compare vectors starting from + * the last element. + * + * Time complexity: O(n), the number of elements in the smaller vector. + * + * \example examples/simple/igraph_vector_ptr_sort.c + */ +int FUNCTION(igraph_vector, lex_cmp)(const void *lhs, const void *rhs) { + const TYPE(igraph_vector) *a = * (TYPE(igraph_vector) **) lhs; + const TYPE(igraph_vector) *b = * (TYPE(igraph_vector) **) rhs; + long int i, sa, sb; + + sa = FUNCTION(igraph_vector, size)(a); + sb = FUNCTION(igraph_vector, size)(b); + + for (i = 0; i < sa; i++) { + if (i >= sb) { + /* b is shorter, and equal to the first part of a */ + return 1; + } + if (VECTOR(*a)[i] < VECTOR(*b)[i]) { + return -1; + } + if (VECTOR(*a)[i] > VECTOR(*b)[i]) { + return 1; + } + } + if (i == sb) { + return 0; + } + /* a is shorter, and equal to the first part of b */ + return -1; +} + +/** + * \function igraph_vector_colex_cmp + * \brief Colexicographical comparison of two vectors. + * + * + * This comparison starts from the last element of both vectors and + * moves backward. If the elements of two vectors match but one is + * shorter, the shorter one comes first. Thus {1, 2} comes after {3, 2, 1}, + * but before {0, 1, 2}. + * + * + * This function is typically used together with \ref igraph_vector_ptr_sort(). + * + * \param lhs Pointer to a pointer to the first vector (interpreted as an igraph_vector_t **). + * \param rhs Pointer to a pointer to the second vector (interpreted as an igraph_vector_t **). + * \return -1 if \p lhs in reverse order is + * lexicographically smaller than the reverse of \p rhs, + * 0 if \p lhs and \p rhs are equal, else 1. + * \sa \ref igraph_vector_lex_cmp() to compare vectors starting from + * the first element. + * + * Time complexity: O(n), the number of elements in the smaller vector. + * + * \example examples/simple/igraph_vector_ptr_sort.c + */ +int FUNCTION(igraph_vector, colex_cmp)(const void *lhs, const void *rhs) { + const TYPE(igraph_vector) *a = * (TYPE(igraph_vector) **) lhs; + const TYPE(igraph_vector) *b = * (TYPE(igraph_vector) **) rhs; + long int i, sa, sb, rai, rbi; + + sa = FUNCTION(igraph_vector, size)(a); + sb = FUNCTION(igraph_vector, size)(b); + + for (i = 0; i < sa; i++) { + if (i >= sb) { + /* b is shorter, and equal to the last part of a */ + return 1; + } + /* use reversed indexes */ + rai = sa - i - 1; + rbi = sb - i - 1; + if (VECTOR(*a)[rai] < VECTOR(*b)[rbi]) { + return -1; + } + if (VECTOR(*a)[rai] > VECTOR(*b)[rbi]) { + return 1; + } + } + if (i == sb) { + return 0; + } + /* a is shorter, and equal to the last part of b */ + return -1; +} +#endif /*NOTORDERED*/ + +/** + * \ingroup vector + * \function igraph_vector_resize + * \brief Resize the vector. + * + * + * 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) { + IGRAPH_ASSERT(v != NULL); + IGRAPH_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. + * + * + * 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 + +/* We will use x != x for NaN checks below and Clang does not like it unless + * we disable a warning */ + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wtautological-compare" +#endif + +/** + * \ingroup vector + * \function igraph_vector_max + * \brief Largest element of a vector. + * + * + * If the size of the vector is zero, an arbitrary number is + * returned. + * \param v The vector object. + * \return The maximum element of \p v, or NaN if any element is NaN. + * + * Time complexity: O(n), the number of elements. + */ +BASE FUNCTION(igraph_vector, max)(const TYPE(igraph_vector)* v) { + BASE max; + BASE *ptr; + IGRAPH_ASSERT(v != NULL); + IGRAPH_ASSERT(v->stor_begin != NULL); + IGRAPH_ASSERT(v->stor_begin != v->end); + max = *(v->stor_begin); +#if defined(BASE_IGRAPH_REAL) || defined(BASE_FLOAT) + if (igraph_is_nan(max)) { return max; }; /* Result is NaN */ +#endif + ptr = v->stor_begin + 1; + while (ptr < v->end) { + if ((*ptr) > max) { + max = *ptr; + } +#if defined(BASE_IGRAPH_REAL) || defined(BASE_FLOAT) + else if (igraph_is_nan(*ptr)) + return *ptr; /* Result is NaN */ +#endif + ptr++; + } + return max; +} + + +/** + * \ingroup vector + * \function igraph_vector_which_max + * \brief Gives the index of the maximum element of the vector. + * + * + * If the size of the vector is zero, -1 is returned. If the largest + * element is not unique, then the index of the first is returned. + * If the vector contains NaN values, the index of the first NaN value + * is returned. + * \param v The vector object. + * \return The index 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) { + if (!FUNCTION(igraph_vector, empty)(v)) { + BASE *max; + BASE *ptr; + IGRAPH_ASSERT(v != NULL); + IGRAPH_ASSERT(v->stor_begin != NULL); + IGRAPH_ASSERT(v->stor_begin != v->end); + max = ptr = v->stor_begin; +#if defined(BASE_IGRAPH_REAL) || defined(BASE_FLOAT) + if (igraph_is_nan(*ptr)) { return ptr - v->stor_begin; } /* Result is NaN */ +#endif + ptr++; + while (ptr < v->end) { + if (*ptr > *max) { + max = ptr; + } +#if defined(BASE_IGRAPH_REAL) || defined(BASE_FLOAT) + else if (igraph_is_nan(*ptr)) { + return ptr - v->stor_begin; /* Result is NaN */ + } +#endif + ptr++; + } + return max - v->stor_begin; + } + return -1; +} + +/** + * \ingroup vector + * \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, or NaN if any element is NaN. + * + * Time complexity: O(n), the number of elements. + */ + +BASE FUNCTION(igraph_vector, min)(const TYPE(igraph_vector)* v) { + BASE min; + BASE *ptr; + IGRAPH_ASSERT(v != NULL); + IGRAPH_ASSERT(v->stor_begin != NULL); + IGRAPH_ASSERT(v->stor_begin != v->end); + min = *(v->stor_begin); +#if defined(BASE_IGRAPH_REAL) || defined(BASE_FLOAT) + if (igraph_is_nan(min)) { return min; }; /* Result is NaN */ +#endif + ptr = v->stor_begin + 1; + while (ptr < v->end) { + if ((*ptr) < min) { + min = *ptr; + } +#if defined(BASE_IGRAPH_REAL) || defined(BASE_FLOAT) + else if (igraph_is_nan(*ptr)) { + return *ptr; /* Result is NaN */ + } +#endif + ptr++; + } + return min; +} + +/** + * \ingroup vector + * \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. If the vector contains NaN + * values, the index of the first NaN value 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) { + if (!FUNCTION(igraph_vector, empty)(v)) { + BASE *min; + BASE *ptr; + IGRAPH_ASSERT(v != NULL); + IGRAPH_ASSERT(v->stor_begin != NULL); + IGRAPH_ASSERT(v->stor_begin != v->end); + min = ptr = v->stor_begin; +#if defined(BASE_IGRAPH_REAL) || defined(BASE_FLOAT) + if (igraph_is_nan(*ptr)) { return ptr - v->stor_begin; } /* Result is NaN */ +#endif + ptr++; + while (ptr < v->end) { + if (*ptr < *min) { + min = ptr; + } +#if defined(BASE_IGRAPH_REAL) || defined(BASE_FLOAT) + else if (igraph_is_nan(*ptr)) { + return ptr - v->stor_begin; /* Result is NaN */ + } +#endif + ptr++; + } + return min - v->stor_begin; + } + return -1; +} + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +#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. + * + * + * 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) { + + IGRAPH_ASSERT(v != NULL); + IGRAPH_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). + * + * + * 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) { + long int from_size; + + IGRAPH_ASSERT(from != NULL); + IGRAPH_ASSERT(from->stor_begin != NULL); + + from_size = FUNCTION(igraph_vector, size)(from); + to->stor_begin = IGRAPH_CALLOC(from_size, 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. + * + * + * 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; + IGRAPH_ASSERT(v != NULL); + IGRAPH_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; + IGRAPH_ASSERT(v != NULL); + IGRAPH_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. + * + * + * 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; + IGRAPH_ASSERT(v != NULL); + IGRAPH_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. + * + * + * \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; + + IGRAPH_ASSERT(from != NULL); + IGRAPH_ASSERT(from->stor_begin != NULL); + IGRAPH_ASSERT(to != NULL); + IGRAPH_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. + * + * + * 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. + * + * + * 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) { + IGRAPH_ASSERT(v != NULL); + IGRAPH_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) { + IGRAPH_ASSERT(v != NULL); + IGRAPH_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. + * + * + * 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) { + IGRAPH_ASSERT(v != NULL); + IGRAPH_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) { + IGRAPH_ASSERT(v != NULL); + IGRAPH_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; + IGRAPH_ASSERT(v != NULL); + IGRAPH_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. If any element is NaN, it will + * return \c 0 (=false). + * + * 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; + IGRAPH_ASSERT(v != NULL); + IGRAPH_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; + IGRAPH_ASSERT(v != NULL); + IGRAPH_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; + IGRAPH_ASSERT(lhs != 0); + IGRAPH_ASSERT(rhs != 0); + IGRAPH_ASSERT(lhs->stor_begin != 0); + IGRAPH_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. If any element + * is NaN, it will return \c 0 (=false). + * + * 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; + IGRAPH_ASSERT(lhs != 0); + IGRAPH_ASSERT(rhs != 0); + IGRAPH_ASSERT(lhs->stor_begin != 0); + IGRAPH_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. If any element + * is NaN, it will return \c 0 (=false). + * + * 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; + IGRAPH_ASSERT(lhs != 0); + IGRAPH_ASSERT(rhs != 0); + IGRAPH_ASSERT(lhs->stor_begin != 0); + IGRAPH_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. If any element is NaN, it will return \c 0 (=false). + * + * 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; + IGRAPH_ASSERT(lhs != 0); + IGRAPH_ASSERT(rhs != 0); + IGRAPH_ASSERT(lhs->stor_begin != 0); + IGRAPH_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. If any element is NaN, it will return \c 0 (=false). + * + * 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; + IGRAPH_ASSERT(lhs != 0); + IGRAPH_ASSERT(rhs != 0); + IGRAPH_ASSERT(lhs->stor_begin != 0); + IGRAPH_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 + + +#ifndef NOTORDERED + +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); + +/** + * \ingroup vector + * \function igraph_vector_binsearch + * \brief Finds an element by binary searching a sorted vector. + * + * + * 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. If the vector contains any NaN values, the returned + * value is undefined and \p pos may point to any position. + * \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)); +} + +/** + * \ingroup vector + * \function igraph_vector_binsearch_slice + * \brief Finds an element by binary searching a sorted slice of a vector. + * + * + + * It is assumed that the indicated slice of the vector, from \p start to \p end, + * is sorted. If the specified element (\p what) is not in the slice of the + * vector, then the position of where it should be inserted (to keep the vector + * sorted) is returned. If the indicated slice contains any NaN values, the + * returned value is undefined and \c pos may point to any position within + * the slice. + * \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 slice of 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). + * \param start The start position of the slice to search (inclusive). + * \param end The end position of the slice to search (exclusive). + * \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 the slice of \p v, i.e. \p end - \p start. + */ + +igraph_bool_t FUNCTION(igraph_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; + + if (left < 0) + IGRAPH_ERROR("Invalid start position.", IGRAPH_EINVAL); + + if (right >= FUNCTION(igraph_vector, size)(v)) + IGRAPH_ERROR("Invalid end position.", IGRAPH_EINVAL); + + if (left > right) + IGRAPH_ERROR("Invalid slice, start position must be smaller than end position.", + IGRAPH_EINVAL); + + return FUNCTION(igraph_i_vector, binsearch_slice)(v, what, pos, start, end); +} + +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. + * + * + * 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. + * + * 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 when \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. If + * any value is NaN in the shorter vector, the result will be NaN. + * \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; + } + #if defined(BASE_IGRAPH_REAL) || defined(BASE_FLOAT) + else if (igraph_is_nan(d)) { /* Result is NaN */ + return d; + }; + #endif + } + + 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 + * as that of \p from. The vector \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); + IGRAPH_CHECK(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. + * + * \param v1 The first vector. + * \param v2 The second vector. + * \return Error code. + * + * Time complexity: O(1). + */ + +int FUNCTION(igraph_vector, swap)(TYPE(igraph_vector) *v1, TYPE(igraph_vector) *v2) { + + TYPE(igraph_vector) tmp; + + tmp = *v1; + *v1 = *v2; + *v2 = tmp; + + return IGRAPH_SUCCESS; +} + +/** + * \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 (may be the same as the + * first one). + * \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 + * + * + * The Fisher-Yates shuffle ensures that every permutation 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. + * + * + * References: + * \clist + * \cli (Fisher & 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 */ + IGRAPH_UNUSED(v); +#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 be non-empty. + * If a vector contains at least one NaN, both \c min and \c max will be NaN. + * \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) { + BASE* ptr; + IGRAPH_ASSERT(v != NULL); + IGRAPH_ASSERT(v->stor_begin != NULL); + IGRAPH_ASSERT(v->stor_begin != v->end); + *min = *max = *(v->stor_begin); + #if defined(BASE_IGRAPH_REAL) || defined(BASE_FLOAT) + if (igraph_is_nan(*min)) { return IGRAPH_SUCCESS; }; /* Result is NaN */ + #endif + ptr = v->stor_begin + 1; + while (ptr < v->end) { + if (*ptr > *max) { + *max = *ptr; + } else if (*ptr < *min) { + *min = *ptr; + } + #if defined(BASE_IGRAPH_REAL) || defined(BASE_FLOAT) + else if (igraph_is_nan(*ptr)) { /* Result is NaN */ + *min = *max = *ptr; + return IGRAPH_SUCCESS; + }; + #endif + ptr++; + } + return IGRAPH_SUCCESS; +} + +/** + * \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 be + * non-empty. If the minimum or maximum is not unique, the index + * of the first minimum or the first maximum is returned, respectively. + * If a vector contains at least one NaN, both \c which_min and \c which_max + * will point to the first NaN value. + * \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) { + BASE *min, *max; + BASE *ptr; + IGRAPH_ASSERT(v != NULL); + IGRAPH_ASSERT(v->stor_begin != NULL); + IGRAPH_ASSERT(v->stor_begin != v->end); + ptr = v->stor_begin; + min = max = ptr; + #if defined(BASE_IGRAPH_REAL) || defined(BASE_FLOAT) + if (igraph_is_nan(*ptr)) { /* Result is NaN */ + *which_min = *which_max = 0; + return IGRAPH_SUCCESS; + } + #endif + while (ptr < v->end) { + if (*ptr > *max) { + max = ptr; + } else if (*ptr < *min) { + min = ptr; + } +#if defined(BASE_IGRAPH_REAL) || defined(BASE_FLOAT) + else if (igraph_is_nan(*ptr)) { /* Result is NaN */ + *which_min = *which_max = ptr - v->stor_begin; + return IGRAPH_SUCCESS; + } +#endif + ptr++; + } + *which_min = min - v->stor_begin; + *which_max = max - v->stor_begin; + return IGRAPH_SUCCESS; +} + +#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. + * + * + * 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. + * + * + * 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. + * + * + * 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/src/rigraph/core/core/vector_ptr.c b/src/rigraph/core/core/vector_ptr.c new file mode 100644 index 0000000..d3358bc --- /dev/null +++ b/src/rigraph/core/core/vector_ptr.c @@ -0,0 +1,639 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2003-2012 Gabor Csardi + 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_error.h" +#include "igraph_qsort.h" + +#include /* memcpy & co. */ +#include + +/** + * \section about_igraph_vector_ptr_objects Pointer vectors + * (igraph_vector_ptr_t) + * + * The \type igraph_vector_ptr_t data type is very similar to + * the \ref igraph_vector_t type, but it stores generic pointers instead of + * real numbers. + * + * This type has the same space complexity as \ref + * igraph_vector_t, and most implemented operations work the same way + * as for \ref igraph_vector_t. + * + * 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. + * + * 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. + * + * 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(). + */ + + +/** + * \ingroup vectorptr + * \function igraph_vector_ptr_init + * \brief Initialize a pointer vector (constructor). + * + * + * 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 + * igraph_vector_ptr_t 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; + IGRAPH_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. + * + * + * 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) { + IGRAPH_ASSERT(v != 0); + if (v->stor_begin != 0) { + IGRAPH_FREE(v->stor_begin); + v->stor_begin = NULL; + } +} + +static 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 \ref igraph_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; + IGRAPH_ASSERT(v != 0); + IGRAPH_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) { + IGRAPH_ASSERT(v != 0); + IGRAPH_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: + * - IGRAPH_ENOMEM: 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; + IGRAPH_ASSERT(v != NULL); + IGRAPH_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) { + IGRAPH_ASSERT(v != NULL); + IGRAPH_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, i.e. the number of pointers stored. + * + * Time complexity: O(1). + */ + +long int igraph_vector_ptr_size(const igraph_vector_ptr_t* v) { + IGRAPH_ASSERT(v != NULL); + /* IGRAPH_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. + * + * + * This function resizes a pointer to vector to zero length. Note that + * the pointed objects are \em not deallocated, you should call + * \ref igraph_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. + * + * + * 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) { + IGRAPH_ASSERT(v != NULL); + IGRAPH_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) { + IGRAPH_ASSERT(v != NULL); + IGRAPH_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) { + IGRAPH_ASSERT(v != NULL); + IGRAPH_ASSERT(v->stor_begin != NULL); + IGRAPH_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) { + IGRAPH_ASSERT(v != NULL); + IGRAPH_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) { + IGRAPH_ASSERT(v != NULL); + IGRAPH_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) { + IGRAPH_ASSERT(v != NULL); + IGRAPH_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. + * + * + * 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) { + IGRAPH_ASSERT(v != NULL); + IGRAPH_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). + * + * + * This function creates a pointer vector by copying another one. This + * is shallow copy, only the pointers in the vector will be copied. + * + * + * 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) { + long int from_size; + + IGRAPH_ASSERT(from != NULL); + /* IGRAPH_ASSERT(from->stor_begin != NULL); */ /* TODO */ + + from_size = igraph_vector_ptr_size(from); + + to->stor_begin = IGRAPH_CALLOC(from_size, 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) { + IGRAPH_ASSERT(v != NULL); + IGRAPH_ASSERT(v->stor_begin != NULL); + if (pos + 1 < igraph_vector_ptr_size(v)) { /* No need to move data when removing the last element. */ + 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 + * \function igraph_vector_ptr_sort + * \brief Sorts 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 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. + * + * \param v The pointer vector to be sorted. + * \param compar A qsort-compatible comparison function. It must take pointers to the + * elements of the pointer vector. For example, if the pointer vector contains + * igraph_vector_t * pointers, then the comparison function must + * interpret its arguments as igraph_vector_t **. + * + * \example examples/simple/igraph_vector_ptr_sort.c + */ +void igraph_vector_ptr_sort(igraph_vector_ptr_t *v, int (*compar)(const void*, const void*)) { + igraph_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) { + IGRAPH_ASSERT(v != 0); + return v->item_destructor; +} diff --git a/src/rigraph/core/f2c.h b/src/rigraph/core/f2c.h new file mode 100644 index 0000000..b8ec2e2 --- /dev/null +++ b/src/rigraph/core/f2c.h @@ -0,0 +1,238 @@ +/* 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 "linalg/blas_internal.h" +#include "linalg/lapack_internal.h" +#include "linalg/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) +#ifndef min +#define min(a,b) ((a) <= (b) ? (a) : (b)) +#endif +#ifndef max +#define max(a,b) ((a) >= (b) ? (a) : (b)) +#endif +#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/src/rigraph/core/flow/flow.c b/src/rigraph/core/flow/flow.c new file mode 100644 index 0000000..ff82d89 --- /dev/null +++ b/src/rigraph/core/flow/flow.c @@ -0,0 +1,2544 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2006-2012 Gabor Csardi + 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_adjlist.h" +#include "igraph_components.h" +#include "igraph_conversion.h" +#include "igraph_constants.h" +#include "igraph_constructors.h" +#include "igraph_dqueue.h" +#include "igraph_error.h" +#include "igraph_interface.h" +#include "igraph_memory.h" +#include "igraph_progress.h" +#include "igraph_operators.h" +#include "igraph_structural.h" +#include "igraph_topology.h" + +#include "core/buckets.h" +#include "core/cutheap.h" +#include "core/interruption.h" +#include "core/math.h" + +#include "config.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. + */ + +static 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), ¤t, &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, ¤t, &to, &excess, \ + &rescap, &rev)) + +static 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) { + + IGRAPH_UNUSED(buckets); + + 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; + } + } +} + +static 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; + } +} + +static 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_UNUSED(current); + IGRAPH_UNUSED(source); + + 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; +} + +static 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); +} + +static 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_UNUSED(source); + + 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 + * + * 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. + * + * 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 (i.e. the sum of the flow on the incoming edges) is + * the same as the outgoing flow (i.e. 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(¤t, 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, &Q); + + 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(¤t); + 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(¤t); + 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 + * + * 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. + * + * 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 (i.e. the sum of the flow on the incoming edges) is + * the same as the outgoing flow (i.e. 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. + * + * 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(). + * + * 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 + * + * 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. + * + * 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. + * + * 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= 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; +} + +static 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. + * + * 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. + * + * + * 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; +} + + +static 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 + * + * 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. + * + * 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; +} + +static 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); + } + 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 */ + + 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; +} + +static 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); + } + 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 + * + * 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. + * + * The vertex connectivity of a pair is the same as the number + * of different (i.e. node-independent) paths from source to + * target. + * + * 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 calculate the number of vertices needed + * to eliminate all paths except for the trivial (direct) paths + * between \p source and \p vertex. TODO: 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; +} + +static 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 = 0; + + 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; +} + +static 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)) */ +static 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(°ree, 0); + if (!igraph_is_directed(graph)) { + IGRAPH_CHECK(igraph_degree(graph, °ree, igraph_vss_all(), + IGRAPH_OUT, IGRAPH_LOOPS)); + if (igraph_vector_min(°ree) == 1) { + *res = 1; + *found = 1; + } + } else { + /* directed, check both in- & out-degree */ + IGRAPH_CHECK(igraph_degree(graph, °ree, igraph_vss_all(), + IGRAPH_OUT, IGRAPH_LOOPS)); + if (igraph_vector_min(°ree) == 1) { + *res = 1; + *found = 1; + } else { + IGRAPH_CHECK(igraph_degree(graph, °ree, igraph_vss_all(), + IGRAPH_IN, IGRAPH_LOOPS)); + if (igraph_vector_min(°ree) == 1) { + *res = 1; + *found = 1; + } + } + } + igraph_vector_destroy(°ree); + IGRAPH_FINALLY_CLEAN(1); + } + return 0; +} + +/** + * \function igraph_vertex_connectivity + * The vertex connectivity of a graph + * + * The vertex connectivity of a graph is the minimum + * vertex connectivity along each pairs of vertices in the graph. + * + * 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 + * + * 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. + * + * 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. + * + * This is the minimum of the edge connectivity over all + * pairs of vertices in the graph. + * + * + * 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. + * + * 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. + * + * 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. + * + * 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. + * + * 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_CHECK(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. + * + * 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. + * + * This quantity was 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 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. + * + * + * 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. + * + * This implementation uses Gusfield's algorithm to construct the + * Gomory-Hu tree. See the following paper for more details: + * + * + * 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 */ + 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 so we need to iterate over &partition to find + * all the nodes that are of interest to us */ + n = igraph_vector_size(&partition); + for (i = 0; i < n; i++) { + mid = VECTOR(partition)[i]; + if (mid != source) { + if (VECTOR(neighbors)[(long int)mid] == target) { + VECTOR(neighbors)[(long int)mid] = source; + } else if (VECTOR(neighbors)[(long int)target] == mid) { + VECTOR(neighbors)[(long int)target] = source; + VECTOR(neighbors)[(long int)source] = mid; + VECTOR(flow_values)[(long int)source] = VECTOR(flow_values)[(long int)target]; + VECTOR(flow_values)[(long int)target] = flow_value; + } + } + } + } + + 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/src/rigraph/core/flow/flow_internal.h b/src/rigraph/core/flow/flow_internal.h new file mode 100644 index 0000000..ccb4784 --- /dev/null +++ b/src/rigraph/core/flow/flow_internal.h @@ -0,0 +1,41 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2021 The igraph development 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., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#ifndef IGRAPH_FLOW_INTERNAL_H +#define IGRAPH_FLOW_INTERNAL_H + +#include "igraph_types.h" + +__BEGIN_DECLS + +IGRAPH_PRIVATE_EXPORT 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); + +__END_DECLS + +#endif diff --git a/src/rigraph/core/flow/st-cuts.c b/src/rigraph/core/flow/st-cuts.c new file mode 100644 index 0000000..1fa7a0a --- /dev/null +++ b/src/rigraph/core/flow/st-cuts.c @@ -0,0 +1,1592 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2010-2012 Gabor Csardi + 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_adjlist.h" +#include "igraph_constants.h" +#include "igraph_constructors.h" +#include "igraph_components.h" +#include "igraph_error.h" +#include "igraph_interface.h" +#include "igraph_memory.h" +#include "igraph_operators.h" +#include "igraph_stack.h" +#include "igraph_visitor.h" + +#include "core/math.h" +#include "core/estack.h" +#include "core/marked_queue.h" +#include "graph/attributes.h" +#include "flow/flow_internal.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); + +/** + * \function igraph_even_tarjan_reduction + * Even-Tarjan reduction of a graph + * + * A digraph is created with twice as many vertices and edges. For each + * original vertex i, two vertices i'= i and i'' = i' + n are created, + * with a directed edge from i' to i''. For each original directed edge + * from i to j, two new edges are created, from i' to j'' and from i'' + * to j'. + * + * This reduction is used in the paper (observation 2): + * Arkady Kanevsky: Finding all minimum-size separating vertex sets in + * a graph, Networks 23, 533--541, 1993. + * + * The original paper where this reduction was conceived is + * Shimon Even and R. Endre Tarjan: Network Flow and Testing Graph + * Connectivity, SIAM J. Comput., 4(4), 507–518. + * + * \param graph A graph. Although directness is not checked, this function + * is commonly used only on directed graphs. + * \param graphbar Pointer to a new directed graph that will contain the + * reduction, with twice as many vertices and edges. + * \param capacity Pointer to an initialized vector or a null pointer. If + * not a null pointer, then it will be filled the capacity from + * the reduction: the first |E| elements are 1, the remaining |E| + * are equal to |V| (which is used to mean infinity). + * \return Error code. + * + * Time complexity: O(|E|+|V|). + * + * \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; +} + +static 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++) { + igraph_real_t c = VECTOR(*capacity)[i] - VECTOR(*flow)[i]; + if (c > 0) { + long int from = IGRAPH_FROM(graph, i); + long int to = IGRAPH_TO(graph, 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; +} + +static 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; + +static 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; +} + +static void igraph_i_dbucket_destroy(igraph_i_dbucket_t *buck) { + igraph_vector_long_destroy(&buck->head); + igraph_vector_long_destroy(&buck->next); +} + +static 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; +} + +static long int igraph_i_dbucket_empty(const igraph_i_dbucket_t *buck, + long int bid) { + return VECTOR(buck->head)[bid] == 0; +} + +static 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; +} + +static 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 */ + +static 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; +} + +static 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. + * + * 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, 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_LOOPS_ONCE, IGRAPH_MULTIPLE)); + IGRAPH_FINALLY(igraph_adjlist_destroy, &succ); + IGRAPH_CHECK(igraph_adjlist_init(graph, &pred, invmode, IGRAPH_LOOPS_ONCE, IGRAPH_MULTIPLE)); + 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; + +static 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; +} + +static 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; +} + +static 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; +} + +/* not 'static' because used in igraph_all_st_cuts.c test program */ +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 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 u = (long int) VECTOR(Isv_min)[j]; + if (igraph_estack_iselement(T, u) || u == 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. + */ + +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. +*/ + +static 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(2); + + return 0; +} + +typedef struct igraph_i_all_st_mincuts_data_t { + const igraph_vector_bool_t *active; +} igraph_i_all_st_mincuts_data_t; + +static 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 edge cuts between two vertices, in a directed graph, + * with minimum total capacity. Possibly, multiple cuts may have the same total + * capacity, although there is often only one minimum cut in weighted graphs. + * It is recommended to supply integer-values capacities. Otherwise, not all + * minimum cuts may be detected because of numerical roundoff errors. + * 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. All capacities must be + * strictly positive. 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 (capacity != NULL && igraph_vector_min(capacity) <= 0) + { + IGRAPH_ERROR("Not all capacities are strictly positive.", 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/src/rigraph/core/games/barabasi.c b/src/rigraph/core/games/barabasi.c new file mode 100644 index 0000000..63eedbe --- /dev/null +++ b/src/rigraph/core/games/barabasi.c @@ -0,0 +1,764 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2003-2021 The igraph development 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., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_games.h" + +#include "igraph_conversion.h" +#include "igraph_constructors.h" +#include "igraph_interface.h" +#include "igraph_memory.h" +#include "igraph_psumtree.h" +#include "igraph_random.h" + +#include "core/interruption.h" + +static 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); + +static 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); + +static 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); + +static 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(igraph_free, bag); + + /* 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(°, sn); + IGRAPH_CHECK(igraph_degree(start_from, °, 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(°); + 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++) { + + IGRAPH_ALLOW_INTERRUPTION(); + + /* 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; +} + +static 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(°ree, 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, °ree, igraph_vss_all(), mm, + IGRAPH_LOOPS)); + IGRAPH_CHECK(igraph_vector_resize(°ree, no_of_nodes)); + for (ii = 0; ii < sn; ii++) { + IGRAPH_CHECK(igraph_psumtree_update(&sumtree, ii, pow(VECTOR(degree)[ii], power) + A)); + } + } else { + IGRAPH_CHECK(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; + + IGRAPH_ALLOW_INTERRUPTION(); + + 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_CHECK(igraph_psumtree_update(&sumtree, nn, pow(VECTOR(degree)[nn], power) + A)); + } + if (outpref) { + VECTOR(degree)[i] += no_of_neighbors; + IGRAPH_CHECK(igraph_psumtree_update(&sumtree, i, pow(VECTOR(degree)[i], power) + A)); + } else { + IGRAPH_CHECK(igraph_psumtree_update(&sumtree, i, A)); + } + } + + RNG_END(); + + igraph_psumtree_destroy(&sumtree); + igraph_vector_destroy(°ree); + IGRAPH_FINALLY_CLEAN(2); + + IGRAPH_CHECK(igraph_create(graph, &edges, n, directed)); + igraph_vector_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + + return 0; +} + +static 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(°ree, 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, °ree, igraph_vss_all(), mm, + IGRAPH_LOOPS)); + IGRAPH_CHECK(igraph_vector_resize(°ree, no_of_nodes)); + for (ii = 0; ii < sn; ii++) { + IGRAPH_CHECK(igraph_psumtree_update(&sumtree, ii, pow(VECTOR(degree)[ii], power) + A)); + } + } else { + IGRAPH_CHECK(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; + + IGRAPH_ALLOW_INTERRUPTION(); + + 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_CHECK(igraph_vector_push_back(&edges, i)); + IGRAPH_CHECK(igraph_vector_push_back(&edges, to)); + edgeptr += 2; + IGRAPH_CHECK(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_CHECK(igraph_vector_push_back(&edges, i)); + IGRAPH_CHECK(igraph_vector_push_back(&edges, to)); + edgeptr += 2; + IGRAPH_CHECK(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_CHECK(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_CHECK(igraph_psumtree_update(&sumtree, i, pow(VECTOR(degree)[i], power) + A)); + } else { + IGRAPH_CHECK(igraph_psumtree_update(&sumtree, i, A)); + } + } + + RNG_END(); + + igraph_psumtree_destroy(&sumtree); + igraph_vector_destroy(°ree); + 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á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. I.e., 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("Number of edges added per step must not be negative.", 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; +} + +/** + * \function igraph_barabasi_aging_game + * \brief Preferential attachment with aging of vertices. + * + * + * This game starts with one vertex (if \p nodes > 0). In each step + * a new node is added, and it is connected to \p m existing nodes. + * Existing nodes to connect to are chosen with probability dependent + * on their (in-)degree (\c k) and age (\c l). + * The degree-dependent part is + * deg_coef * k^pa_exp + zero_deg_appeal, + * while the age-dependent part is + * age_coef * l^aging_exp + zero_age_appeal, + * which are summed to obtain the final weight. + * + * + * The age \c l is based on the number of vertices in the + * network and the \p aging_bins argument: the age of a node + * is incremented by 1 after each + * floor(nodes / aging_bins) + 1 + * time steps. + * + * \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. + * Ignored if \p outseq is a non-zero length vector. + * \param outseq The number of edges to add in each time step. If it + * is \c NULL 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_bins Integer constant, the number of age bins to use. + * \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_bins)*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_bins, + 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; + 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_ERRORF("Number of nodes must not be negative, got %ld.", IGRAPH_EINVAL, no_of_nodes); + } + if (outseq != 0 && igraph_vector_size(outseq) != 0 && igraph_vector_size(outseq) != no_of_nodes) { + IGRAPH_ERRORF("The length of the out-degree sequence (%ld) does not agree with the number of nodes (%ld).", + IGRAPH_EINVAL, + igraph_vector_size(outseq), no_of_nodes); + } + if ( (outseq == 0 || igraph_vector_size(outseq) == 0) && m < 0) { + IGRAPH_ERRORF("The number of edges per time step must not be negative, got %" IGRAPH_PRId ".", + IGRAPH_EINVAL, + m); + } + if (aging_bins <= 0) { + IGRAPH_ERRORF("Number of aging bins must be positive, got %" IGRAPH_PRId ".", + IGRAPH_EINVAL, + aging_bins); + } + if (deg_coef < 0) { + IGRAPH_ERRORF("Degree coefficient must be non-negative, got %g.", + IGRAPH_EINVAL, + deg_coef); + } + if (age_coef < 0) { + IGRAPH_ERRORF("Age coefficient must be non-negative, got %g.", + IGRAPH_EINVAL, + deg_coef); + } + + if (zero_deg_appeal < 0) { + IGRAPH_ERRORF("Zero degree appeal must be non-negative, got %g.", + IGRAPH_EINVAL, + zero_deg_appeal); + } + if (zero_age_appeal < 0) { + IGRAPH_ERRORF("Zero age appeal must be non-negative, got %g.", + IGRAPH_EINVAL, + zero_age_appeal); + } + + if (no_of_nodes == 0) { + return igraph_empty(graph, 0, directed); + } + + binwidth = no_of_nodes / aging_bins + 1; + + 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(°ree, no_of_nodes); + + RNG_BEGIN(); + + /* first node */ + IGRAPH_CHECK(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; + + IGRAPH_ALLOW_INTERRUPTION(); + + 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_CHECK(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_CHECK(igraph_psumtree_update( + &sumtree, i, + (zero_age_appeal + 1) * (deg_coef * pow(VECTOR(degree)[i], pa_exp) + zero_deg_appeal) + )); + } else { + IGRAPH_CHECK(igraph_psumtree_update( + &sumtree, i, (1 + zero_age_appeal) * zero_deg_appeal + )); + } + + /* aging */ + for (k = 1; binwidth * k <= i; 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_CHECK(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(°ree); + 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 IGRAPH_SUCCESS; +} diff --git a/src/rigraph/core/games/callaway_traits.c b/src/rigraph/core/games/callaway_traits.c new file mode 100644 index 0000000..3bdb897 --- /dev/null +++ b/src/rigraph/core/games/callaway_traits.c @@ -0,0 +1,200 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2003-2021 The igraph development 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., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_games.h" + +#include "igraph_constructors.h" +#include "igraph_memory.h" +#include "igraph_random.h" + +/** + * \function igraph_callaway_traits_game + * \brief Simulates a growing network with vertex types. + * + * + * The different types of vertices prefer to connect other types of + * vertices with a given probability. + * + * + * 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. + * + * + * References: + * + * + * D. S. Callaway, J. E. Hopcroft, J. M. Kleinberg, M. E. J. Newman, and S. H. Strogatz, + * Are randomly grown graphs really random? + * Phys. Rev. E 64, 041902 (2001). + * https://doi.org/10.1103/PhysRevE.64.041902 + * + * \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 connections tried in each time step. + * \param type_dist Vector giving the distribution of the vertex types. + * If \c NULL, the distribution is assumed to be uniform. + * \param pref_matrix Matrix giving the connection probabilities for + * the vertex types. + * \param directed Logical, whether to generate a directed graph. + * \param node_type_vec An initialized vector or \c NULL. + * If not \c NULL, the type of each node will be stored here. + * \return Error code. + * + * Added in version 0.2. + * + * Time complexity: O(|V|*k*log(|V|)), |V| is the number of vertices, + * k 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, + const igraph_vector_t *type_dist, + const igraph_matrix_t *pref_matrix, + igraph_bool_t directed, + igraph_vector_t *node_type_vec) { + long int i, j; + igraph_vector_t edges; + igraph_vector_t cumdist; + igraph_real_t maxcum; + igraph_vector_t *nodetypes; + + /* Argument contracts */ + if(nodes < 0){ + IGRAPH_ERROR("The number of vertices must be non-negative.", IGRAPH_EINVAL); + } + + if (types < 1) { + IGRAPH_ERROR("The number of vertex types must be at least 1.", IGRAPH_EINVAL); + } + + if (type_dist) { + igraph_real_t lo; + + if (igraph_vector_size(type_dist) != types) { + IGRAPH_ERROR("The vertex type distribution vector must agree in length with the number of types.", + IGRAPH_EINVAL); + } + + lo = igraph_vector_min(type_dist); + if (lo < 0) { + IGRAPH_ERROR("The vertex type distribution vector must not contain negative values.", IGRAPH_EINVAL); + } + if (igraph_is_nan(lo)) { + IGRAPH_ERROR("The vertex type distribution vector must not contain NaN.", IGRAPH_EINVAL); + } + } + + if (igraph_matrix_nrow(pref_matrix) != types || igraph_matrix_ncol(pref_matrix) != types) { + IGRAPH_ERROR("The preference matrix must be square and agree in dimensions with the number of types.", IGRAPH_EINVAL); + } + + { + igraph_real_t lo, hi; + igraph_matrix_minmax(pref_matrix, &lo, &hi); + + if (lo < 0 || hi > 1) { + IGRAPH_ERROR("The preference matrix must contain probabilities in [0, 1].", IGRAPH_EINVAL); + } + if (igraph_is_nan(lo) || igraph_is_nan(hi)) { + IGRAPH_ERROR("The preference matrix must not contain NaN.", IGRAPH_EINVAL); + } + } + + if (! directed && ! igraph_matrix_is_symmetric(pref_matrix)) { + IGRAPH_ERROR("The preference matrix must be symmetric when generating undirected graphs.", IGRAPH_EINVAL); + } + + IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); + IGRAPH_VECTOR_INIT_FINALLY(&cumdist, types + 1); + + if (type_dist) { + VECTOR(cumdist)[0] = 0; + for (i = 0; i < types; ++i) { + VECTOR(cumdist)[i + 1] = VECTOR(cumdist)[i] + VECTOR(*type_dist)[i]; + } + } else { + for (i = 0; i < types+1; ++i) { + VECTOR(cumdist)[i] = i; + } + } + maxcum = igraph_vector_tail(&cumdist); + + if (maxcum <= 0) { + IGRAPH_ERROR("The vertex type distribution vector must contain at least one positive value.", IGRAPH_EINVAL); + } + + if (node_type_vec) { + nodetypes = node_type_vec; + IGRAPH_CHECK(igraph_vector_resize(nodetypes, nodes)); + } else { + nodetypes = IGRAPH_CALLOC(1, igraph_vector_t); + if (! nodetypes) { + IGRAPH_ERROR("Insufficient memory for callaway_traits_game.", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, nodetypes); + IGRAPH_VECTOR_INIT_FINALLY(nodetypes, nodes); + } + + 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(); + + if (! node_type_vec) { + igraph_vector_destroy(nodetypes); + IGRAPH_FREE(nodetypes); + IGRAPH_FINALLY_CLEAN(2); + } + igraph_vector_destroy(&cumdist); + IGRAPH_FINALLY_CLEAN(1); + IGRAPH_CHECK(igraph_create(graph, &edges, nodes, directed)); + igraph_vector_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} diff --git a/src/rigraph/core/games/citations.c b/src/rigraph/core/games/citations.c new file mode 100644 index 0000000..6fab65e --- /dev/null +++ b/src/rigraph/core/games/citations.c @@ -0,0 +1,480 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2003-2021 The igraph development 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., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_games.h" + +#include "igraph_constructors.h" +#include "igraph_memory.h" +#include "igraph_psumtree.h" +#include "igraph_random.h" +#include "igraph_interface.h" + +typedef struct { + long int no; + igraph_psumtree_t *sumtrees; +} igraph_i_citing_cited_type_game_struct_t; + +static void igraph_i_citing_cited_type_game_free ( + igraph_i_citing_cited_type_game_struct_t *s); + +/** + * \function igraph_lastcit_game + * \brief Simulates a 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 agebins 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/agebins+1, + * in the last expression '/' denotes integer division, so the + * fraction part is omitted. + * + * + * The \p preference argument specifies the preferences for the + * citation lags, i.e. 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. + * + * + * 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 agebins The number of age bins to use. + * \param preference Pointer to an initialized vector of length + * \c agebins+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. + * Preferences cannot be negative. + * \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 agebins parameter. + */ +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) { + + 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 binwidth; + + if (agebins != igraph_vector_size(preference) - 1) { + IGRAPH_ERRORF("The `preference' vector should be of length `agebins' plus one." + "Number of agebins is %"IGRAPH_PRId", preference vector is of length %ld.", + IGRAPH_EINVAL, + agebins, igraph_vector_size(preference)); + } + if (nodes < 0 ) { + IGRAPH_ERRORF("Number of nodes should be non-negative, received %"IGRAPH_PRId".", + IGRAPH_EINVAL, + nodes); + } + if (agebins < 1 ) { + IGRAPH_ERRORF("Number of age bins should be at least 1, received %"IGRAPH_PRId".", + IGRAPH_EINVAL, + agebins); + } + if (VECTOR(*preference)[agebins] <= 0) { + IGRAPH_ERRORF("The last element of the `preference' vector needs to be positive, but is %g.", + IGRAPH_EINVAL, + VECTOR(*preference)[agebins]); + } + if (igraph_vector_min(preference) < 0) { + IGRAPH_ERRORF("The preference vector must contain only non-negative values, but found %g.", + IGRAPH_EINVAL, + igraph_vector_min(preference)); + } + + if (nodes == 0) { + IGRAPH_CHECK(igraph_empty(graph, nodes, directed)); + return IGRAPH_SUCCESS; + } + + binwidth = no_of_nodes / agebins + 1; + 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_CHECK(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_CHECK(igraph_psumtree_update(&sumtree, to, VECTOR(*preference)[0])); + } + + /* Add the node itself */ + IGRAPH_CHECK(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_CHECK(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 Simulates 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 citing edges are added. Nodes with + * different categories may have different probabilities to get + * cited, as given by the \p pref vector. + * + * + * 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, nnval; + long int i, j, type; + long int pref_len = igraph_vector_size(pref); + + if (igraph_vector_size(types) != nodes) { + IGRAPH_ERRORF("Length of types vector (%ld) must match number of nodes (%" IGRAPH_PRId ").", + IGRAPH_EINVAL, (long) igraph_vector_size(types), nodes); + } + + if (nodes == 0) { + igraph_empty(graph, 0, directed); + return IGRAPH_SUCCESS; + } + + /* the case of zero-length type vector is caught above, safe to call vector_min here */ + if (igraph_vector_min(types) < 0) { + IGRAPH_ERRORF("Types should be non-negative, but found %g.", + IGRAPH_EINVAL, igraph_vector_min(types)); + } + + IGRAPH_VECTOR_INIT_FINALLY(&edges, 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 >= pref_len) { + goto err_pref_too_short; + } + nnval = VECTOR(*pref)[type]; + if (nnval < 0) { + goto err_pref_neg; + } + 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 >= pref_len) { + goto err_pref_too_short; + } + nnval = VECTOR(*pref)[type]; + if (nnval < 0) { + goto err_pref_neg; + } + 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 IGRAPH_SUCCESS; + +err_pref_too_short: + IGRAPH_ERRORF("Preference vector should have length at least %ld with the given types.", IGRAPH_EINVAL, + (long) igraph_vector_max(types) + 1); + +err_pref_neg: + IGRAPH_ERRORF("Preferences should be non-negative, but found %g.", IGRAPH_EINVAL, + igraph_vector_min(pref)); +} + +static 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]); + } + igraph_free(s->sumtrees); +} + +/** + * \function igraph_citing_cited_type_game + * \brief Simulates 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. + * + * + * 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. I.e. 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. + * + * + * 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, NULL }; + igraph_psumtree_t *sumtrees; + igraph_vector_t sums; + long int no_of_types; + long int i, j; + + if (igraph_vector_size(types) != nodes) { + IGRAPH_ERRORF("Length of types vector (%ld) not equal to number" + " of nodes (%" IGRAPH_PRId ").", + IGRAPH_EINVAL, igraph_vector_size(types), nodes); + } + + /* avoid calling vector_max on empty vector */ + no_of_types = nodes == 0 ? 0 : igraph_vector_max(types) + 1; + + if (igraph_matrix_ncol(pref) != no_of_types) { + IGRAPH_ERRORF("Number of preference matrix columns (%ld) not " + "equal to number of types (%ld).", + IGRAPH_EINVAL, + igraph_matrix_ncol(pref), + no_of_types); + } + if (igraph_matrix_nrow(pref) != no_of_types) { + IGRAPH_ERRORF("Number of preference matrix rows (%ld) not " + "equal to number of types (%ld).", + IGRAPH_EINVAL, + igraph_matrix_nrow(pref), + no_of_types); + } + + /* return an empty graph if nodes is zero */ + if (nodes == 0) { + return igraph_empty(graph, 0, directed); + } + + IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); + + str.sumtrees = sumtrees = IGRAPH_CALLOC(no_of_types, 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 < no_of_types; i++) { + IGRAPH_CHECK(igraph_psumtree_init(&sumtrees[i], nodes)); + str.no++; + } + IGRAPH_VECTOR_INIT_FINALLY(&sums, no_of_types); + + IGRAPH_CHECK(igraph_vector_reserve(&edges, nodes * edges_per_step)); + + /* First node */ + for (i = 0; i < no_of_types; i++) { + long int type = (long int) VECTOR(*types)[0]; + if ( MATRIX(*pref, i, type) < 0) { + IGRAPH_ERRORF("Preference matrix contains negative entry: %g.", IGRAPH_EINVAL, MATRIX(*pref, i, type)); + } + IGRAPH_CHECK(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 < no_of_types; j++) { + if ( MATRIX(*pref, j, type) < 0) { + IGRAPH_ERRORF("Preference matrix contains negative entry: %g.", IGRAPH_EINVAL, MATRIX(*pref, j, type)); + } + IGRAPH_CHECK(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_vector_destroy(&sums); + IGRAPH_FINALLY_CLEAN(2); + return IGRAPH_SUCCESS; +} diff --git a/src/rigraph/core/games/correlated.c b/src/rigraph/core/games/correlated.c new file mode 100644 index 0000000..732d00b --- /dev/null +++ b/src/rigraph/core/games/correlated.c @@ -0,0 +1,326 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2003-2021 The igraph development 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., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_games.h" + +#include "igraph_conversion.h" +#include "igraph_constructors.h" +#include "igraph_interface.h" +#include "igraph_qsort.h" +#include "igraph_random.h" +#include "igraph_structural.h" + +#include "core/interruption.h" + +/* The "code" of an edge is a single index representing its location in the adjacency matrix, + * More specifically, the relevant parts of the adjacency matrix (i.e. non-diagonal in directed, + * upper triangular in undirecred) are column-wise concatenated into an array. The "code" is + * the index in this array. We use floating point numbers for the code, as it can easily + * exceed integers representable on 32 bits. + */ +#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((double)(f),(double)(t)) : U_CODE((double)(f),(double)(t))) + +/* TODO: Slight speedup may be possible if repeated vertex count queries are avoided. */ +static int code_cmp(void *graph, const void *va, const void *vb) { + const igraph_real_t *a = (const igraph_real_t *) va; + const igraph_real_t *b = (const igraph_real_t *) vb; + const long int no_of_nodes = igraph_vcount((igraph_t *) graph); + const igraph_bool_t directed = igraph_is_directed((igraph_t *) graph); + const igraph_real_t codea = CODE(a[0], a[1]); + const igraph_real_t codeb = CODE(b[0], b[1]); + if (codea < codeb) { + return -1; + } else if (codea > codeb) { + return 1; + } else { + return 0; + } +} + +/* Sort an edge vector by edge codes. */ +static void sort_edges(igraph_vector_t *edges, const igraph_t *graph) { + igraph_qsort_r(VECTOR(*edges), igraph_vector_size(edges) / 2, 2*sizeof(igraph_real_t), (void *) graph, code_cmp); +} + +/** + * \function igraph_correlated_game + * \brief Generates a random graph correlated to an existing graph. + * + * Sample a new graph by perturbing the adjacency matrix of a + * given simple graph and shuffling its vertices. + * + * \param old_graph The original graph, it must be simple. + * \param new_graph The new graph will be stored here. + * \param corr A scalar in the unit interval [0,1], the target Pearson + * correlation between the adjacency matrices of the original and 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. Typically, + * the density of \p old_graph. + * \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) { + + long int no_of_nodes = igraph_vcount(old_graph); + long 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; + long 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; + long int i; + igraph_bool_t simple; + + if (corr < 0 || corr > 1) { + IGRAPH_ERRORF("Correlation must be in [0,1] in correlated Erdos-Renyi game, got %g.", + IGRAPH_EINVAL, corr); + } + if (p <= 0 || p >= 1) { + IGRAPH_ERRORF("Edge probability must be in (0,1) in correlated Erdos-Renyi game, got %g.", + IGRAPH_EINVAL, p); + } + if (permutation) { + if (igraph_vector_size(permutation) != no_of_nodes) { + IGRAPH_ERROR("Invalid permutation length in correlated Erdos-Renyi game.", + IGRAPH_EINVAL); + } + } + IGRAPH_CHECK(igraph_is_simple(old_graph, &simple)); + if (! simple) { + IGRAPH_ERROR("The original graph must be simple for 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)); + /* The samping method used is analogous to the one in igraph_erdos_renyi_game_gnp(), + * and assumes that the edge list of the old graph is in order of increasing "codes". + * Even IGRAPH_EDGEORDER_TO does not guarantee this, therefore we sort explicitly. + */ + sort_edges(&edges, old_graph); + + 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(); + + /* 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 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) { + IGRAPH_ALLOW_INTERRUPTION(); + 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 */ + long int to, from; + IGRAPH_ASSERT(igraph_finite(next_a)); + if (directed) { + to = (long int) floor(next_a / no_of_nodes); + from = (long int) (next_a - ((igraph_real_t)to) * no_of_nodes); + if (from == to) { + to = no_of_nodes - 1; + } + } else { + to = (long int) floor((sqrt(8 * next_a + 1) + 1) / 2); + from = (long 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) { + long int newec = igraph_vector_size(&newedges); + for (i = 0; i < newec; i++) { + long 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 IGRAPH_SUCCESS; +} + +#undef D_CODE +#undef U_CODE +#undef CODE +#undef CODEE +#undef UPD_E +#undef UPD_A +#undef UPD_D + +/** + * \function igraph_correlated_pair_game + * \brief Generates 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, + igraph_integer_t 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 IGRAPH_SUCCESS; +} diff --git a/src/rigraph/core/games/degree_sequence.c b/src/rigraph/core/games/degree_sequence.c new file mode 100644 index 0000000..b4e6dd4 --- /dev/null +++ b/src/rigraph/core/games/degree_sequence.c @@ -0,0 +1,775 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2003-2021 The igraph development 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., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_games.h" + +#include "igraph_adjlist.h" +#include "igraph_constructors.h" +#include "igraph_conversion.h" +#include "igraph_graphicality.h" +#include "igraph_memory.h" +#include "igraph_random.h" +#include "igraph_vector_ptr.h" + +#include "core/interruption.h" +#include "core/set.h" + +static int igraph_i_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_graphical(out_seq, in_seq, IGRAPH_LOOPS_SW | IGRAPH_MULTI_SW, °seq_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(igraph_free, bag1); + + 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(igraph_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; +} + +static int igraph_i_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(seq, 0, IGRAPH_SIMPLE_SW, °seq_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) { + IGRAPH_ALLOW_INTERRUPTION(); + + /* 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; +} + +static int igraph_i_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(out_seq, in_seq, IGRAPH_SIMPLE_SW, °_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) { + IGRAPH_ALLOW_INTERRUPTION(); + + /* 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); + + /* 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; +} + +/* 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_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; + long vcount, ecount, stub_count; + + IGRAPH_CHECK(igraph_is_graphical(degseq, NULL, IGRAPH_SIMPLE_SW, °seq_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); + + /* Fill stubs vector. */ + { + long k = 0; + for (i = 0; i < vcount; ++i) { + long deg = (long) VECTOR(*degseq)[i]; + for (j = 0; j < deg; ++j) { + VECTOR(stubs)[k++] = i; + } + } + } + + /* Build an adjacency list in terms of sets; used to check for multi-edges. */ + IGRAPH_CHECK(igraph_vector_ptr_init(&adjlist, vcount)); + IGRAPH_VECTOR_PTR_SET_ITEM_DESTRUCTOR(&adjlist, igraph_set_destroy); + IGRAPH_FINALLY(igraph_vector_ptr_destroy_all, &adjlist); + for (i = 0; i < vcount; ++i) { + igraph_set_t *set = IGRAPH_CALLOC(1, 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])); + } + + RNG_BEGIN(); + + for (;;) { + igraph_bool_t success = 1; + + /* Shuffle stubs vector with Fisher-Yates and check for self-loops and multi-edges as we go. */ + for (i = 0; i < ecount; ++i) { + long k, from, to; + + k = RNG_INTEGER(2*i, stub_count-1); + SWAP_INT_ELEM(stubs, 2*i, k); + + k = RNG_INTEGER(2*i+1, stub_count-1); + SWAP_INT_ELEM(stubs, 2*i+1, k); + + from = VECTOR(stubs)[2*i]; + to = VECTOR(stubs)[2*i+1]; + + /* self-loop, fail */ + if (from == to) { + 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; + } + + /* Clear adjacency list. */ + for (j = 0; j < vcount; ++j) { + igraph_set_clear((igraph_set_t *) VECTOR(adjlist)[j]); + } + + IGRAPH_ALLOW_INTERRUPTION(); + } + + 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; +} + + +static int igraph_i_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; + long vcount, ecount; + + IGRAPH_CHECK(igraph_is_graphical(out_deg, in_deg, IGRAPH_SIMPLE_SW, °seq_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); + + /* Fill in- and out-stubs vectors. */ + { + long 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; + } + } + } + + /* Build an adjacency list in terms of sets; used to check for multi-edges. */ + IGRAPH_CHECK(igraph_vector_ptr_init(&adjlist, vcount)); + IGRAPH_VECTOR_PTR_SET_ITEM_DESTRUCTOR(&adjlist, igraph_set_destroy); + IGRAPH_FINALLY(igraph_vector_ptr_destroy_all, &adjlist); + for (i = 0; i < vcount; ++i) { + igraph_set_t *set = IGRAPH_CALLOC(1, 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])); + } + + RNG_BEGIN(); + + for (;;) { + igraph_bool_t success = 1; + + /* Shuffle out-stubs vector with Fisher-Yates and check for self-loops and multi-edges as we go. */ + for (i = 0; i < ecount; ++i) { + long k, from, to; + igraph_set_t *set; + + k = RNG_INTEGER(i, ecount-1); + SWAP_INT_ELEM(out_stubs, i, k); + + from = VECTOR(out_stubs)[i]; + to = VECTOR(in_stubs)[i]; + + /* self-loop, 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; + } + + /* Clear adjacency list. */ + for (j = 0; j < vcount; ++j) { + igraph_set_clear((igraph_set_t *) VECTOR(adjlist)[j]); + } + + IGRAPH_ALLOW_INTERRUPTION(); + } + + 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; +} + +#undef SWAP_INT_ELEM + + +/* 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 \c NULL or 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 may 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 + * (\prod_{i<j} A_{ij} ! \prod_i A_{ii} !!)^{-1}, + * where \c A denotes the adjacency matrix and !! denotes + * the double factorial. Here \c A is assumed to have twice the number of + * self-loops on its diagonal. + * The corresponding expression for directed graphs is + * (\prod_{i,j} A_{ij}!)^{-1}. + * 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 edge and self-loop multiplicities. + * \cli IGRAPH_DEGSEQ_SIMPLE_NO_MULTIPLE + * This method generates simple graphs. + * It 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 can generate all simple + * realizations of a degree sequence, but it is not guaranteed + * to sample them uniformly. This method 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 generates all simple graphs with the same probability. + * \cli IGRAPH_DEGSEQ_VL + * This method samples undirected \em connected graphs approximately + * uniformly. It is a Monte Carlo method based on degree-preserving + * edge swaps. + * 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_graphical() + * + * \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_i_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_i_degree_sequence_game_no_multiple_undirected(graph, out_deg); + } else { + return igraph_i_degree_sequence_game_no_multiple_directed(graph, out_deg, in_deg); + } + + case IGRAPH_DEGSEQ_SIMPLE_NO_MULTIPLE_UNIFORM: + if (in_deg == 0) { + return igraph_i_degree_sequence_game_no_multiple_undirected_uniform(graph, out_deg); + } else { + return igraph_i_degree_sequence_game_no_multiple_directed_uniform(graph, out_deg, in_deg); + } + + default: + IGRAPH_ERROR("Invalid degree sequence game method", IGRAPH_EINVAL); + } +} diff --git a/src/rigraph/core/games/degree_sequence_vl/gengraph_box_list.cpp b/src/rigraph/core/games/degree_sequence_vl/gengraph_box_list.cpp new file mode 100644 index 0000000..11848d2 --- /dev/null +++ b/src/rigraph/core/games/degree_sequence_vl/gengraph_box_list.cpp @@ -0,0 +1,110 @@ +/* + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU 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 . + */ +#include "gengraph_box_list.h" +#include + +namespace gengraph { + +void box_list::insert(int v) { + 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) { + int p = prev[v]; + int nxt = next[v]; + if (p < 0) { + int d = deg[v]; + assert(list[d - 1] == v); + list[d - 1] = nxt; + if (d == dmax && nxt < 0) { + do { + dmax--; + } while (dmax > 0 && list[dmax - 1] < 0); + } + } else { + next[p] = nxt; + } + if (nxt >= 0) { + prev[nxt] = 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++); + int *w2 = neigh[v2]; + while (*w2 != v) { + w2++; + } + int *w3 = neigh[v2] + (deg[v2] - 1); + assert(w2 <= w3); + int tmp = *w3; + *w3 = *w2; + *w2 = tmp; + pop(v2); + deg[v2]--; + insert(v2); + } +} + +} // namespace gengraph diff --git a/src/rigraph/core/games/degree_sequence_vl/gengraph_box_list.h b/src/rigraph/core/games/degree_sequence_vl/gengraph_box_list.h new file mode 100644 index 0000000..f5c7a41 --- /dev/null +++ b/src/rigraph/core/games/degree_sequence_vl/gengraph_box_list.h @@ -0,0 +1,83 @@ +/* + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU 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 . + */ +// 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 + +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/src/rigraph/core/games/degree_sequence_vl/gengraph_definitions.h b/src/rigraph/core/games/degree_sequence_vl/gengraph_definitions.h new file mode 100644 index 0000000..b52ebd1 --- /dev/null +++ b/src/rigraph/core/games/degree_sequence_vl/gengraph_definitions.h @@ -0,0 +1,201 @@ +/* + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU 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 . + */ +#ifndef DEFINITIONS_H +#define DEFINITIONS_H + +#include +#include +#include + +#include "internal/hacks.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 + +//inline double round(double x) throw () { return (floor(0.5+x)); } + +// Min & Max +#ifndef min + #define defmin(type) inline type min(type a, type b) { return ab ? 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() { + 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/src/rigraph/core/games/degree_sequence_vl/gengraph_degree_sequence.cpp b/src/rigraph/core/games/degree_sequence_vl/gengraph_degree_sequence.cpp new file mode 100644 index 0000000..0f5a784 --- /dev/null +++ b/src/rigraph/core/games/degree_sequence_vl/gengraph_degree_sequence.cpp @@ -0,0 +1,421 @@ +/* + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU 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 . + */ +#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 +#include +#include +#include +#include +#include + +// 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 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::iterator end = degree.end(); +// for(vector::iterator it=degree.begin(); it!=end; *(yo++) = *(it++)); +// } +// else { +// // Input is a degree distribution : d0 #(degree=d0), d1 #(degree=d1), ... +// vector 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::iterator it_n = n_with_degree.begin(); +// for(vector::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) { + throw std::invalid_argument( + "Fatal error in degree_sequence constructor: " + "positive average degree must be specified."); + } + 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; ideg[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; i0) 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/src/rigraph/core/games/degree_sequence_vl/gengraph_degree_sequence.h b/src/rigraph/core/games/degree_sequence_vl/gengraph_degree_sequence.h new file mode 100644 index 0000000..61c287d --- /dev/null +++ b/src/rigraph/core/games/degree_sequence_vl/gengraph_degree_sequence.h @@ -0,0 +1,100 @@ +/* + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU 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 . + */ +#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/src/rigraph/core/games/degree_sequence_vl/gengraph_graph_molloy_hash.cpp b/src/rigraph/core/games/degree_sequence_vl/gengraph_graph_molloy_hash.cpp new file mode 100644 index 0000000..dc055a2 --- /dev/null +++ b/src/rigraph/core/games/degree_sequence_vl/gengraph_graph_molloy_hash.cpp @@ -0,0 +1,1172 @@ +/* + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU 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 . + */ +#include "gengraph_definitions.h" +#include +#include +#include +#include +#include + +#include "gengraph_qsort.h" +#include "gengraph_hash.h" +#include "gengraph_degree_sequence.h" +#include "gengraph_graph_molloy_hash.h" + +#include "config.h" +#include "core/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 °s) { + 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 °s) { + 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++) { + 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: + throw std::invalid_argument("Error in graph_molloy_hash::shuffle(): Unknown heuristics type."); + } + } + + delete[] Kbuff; + delete[] visited; + + if (maxtimes <= all_swaps) { + IGRAPH_WARNING("Cannot shuffle graph, maybe it is the only realization of its degree sequence?"); + } + + // 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; in) 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; im) 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=0; i--) { + int t=nb[i]; + nb[i]=c; + c+=t; + } + // sort + for(i=0; i0; ) { + // 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; v0min_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. + */ +#ifndef GRAPH_MOLLOY_HASH_H +#define GRAPH_MOLLOY_HASH_H + +#include "gengraph_definitions.h" +#include "gengraph_hash.h" +#include "gengraph_degree_sequence.h" + +#include +#include +// 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 (u,v). Return FALSE if vertex a is already full. + // WARNING : only to be used by havelhakimi(), restore() or constructors + inline bool add_edge(int u, int v, int *realdeg) { + int deg_u = realdeg[u]; + if (deg_u == deg[u]) { + return false; + } + // Check that edge was not already inserted + assert(fast_search(neigh[u], int((u == n - 1 ? links + size : neigh[u + 1]) - neigh[u]), v) == NULL); + assert(fast_search(neigh[v], int((v == n - 1 ? links + size : neigh[v + 1]) - neigh[v]), u) == NULL); + assert(deg[u] < deg_u); + int deg_v = realdeg[v]; + if (IS_HASH(deg_u)) { + *H_add(neigh[u], HASH_EXPAND(deg_u), v) = v; + } else { + neigh[u][deg[u]] = v; + } + if (IS_HASH(deg_v)) { + *H_add(neigh[v], HASH_EXPAND(deg_v), u) = u; + } else { + neigh[v][deg[v]] = u; + } + deg[u]++; + deg[v]++; + // Check that edge was actually inserted + assert(fast_search(neigh[u], int((u == n - 1 ? links + size : neigh[u + 1]) - neigh[u]), v) != NULL); + assert(fast_search(neigh[v], int((v == n - 1 ? links + size : neigh[v + 1]) - neigh[v]), u) != 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 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 u, int v) { + assert(H_is(neigh[u], deg[u], v) == (fast_search(neigh[u], HASH_SIZE(deg[u]), v) != NULL)); + assert(H_is(neigh[v], deg[v], u) == (fast_search(neigh[v], HASH_SIZE(deg[v]), u) != NULL)); + assert(H_is(neigh[u], deg[u], v) == H_is(neigh[v], deg[v], u)); + if (deg[u] < deg[v]) { + return H_is(neigh[u], deg[u], v); + } else { + return H_is(neigh[v], deg[v], u); + } + } + // 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/src/rigraph/core/games/degree_sequence_vl/gengraph_graph_molloy_optimized.cpp b/src/rigraph/core/games/degree_sequence_vl/gengraph_graph_molloy_optimized.cpp new file mode 100644 index 0000000..2859cdb --- /dev/null +++ b/src/rigraph/core/games/degree_sequence_vl/gengraph_graph_molloy_optimized.cpp @@ -0,0 +1,2231 @@ +/* + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU 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 . + */ +#include "gengraph_definitions.h" +#include +#include +#include +#include + +#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" + + +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 °s) { + 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 °s) { + 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= 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(); + + /* Cannot use IGRAPH_ERRORF() as this function does not return + * an error code. This situation should only occur when the degree + * sequence is not graphical, but that is already checked at the top + * level. Therefore, we report EINTERNAL, as triggering this + * indicates a bug. */ + igraph_errorf("Error in graph_molloy_opt::havelhakimi(): " + "Couldn't bind vertex %d entirely (%d edges remaining)", + IGRAPH_FILE_BASENAME, __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; + /* Cannot use IGRAPH_ERROR() as this function does not return an error code. */ + igraph_error("Cannot create connected graph from degree sequence: " + "vertex with degree 0 found.", + IGRAPH_FILE_BASENAME, __LINE__, + IGRAPH_EINVAL); + 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::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 + 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", IGRAPH_FILE_BASENAME, + __LINE__, -1, src_0); + if (nopath) igraph_warningf("%d (src,dst) pairs had no possible path\n", + IGRAPH_FILE_BASENAME, __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 DEGSEQ_VL_DEBUG + igraph_statusf("Sampling path %d -> %d\n", 0, s, v); +#endif // DEGSEQ_VL_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", IGRAPH_FILE_BASENAME, + __LINE__, -1, src_0); + if (nopath) igraph_warningf("%d (src,dst) pairs had no possible path", + IGRAPH_FILE_BASENAME, __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)", + IGRAPH_FILE_BASENAME, __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", IGRAPH_FILE_BASENAME, __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", IGRAPH_FILE_BASENAME, __LINE__, + -1, src_0); + if (nopath) igraph_warningf("%d (src,dst) pairs had no possible path", + IGRAPH_FILE_BASENAME, __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) { + IGRAPH_UNUSED(mode); +#ifndef NDEBUG + 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 0); + } +#endif + 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; inb_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; i0; 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(ideg[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(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(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; i1 && 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; i0) { + 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. + */ +#ifndef GRAPH_MOLLOY_OPT_H +#define GRAPH_MOLLOY_OPT_H + +#include "gengraph_definitions.h" +#include "gengraph_degree_sequence.h" +#include "gengraph_qsort.h" + +#include +#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 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 u, const int v) { + if (deg[v] < deg[u]) { + return (fast_search(neigh[v], deg[v], u) != NULL); + } else { + return (fast_search(neigh[u], deg[u], v) != 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/src/rigraph/core/games/degree_sequence_vl/gengraph_hash.h b/src/rigraph/core/games/degree_sequence_vl/gengraph_hash.h new file mode 100644 index 0000000..7084edc --- /dev/null +++ b/src/rigraph/core/games/degree_sequence_vl/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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU 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 . + */ +#ifndef HASH_H +#define HASH_H + +#include +#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/src/rigraph/core/games/degree_sequence_vl/gengraph_header.h b/src/rigraph/core/games/degree_sequence_vl/gengraph_header.h new file mode 100644 index 0000000..50e65d5 --- /dev/null +++ b/src/rigraph/core/games/degree_sequence_vl/gengraph_header.h @@ -0,0 +1,109 @@ +/* + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU 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 . + */ +#include "gengraph_definitions.h" +#include +#include + +#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(); +} + +} + +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/src/rigraph/core/games/degree_sequence_vl/gengraph_mr-connected.cpp b/src/rigraph/core/games/degree_sequence_vl/gengraph_mr-connected.cpp new file mode 100644 index 0000000..0417c24 --- /dev/null +++ b/src/rigraph/core/games/degree_sequence_vl/gengraph_mr-connected.cpp @@ -0,0 +1,192 @@ +/* + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU 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 . + */ +#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_graphicality.h" +#include "igraph_types.h" +#include "igraph_error.h" + +#include "core/exceptions.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 %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) { + IGRAPH_HANDLE_EXCEPTIONS( + igraph_bool_t is_graphical; + + if (in_seq && igraph_vector_size(in_seq) != 0) { + IGRAPH_ERROR("This generator works with undirected graphs only", IGRAPH_EINVAL); + } + + IGRAPH_CHECK(igraph_is_graphical(out_seq, 0, IGRAPH_SIMPLE_SW, &is_graphical)); + if (!is_graphical) { + IGRAPH_ERROR("Cannot realize the given degree sequence as an undirected, simple graph", + IGRAPH_EINVAL); + } + + RNG_BEGIN(); + + 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_FATAL("g->havelhakimi() failed; please report as a bug."); + } + + 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 IGRAPH_SUCCESS; + } + +} diff --git a/src/rigraph/core/games/degree_sequence_vl/gengraph_powerlaw.cpp b/src/rigraph/core/games/degree_sequence_vl/gengraph_powerlaw.cpp new file mode 100644 index 0000000..8ba0c54 --- /dev/null +++ b/src/rigraph/core/games/degree_sequence_vl/gengraph_powerlaw.cpp @@ -0,0 +1,272 @@ +/* + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU 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 . + */ +// Pascalou ... +#ifdef pascalou + #define my_random() random() + #define MY_RAND_MAX 0x7FFFFFFF +#else + #include "gengraph_definitions.h" +#endif + +#include "gengraph_powerlaw.h" +#include +#include +#include + +#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", IGRAPH_FILE_BASENAME, __LINE__, -1, alpha); + if (alpha <= 1.0 && maxi >= 0) + igraph_warningf("powerlaw exponent %f should be > 1", IGRAPH_FILE_BASENAME, __LINE__, + -1, alpha); + if (maxi >= 0 && mini > maxi) + igraph_warningf("powerlaw max %d should be greater than min %d", + IGRAPH_FILE_BASENAME, __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 k1 = 0; + while (k1 < max_dt) { + div *= 0.5; + k1++; + }; + while (dt[k1] < 0 || dt[k1] < prev_pos_in_table) { + k1++; + div *= 0.5; + }; + double prob2 = double(table[prev_pos_in_table + 1]); + if (dt[k1] == prev_pos_in_table) do { + prob2 *= 0.5; + } while (dt[++k1] < 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))) { + /* Cannot use IGRAPH_ERRORF() as this function does not + * return an igraph error code. */ + igraph_errorf("Fatal error in powerlaw::init_to_mean(%f): " + "Mean must be in ]min, (min+max)/2[ = ]%d, %d[", + IGRAPH_FILE_BASENAME, __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/src/rigraph/core/games/degree_sequence_vl/gengraph_powerlaw.h b/src/rigraph/core/games/degree_sequence_vl/gengraph_powerlaw.h new file mode 100644 index 0000000..57b6b7d --- /dev/null +++ b/src/rigraph/core/games/degree_sequence_vl/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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU 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 . + */ +#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/src/rigraph/core/games/degree_sequence_vl/gengraph_qsort.h b/src/rigraph/core/games/degree_sequence_vl/gengraph_qsort.h new file mode 100644 index 0000000..6af1f2c --- /dev/null +++ b/src/rigraph/core/games/degree_sequence_vl/gengraph_qsort.h @@ -0,0 +1,564 @@ +/* + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU 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 . + */ +#ifndef QSORT_H +#define QSORT_H + +#include +#include + +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++) { + 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++) { + 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--) { + 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++) { + 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 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++) { + 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 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/src/rigraph/core/games/degree_sequence_vl/gengraph_random.cpp b/src/rigraph/core/games/degree_sequence_vl/gengraph_random.cpp new file mode 100644 index 0000000..c4d08cb --- /dev/null +++ b/src/rigraph/core/games/degree_sequence_vl/gengraph_random.cpp @@ -0,0 +1,277 @@ +/* + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU 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 . + */ +#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 "gengraph_random.h" +#include + +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()= 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/src/rigraph/core/games/degree_sequence_vl/gengraph_random.h b/src/rigraph/core/games/degree_sequence_vl/gengraph_random.h new file mode 100644 index 0000000..90d86b5 --- /dev/null +++ b/src/rigraph/core/games/degree_sequence_vl/gengraph_random.h @@ -0,0 +1,213 @@ +/* + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU 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 . + */ +#ifndef RNG_H +#define RNG_H + +#include "igraph_random.h" + +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 +// #include +// #ifdef _WIN32 +// #include +// #define getpid _getpid +// #else +// #include +// #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/src/rigraph/core/games/degree_sequence_vl/gengraph_vertex_cover.h b/src/rigraph/core/games/degree_sequence_vl/gengraph_vertex_cover.h new file mode 100644 index 0000000..25b8e84 --- /dev/null +++ b/src/rigraph/core/games/degree_sequence_vl/gengraph_vertex_cover.h @@ -0,0 +1,72 @@ +/* + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU 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 . + */ +#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" +#include + +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]; + int v2 = *(w++); + int dm = deg[v2]; + 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/src/rigraph/core/games/dotproduct.c b/src/rigraph/core/games/dotproduct.c new file mode 100644 index 0000000..c4bf614 --- /dev/null +++ b/src/rigraph/core/games/dotproduct.c @@ -0,0 +1,280 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2014 Gabor Csardi + 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_blas.h" + +/** + * \function igraph_dot_product_game + * \brief Generates 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. + * + * + * 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_blas_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/src/rigraph/core/games/erdos_renyi.c b/src/rigraph/core/games/erdos_renyi.c new file mode 100644 index 0000000..c0b1acf --- /dev/null +++ b/src/rigraph/core/games/erdos_renyi.c @@ -0,0 +1,285 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2003-2021 The igraph development 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., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_games.h" + +#include "igraph_constructors.h" +#include "igraph_interface.h" +#include "igraph_nongraph.h" +#include "igraph_random.h" + +/** + * \section about_games + * + * Games are randomized graph generators. Randomization means that + * they generate a different graph every time you call them. + */ + +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; + long int vsize; + + 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 == 0) { + 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)); + + vsize = igraph_vector_size(&s); + if (directed && loops) { + for (i = 0; i < vsize; 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 < vsize; 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 < vsize; 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 < vsize; 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 == 0) { + 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 (Erdős-Rényi) 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; +} diff --git a/src/rigraph/core/games/establishment.c b/src/rigraph/core/games/establishment.c new file mode 100644 index 0000000..552858c --- /dev/null +++ b/src/rigraph/core/games/establishment.c @@ -0,0 +1,186 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2003-2021 The igraph development 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., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_games.h" + +#include "igraph_constructors.h" +#include "igraph_memory.h" +#include "igraph_nongraph.h" +#include "igraph_random.h" + +/** + * \function igraph_establishment_game + * \brief Generates a graph with a simple growing model with vertex types. + * + * + * 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. + * If \c NULL, the distribution is assumed to be uniform. + * \param pref_matrix Matrix giving the connection probabilities for + * different vertex types. + * \param directed Logical, whether to generate a directed graph. + * \param node_type_vec An initialized vector or \c NULL. + * If not \c NULL, the type of each node will be stored here. + * \return Error code. + * + * Added in version 0.2. + * + * 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, + const igraph_vector_t *type_dist, + const igraph_matrix_t *pref_matrix, + igraph_bool_t directed, + igraph_vector_t *node_type_vec) { + long int i, j; + igraph_vector_t edges; + igraph_vector_t cumdist; + igraph_vector_t potneis; + igraph_real_t maxcum; + igraph_vector_t *nodetypes; + + /* Argument contracts */ + if(nodes < 0){ + IGRAPH_ERROR("The number of vertices must be non-negative.", IGRAPH_EINVAL); + } + + if (types < 1) { + IGRAPH_ERROR("The number of vertex types must be at least 1.", IGRAPH_EINVAL); + } + + if (type_dist) { + igraph_real_t lo; + + if (igraph_vector_size(type_dist) != types) { + IGRAPH_ERROR("The vertex type distribution vector must agree in length with the number of types.", + IGRAPH_EINVAL); + } + + lo = igraph_vector_min(type_dist); + if (lo < 0) { + IGRAPH_ERROR("The vertex type distribution vector must not contain negative values.", IGRAPH_EINVAL); + } + if (igraph_is_nan(lo)) { + IGRAPH_ERROR("The vertex type distribution vector must not contain NaN.", IGRAPH_EINVAL); + } + } + + if (igraph_matrix_nrow(pref_matrix) != types || igraph_matrix_ncol(pref_matrix) != types) { + IGRAPH_ERROR("The preference matrix must be square and agree in dimensions with the number of types.", IGRAPH_EINVAL); + } + + { + igraph_real_t lo, hi; + igraph_matrix_minmax(pref_matrix, &lo, &hi); + + if (lo < 0 || hi > 1) { + IGRAPH_ERROR("The preference matrix must contain probabilities in [0, 1].", IGRAPH_EINVAL); + } + if (igraph_is_nan(lo) || igraph_is_nan(hi)) { + IGRAPH_ERROR("The preference matrix must not contain NaN.", IGRAPH_EINVAL); + } + } + + if (! directed && ! igraph_matrix_is_symmetric(pref_matrix)) { + IGRAPH_ERROR("The preference matrix must be symmetric when generating undirected graphs.", IGRAPH_EINVAL); + } + + IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); + IGRAPH_VECTOR_INIT_FINALLY(&cumdist, types + 1); + IGRAPH_VECTOR_INIT_FINALLY(&potneis, k); + + if (type_dist) { + VECTOR(cumdist)[0] = 0; + for (i = 0; i < types; ++i) { + VECTOR(cumdist)[i + 1] = VECTOR(cumdist)[i] + VECTOR(*type_dist)[i]; + } + } else { + for (i = 0; i < types+1; ++i) { + VECTOR(cumdist)[i] = i; + } + } + maxcum = igraph_vector_tail(&cumdist); + + if (maxcum <= 0) { + IGRAPH_ERROR("The vertex type distribution vector must contain at least one positive value.", IGRAPH_EINVAL); + } + + if (node_type_vec) { + nodetypes = node_type_vec; + IGRAPH_CHECK(igraph_vector_resize(nodetypes, nodes)); + } else { + nodetypes = IGRAPH_CALLOC(1, igraph_vector_t); + if (! nodetypes) { + IGRAPH_ERROR("Insufficient memory for establishment_game.", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, nodetypes); + IGRAPH_VECTOR_INIT_FINALLY(nodetypes, nodes); + } + + 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(); + + if (! node_type_vec) { + igraph_vector_destroy(nodetypes); + IGRAPH_FREE(nodetypes); + IGRAPH_FINALLY_CLEAN(2); + } + igraph_vector_destroy(&potneis); + 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 IGRAPH_SUCCESS; +} diff --git a/src/rigraph/core/games/forestfire.c b/src/rigraph/core/games/forestfire.c new file mode 100644 index 0000000..a4d69bf --- /dev/null +++ b/src/rigraph/core/games/forestfire.c @@ -0,0 +1,263 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2007-2012 Gabor Csardi + 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_interface.h" +#include "igraph_constructors.h" +#include "igraph_dqueue.h" + +#include "core/interruption.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; + + +static 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 + * + * + * The network is generated in the following way. One vertex is added at + * a time. This vertex connects to (cites) ambs vertices already + * present in the network, chosen uniformly random. Now, for each cited + * vertex v we do the following procedure: + * \olist + * \oli We generate two random numbers, x and y, that are + * geometrically distributed with means p/(1-p) and + * rp(1-rp). (p is \p fw_prob, r is + * \p bw_factor.) The new vertex cites x outgoing neighbors + * and y incoming neighbors of v, from those which are + * not yet cited by the new vertex. If there are less than x or + * y such vertices available then we cite all of them. + * \oli The same procedure is applied to all the newly cited + * vertices. + * \endolist + * + * 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. + * + * 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 bw.factor*fw.prob. + * \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 || fw_prob >= 1) { + IGRAPH_ERROR("Forest fire model: 'fw_prob' must satisfy 0 <= fw_prob < 1.", + IGRAPH_EINVAL); + } + if (bw_factor * fw_prob < 0 || bw_factor * fw_prob >= 1) { + IGRAPH_ERROR("Forest fire model: 'bw_factor' must satisfy 0 <= bw_factor * fw_prob < 1.", + IGRAPH_EINVAL); + } + if (ambs < 0) { + IGRAPH_ERROR("Forest fire model: Number of ambassadors must not be negative.", + IGRAPH_EINVAL); + } + + if (ambs == 0) { + 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 IGRAPH_SUCCESS; +} diff --git a/src/rigraph/core/games/grg.c b/src/rigraph/core/games/grg.c new file mode 100644 index 0000000..797cd66 --- /dev/null +++ b/src/rigraph/core/games/grg.c @@ -0,0 +1,166 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2003-2021 The igraph development 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., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_games.h" + +#include "igraph_constructors.h" +#include "igraph_random.h" + +#include "core/interruption.h" + +/** + * \function igraph_grg_game + * \brief Generates a geometric random graph. + * + * A geometric random graph is created by dropping points (i.e. vertices) + * randomly on the unit square and then connecting all those pairs + * which are less than \c radius apart in Euclidean distance. + * + * + * 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, i.e. the vertices are assumed to be on a torus + * instead of a square. + * \param x An initialized vector or \c NULL. If not \c NULL, the points' + * x coordinates will be returned here. + * \param y An initialized vector or \c NULL. If not \c NULL, the points' + * y coordinates will be returned here. + * \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; + + IGRAPH_ALLOW_INTERRUPTION(); + + 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; + + IGRAPH_ALLOW_INTERRUPTION(); + + 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 IGRAPH_SUCCESS; +} diff --git a/src/rigraph/core/games/growing_random.c b/src/rigraph/core/games/growing_random.c new file mode 100644 index 0000000..68fdefc --- /dev/null +++ b/src/rigraph/core/games/growing_random.c @@ -0,0 +1,103 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2003-2021 The igraph development 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., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_games.h" + +#include "igraph_constructors.h" +#include "igraph_random.h" + +/** + * \ingroup generators + * \function igraph_growing_random_game + * \brief Generates a growing random graph. + * + * + * This function simulates a growing random graph. We start out with + * one vertex. In each 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 (i.e. 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 and are + * connected to a previous 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. + */ +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 > 0 ? (no_of_nodes - 1) * no_of_neighbors : 0; + + 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; +} diff --git a/src/rigraph/core/games/islands.c b/src/rigraph/core/games/islands.c new file mode 100644 index 0000000..7bb0004 --- /dev/null +++ b/src/rigraph/core/games/islands.c @@ -0,0 +1,158 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2003-2021 The igraph development 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., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_games.h" + +#include "igraph_constructors.h" +#include "igraph_random.h" + +/** + * \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 nbNodes; + double maxpossibleedgesPerIsland; + double maxedgesPerIsland; + int nbEdgesInterIslands; + double maxedges; + int startIsland = 0; + int endIsland = 0; + int i, j, is; + double myrand, last; + long int vsize; + + if (islands_n < 0) { + IGRAPH_ERRORF("Number of islands cannot be negative, got %" IGRAPH_PRId ".", IGRAPH_EINVAL, islands_n); + } + if (islands_size < 0) { + IGRAPH_ERRORF("Size of islands cannot be negative, got %" IGRAPH_PRId ".", IGRAPH_EINVAL, islands_size); + } + if (islands_pin < 0 || islands_pin > 1) { + IGRAPH_ERRORF("Edge probability within islands should be between 0 and 1, got %g.", IGRAPH_EINVAL, islands_pin); + } + if (n_inter < 0) { + IGRAPH_ERRORF("Number of inter-island links cannot be negative, got %" IGRAPH_PRId ".", IGRAPH_EINVAL, n_inter); + } + + /* 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; + + /* reserve enough space for all the edges */ + IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); + IGRAPH_CHECK(igraph_vector_reserve(&edges, (long int) maxedges)); + + RNG_BEGIN(); + + /* first create all the islands */ + for (is = 0; is < islands_n; is++) { /* for each island */ + + /* index for start and end of nodes in this island */ + startIsland = islands_size * is; + endIsland = startIsland + islands_size - 1; + + /* 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); + while (last < maxpossibleedgesPerIsland) { /* maxedgesPerIsland */ + IGRAPH_CHECK(igraph_vector_push_back(&s, last)); + myrand = RNG_GEOM(islands_pin); + last += myrand; /* RNG_GEOM(islands_pin); */ + last += 1; + } + + + + /* change this to edges ! */ + vsize = igraph_vector_size(&s); + for (i = 0; i < vsize; 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; + + 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) */ + + for (j = 0; j < n_inter; j++) { /* for each link between islands */ + long int from = RNG_INTEGER(startIsland, endIsland); + long int to = RNG_INTEGER(i * islands_size, (i + 1) * islands_size - 1); + + igraph_vector_push_back(&edges, from); + igraph_vector_push_back(&edges, to); + } + + } + } + + RNG_END(); + + /* actually fill the graph object */ + IGRAPH_CHECK(igraph_create(graph, &edges, nbNodes, 0)); + + /* clean remaining things */ + igraph_vector_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} diff --git a/src/rigraph/core/games/k_regular.c b/src/rigraph/core/games/k_regular.c new file mode 100644 index 0000000..ff989ec --- /dev/null +++ b/src/rigraph/core/games/k_regular.c @@ -0,0 +1,85 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2003-2021 The igraph development 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., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_games.h" + +#include "igraph_interface.h" + +/** + * \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. + * + * + * Currently, this game simply uses \ref igraph_degree_sequence_game with + * the \c SIMPLE_NO_MULTIPLE method and appropriately constructed degree sequences. + * Thefore, it does not sample uniformly: while it can generate all k-regular graphs + * with the given number of vertices, it does not generate each one with the same + * probability. + * + * \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(°seq, no_of_nodes); + igraph_vector_fill(°seq, k); + IGRAPH_CHECK(igraph_degree_sequence_game(graph, °seq, directed ? °seq : 0, mode)); + + igraph_vector_destroy(°seq); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} diff --git a/src/rigraph/core/games/preference.c b/src/rigraph/core/games/preference.c new file mode 100644 index 0000000..ac987e0 --- /dev/null +++ b/src/rigraph/core/games/preference.c @@ -0,0 +1,631 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2003-2021 The igraph development 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., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_games.h" + +#include "igraph_constructors.h" +#include "igraph_memory.h" +#include "igraph_random.h" + +#include "core/interruption.h" + +static 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. + * + * + * 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. + * + * + * 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 + * \p fixed_sizes argument. + * \param fixed_sizes Boolean. If true, then the number of vertices with a + * given vertex type is fixed and the \p type_dist argument gives these + * numbers for each vertex type. If true, and \p 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. + * + * Time complexity: O(|V|+|E|), the + * number of vertices plus the number of edges in the graph. + * + * \sa \ref igraph_asymmetric_preference_game(), + * \ref igraph_establishment_game(), \ref igraph_callaway_traits_game() + */ + +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(nodes < 0){ + IGRAPH_ERROR("The number of vertices must be non-negative.", IGRAPH_EINVAL); + } + + if (types < 1) { + IGRAPH_ERROR("The number of vertex types must be at least 1.", IGRAPH_EINVAL); + } + + if (type_dist) { + igraph_real_t lo; + + if (igraph_vector_size(type_dist) != types) { + IGRAPH_ERROR("The vertex type distribution vector must agree in length with the number of types.", + IGRAPH_EINVAL); + } + + lo = igraph_vector_min(type_dist); + if (lo < 0) { + IGRAPH_ERROR("The vertex type distribution vector must not contain negative values.", IGRAPH_EINVAL); + } + if (igraph_is_nan(lo)) { + IGRAPH_ERROR("The vertex type distribution vector must not contain NaN.", IGRAPH_EINVAL); + } + } + + if (igraph_matrix_nrow(pref_matrix) != types || igraph_matrix_ncol(pref_matrix) != types) { + IGRAPH_ERROR("The preference matrix must be square and agree in dimensions with the number of types.", IGRAPH_EINVAL); + } + + { + igraph_real_t lo, hi; + igraph_matrix_minmax(pref_matrix, &lo, &hi); + + if (lo < 0 || hi > 1) { + IGRAPH_ERROR("The preference matrix must contain probabilities in [0, 1].", IGRAPH_EINVAL); + } + if (igraph_is_nan(lo) || igraph_is_nan(hi)) { + IGRAPH_ERROR("The preference matrix must not contain NaN.", IGRAPH_EINVAL); + } + } + + if (! directed && ! igraph_matrix_is_symmetric(pref_matrix)) { + IGRAPH_ERROR("The preference matrix must be symmetric when generating undirected graphs.", 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("Insufficient memory for preference_game.", 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("Insufficient memory for preference_game.", 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 { + long int an = 0; + if (type_dist) { + for (i = 0; i < types; i++) { + long int no = (long 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 { + long int fixno = (long 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 IGRAPH_SUCCESS; +} + +/** + * \function igraph_asymmetric_preference_game + * \brief Generates a graph with asymmetric vertex types and connection preferences. + * + * + * This is the asymmetric variant of \ref igraph_preference_game(). + * A given number of vertices are generated. Every vertex is assigned to an + * "outgoing" and an "incoming " 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 out_types The number of vertex out-types. + * \param in_types The number of vertex in-types. + * \param type_dist_matrix Matrix of size out_types * in_types, + * giving the joint distribution of vertex types. + * If \c NULL, incoming and outgoing vertex types are independent and uniformly + * distributed. + * \param pref_matrix Matrix of size out_types * in_types, + * giving the connection probabilities for different vertex types. + * \param node_type_out_vec A vector where the individual generated "outgoing" + * vertex types will be stored. If \c NULL, the vertex types won't be saved. + * \param node_type_in_vec A vector where the individual generated "incoming" + * vertex types will be stored. If \c NULL, the vertex types won't be saved. + * \param loops Logical, whether loop edges are allowed. + * \return Error code. + * + * Added in version 0.3. + * + * 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 out_types, + igraph_integer_t in_types, + const igraph_matrix_t *type_dist_matrix, + const igraph_matrix_t *pref_matrix, + igraph_vector_t *node_type_out_vec, + igraph_vector_t *node_type_in_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(nodes < 0){ + IGRAPH_ERROR("The number of vertices must not be negative.", IGRAPH_EINVAL); + } + + if (in_types < 1) { + IGRAPH_ERROR("The number of vertex in-types must be at least 1.", IGRAPH_EINVAL); + } + + if (out_types < 1) { + IGRAPH_ERROR("The number of vertex out-types must be at least 1.", IGRAPH_EINVAL); + } + + if (type_dist_matrix) { + igraph_real_t lo; + + if (igraph_matrix_nrow(type_dist_matrix) != out_types || + igraph_matrix_ncol(type_dist_matrix) != in_types) { + IGRAPH_ERROR("The type distribution matrix must have dimensions out_types * in_types.", IGRAPH_EINVAL); + } + + lo = igraph_matrix_min(type_dist_matrix); + if (lo < 0) { + IGRAPH_ERROR("The type distribution matrix must not contain negative values.", IGRAPH_EINVAL); + } + if (igraph_is_nan(lo)) { + IGRAPH_ERROR("The type distribution matrix must not contain NaN.", IGRAPH_EINVAL); + } + } + + if (igraph_matrix_nrow(pref_matrix) != out_types || + igraph_matrix_ncol(pref_matrix) != in_types) { + IGRAPH_ERROR("The preference matrix must have dimensions out_types * in_types.", IGRAPH_EINVAL); + } + + { + igraph_real_t lo, hi; + igraph_matrix_minmax(pref_matrix, &lo, &hi); + + if (lo < 0 || hi > 1) { + IGRAPH_ERROR("The preference matrix must contain probabilities in [0, 1].", IGRAPH_EINVAL); + } + if (igraph_is_nan(lo) || igraph_is_nan(hi)) { + IGRAPH_ERROR("The preference matrix must not contain NaN.", IGRAPH_EINVAL); + } + } + + IGRAPH_VECTOR_INIT_FINALLY(&cumdist, in_types * out_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("Insufficient memory for asymmetric_preference_game.", 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("Insufficient memory for asymmetric_preference_game.", IGRAPH_ENOMEM); + } + IGRAPH_VECTOR_INIT_FINALLY(nodetypes_out, nodes); + } + + IGRAPH_CHECK(igraph_vector_ptr_init(&vids_by_intype, in_types)); + IGRAPH_FINALLY(igraph_vector_ptr_destroy_all, &vids_by_intype); + IGRAPH_CHECK(igraph_vector_ptr_init(&vids_by_outtype, out_types)); + IGRAPH_FINALLY(igraph_vector_ptr_destroy_all, &vids_by_outtype); + for (i = 0; i < in_types; i++) { + VECTOR(vids_by_intype)[i] = IGRAPH_CALLOC(1, igraph_vector_t); + if (! VECTOR(vids_by_intype)[i]) { + IGRAPH_ERROR("Insufficient memory for asymmetric_preference_game.", IGRAPH_ENOMEM); + } + IGRAPH_CHECK(igraph_vector_init(VECTOR(vids_by_intype)[i], 0)); + } + for (i = 0; i < out_types; i++) { + VECTOR(vids_by_outtype)[i] = IGRAPH_CALLOC(1, igraph_vector_t); + if (! VECTOR(vids_by_outtype)[i]) { + IGRAPH_ERROR("Insufficient memory for asymmetric_preference_game.", IGRAPH_ENOMEM); + } + 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 < out_types; i++) { + for (j = 0; j < in_types; j++, k++) { + VECTOR(cumdist)[k + 1] = VECTOR(cumdist)[k] + MATRIX(*type_dist_matrix, i, j); + } + } + } else { + for (i = 0; i < out_types * in_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) % (long int) out_types; + type1 = (type1 - 1) / (long int) out_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 < out_types; i++) { + for (j = 0; j < in_types; j++) { + long int kk, l, c = 0; + 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 IGRAPH_SUCCESS; +} diff --git a/src/rigraph/core/games/recent_degree.c b/src/rigraph/core/games/recent_degree.c new file mode 100644 index 0000000..188bb3e --- /dev/null +++ b/src/rigraph/core/games/recent_degree.c @@ -0,0 +1,372 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2003-2021 The igraph development 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., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_games.h" + +#include "igraph_constructors.h" +#include "igraph_dqueue.h" +#include "igraph_psumtree.h" +#include "igraph_random.h" +#include "igraph_interface.h" + +/** + * \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 nodes 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 time_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. In 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. + * For most applications it is reasonable to set it to false. + * \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 nodes, + igraph_real_t power, + igraph_integer_t time_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 = nodes; + long int no_of_neighbors = 0; + 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; + igraph_dqueue_t history; + igraph_bool_t have_outseq = outseq && igraph_vector_size(outseq) > 0; + + if (no_of_nodes < 0) { + IGRAPH_ERRORF("Number of vertices cannot be negative, got %ld.", IGRAPH_EINVAL, no_of_nodes); + } + if (have_outseq && igraph_vector_size(outseq) != no_of_nodes) { + IGRAPH_ERRORF("Out-degree sequence is specified, but its length (%ld) does not equal the number of nodes (%ld).", + IGRAPH_EINVAL, (long) igraph_vector_size(outseq), no_of_nodes); + } + if (!have_outseq && m < 0) { + IGRAPH_ERRORF("Numer of edges per step cannot be negative, got %" IGRAPH_PRId ".", + IGRAPH_EINVAL, m); + } + if (time_window < 0) { + IGRAPH_ERRORF("Time window cannot be negative, got %" IGRAPH_PRId ".", IGRAPH_EINVAL, time_window); + } + if (zero_appeal < 0) { + IGRAPH_ERRORF("The zero appeal cannot be negative, got %g.", IGRAPH_EINVAL, zero_appeal); + } + + if (nodes == 0) { + igraph_empty(graph, 0, directed); + return IGRAPH_SUCCESS; + } + + if (!have_outseq) { + no_of_neighbors = m; + no_of_edges = (no_of_nodes - 1) * no_of_neighbors; + } else { + long int outseq_len = igraph_vector_size(outseq); + no_of_edges = 0; + for (i = 1; i < outseq_len; 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(°ree, no_of_nodes); + IGRAPH_CHECK(igraph_dqueue_init(&history, + 1.5 * time_window * no_of_edges / no_of_nodes + 10)); + IGRAPH_FINALLY(igraph_dqueue_destroy, &history); + + RNG_BEGIN(); + + /* first node */ + IGRAPH_CHECK(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 (have_outseq) { + 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_CHECK(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_CHECK(igraph_psumtree_update(&sumtree, nn, pow(VECTOR(degree)[nn], power) + zero_appeal)); + } + if (outpref) { + VECTOR(degree)[i] += no_of_neighbors; + IGRAPH_CHECK(igraph_psumtree_update(&sumtree, i, pow(VECTOR(degree)[i], power) + zero_appeal)); + } else { + IGRAPH_CHECK(igraph_psumtree_update(&sumtree, i, zero_appeal)); + } + } + + RNG_END(); + + igraph_dqueue_destroy(&history); + igraph_psumtree_destroy(&sumtree); + igraph_vector_destroy(°ree); + 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_aging_game + * \brief Preferential attachment based on the number of edges gained recently, with aging of vertices. + * + * + * 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. + * + * 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_bins Integer constant, the number of age bins to use. + * \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_bins)*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_bins, + 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; + long int binwidth; + 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; + igraph_bool_t have_outseq = outseq && igraph_vector_size(outseq) > 0; + + if (no_of_nodes == 0) { + igraph_empty(graph, 0, directed); + return IGRAPH_SUCCESS; + } + if (no_of_nodes < 0) { + IGRAPH_ERRORF("Number of nodes should not be negative, got %ld.", IGRAPH_EINVAL, no_of_nodes); + } + if (have_outseq && igraph_vector_size(outseq) != no_of_nodes) { + IGRAPH_ERRORF("Out-degree sequence is specified, but its length (%ld) does not equal the number of nodes (%ld).", + IGRAPH_EINVAL, (long) igraph_vector_size(outseq), no_of_nodes); + } + if (!have_outseq && m < 0) { + IGRAPH_ERRORF("Numer of edges per step cannot be negative, got %" IGRAPH_PRId ".", IGRAPH_EINVAL, m); + } + if (aging_bins <= 0) { + IGRAPH_ERRORF("Aging bins should be positive, got %" IGRAPH_PRId ".", IGRAPH_EINVAL, aging_bins); + } + if (time_window < 0) { + IGRAPH_ERRORF("Time window cannot be negative, got %" IGRAPH_PRId ".", IGRAPH_EINVAL, time_window); + } + if (zero_appeal < 0) { + IGRAPH_ERRORF("The zero appeal cannot be negative, got %g.", IGRAPH_EINVAL, zero_appeal); + } + + if (!have_outseq) { + no_of_neighbors = m; + no_of_edges = (no_of_nodes - 1) * no_of_neighbors; + } else { + long int outseq_len = igraph_vector_size(outseq); + no_of_edges = 0; + for (i = 1; i < outseq_len; i++) { + no_of_edges += VECTOR(*outseq)[i]; + } + } + + binwidth = nodes / aging_bins + 1; + + 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(°ree, no_of_nodes); + IGRAPH_CHECK(igraph_dqueue_init(&history, + 1.5 * time_window * no_of_edges / no_of_nodes + 10)); + IGRAPH_FINALLY(igraph_dqueue_destroy, &history); + + RNG_BEGIN(); + + /* first node */ + IGRAPH_CHECK(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 (have_outseq) { + 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_CHECK(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_CHECK(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_CHECK(igraph_psumtree_update( + &sumtree, i, + pow(VECTOR(degree)[i], pa_exp) + zero_appeal + )); + } else { + IGRAPH_CHECK(igraph_psumtree_update(&sumtree, i, zero_appeal)); + } + + /* aging */ + for (k = 1; binwidth * k <= i; k++) { + long int shnode = i - binwidth * k; + long int deg = (long int) VECTOR(degree)[shnode]; + long int age = (i - shnode) / binwidth; + IGRAPH_CHECK(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(°ree); + 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; +} diff --git a/src/rigraph/core/games/sbm.c b/src/rigraph/core/games/sbm.c new file mode 100644 index 0000000..b2d75e8 --- /dev/null +++ b/src/rigraph/core/games/sbm.c @@ -0,0 +1,621 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph R library. + Copyright (C) 2003-2013 Gabor Csardi + 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 "core/interruption.h" + +#include /* for DBL_EPSILON */ +#include /* for sqrt */ + +/** + * \function igraph_sbm_game + * \brief 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., & Wasserman, S. (1992a). Blockmodels: + * Interpretation and evaluation. Social Networks, 14, 5-–61. + * + * + * The order of the vertex ids in the generated graph corresponds to + * the \p block_sizes argument. + * + * \param graph The output graph. This should be a pointer to an + * uninitialized 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) { + + long int no_blocks = igraph_matrix_nrow(pref_matrix); + long 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); + } + + if (no_blocks > 0) { + igraph_matrix_minmax(pref_matrix, &minp, &maxp); + if (minp < 0 || maxp > 1) { + IGRAPH_ERROR("Connection probabilities must be in [0,1].", 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_ERRORF("Block size vector length (%ld) does not agree with " + "preference matrix size (%ld).", IGRAPH_EINVAL, + igraph_vector_int_size(block_sizes), no_blocks); + } + + if (no_blocks > 0) { + if (igraph_vector_int_min(block_sizes) < 0) { + IGRAPH_ERRORF("Block sizes must be non-negative, but got %" IGRAPH_PRId ".", + IGRAPH_EINVAL, igraph_vector_int_min(block_sizes)); + } + } + + if (igraph_vector_int_sum(block_sizes) != n) { + IGRAPH_ERRORF("Sum of the block sizes (%" IGRAPH_PRId ") must equal the number of vertices (%" IGRAPH_PRId ").", + IGRAPH_EINVAL, igraph_vector_int_sum(block_sizes), n); + } + + /* Since the sum of the block sizes should equal the number of vertices, + * and the block sizes are non-negative, the number of vertices is + * guaranteed to be non-negative. This shouldn't be checked separately. + */ + + IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); + + RNG_BEGIN(); + + for (from = 0; from < no_blocks; from++) { + double fromsize = VECTOR(*block_sizes)[from]; + long int start = directed ? 0 : from; + long int i, tooff = 0; + + IGRAPH_ALLOW_INTERRUPTION(); + + 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) { + long int vto = floor(last / fromsize); + long 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) { + long int vto = floor(last / fromsize); + long 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) { + long int vto = floor(last / fromsize); + long 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) { + long int vto = floor(last / fromsize); + long 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) { + long int vto = floor(last / fromsize); + long 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) { + 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; + } + } + + tooff += tosize; + } + fromoff += fromsize; + } + + RNG_END(); + + igraph_create(graph, &edges, n, directed); + igraph_vector_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_hsbm_game + * \brief 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 + * \brief 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/src/rigraph/core/games/static_fitness.c b/src/rigraph/core/games/static_fitness.c new file mode 100644 index 0000000..8a7d4c0 --- /dev/null +++ b/src/rigraph/core/games/static_fitness.c @@ -0,0 +1,436 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2003-2021 The igraph development 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., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_games.h" + +#include "igraph_adjlist.h" +#include "igraph_conversion.h" +#include "igraph_constructors.h" +#include "igraph_interface.h" +#include "igraph_progress.h" +#include "igraph_random.h" + +#include "core/interruption.h" + +/** + * \ingroup generators + * \function igraph_static_fitness_game + * \brief 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. + * + * + * 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. + * + * + * 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. + * + * + * 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. + * + * + * 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, + const igraph_vector_t *fitness_out, const 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_ERRORF("Number of edges cannot be negative, got %" IGRAPH_PRId ".", IGRAPH_EINVAL, no_of_edges); + } + + no_of_nodes = (igraph_integer_t) 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. + * + * + * The game simply uses \ref igraph_static_fitness_game with appropriately + * constructed fitness vectors. In particular, the fitness of vertex i + * is i-alpha, where alpha = 1/(gamma-1) + * and gamma is the exponent given in the arguments. + * + * + * 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. + * + * + * 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)-alpha, + * 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. + * + * + * References: + * + * + * Goh K-I, Kahng B, Kim D: Universal behaviour of load distribution + * in scale-free networks. Phys Rev Lett 87(27):278701, 2001. + * + * + * Chung F and Lu L: Connected components in a random graph with given + * degree sequences. Annals of Combinatorics 6, 125-145, 2002. + * + * + * 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_ERRORF("Number of nodes cannot be negative, got %" IGRAPH_PRId".", IGRAPH_EINVAL, no_of_nodes); + } + + /* Calculate alpha_out */ + if (exponent_out < 2) { + IGRAPH_ERRORF("Out-degree exponent must be >= 2, got %g.", IGRAPH_EINVAL, exponent_out); + } 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_ERRORF("For directed graphs the in-degree exponent must be >= 2, got %g.", + IGRAPH_EINVAL, exponent_in); + } 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; +} diff --git a/src/rigraph/core/games/tree.c b/src/rigraph/core/games/tree.c new file mode 100644 index 0000000..805ad53 --- /dev/null +++ b/src/rigraph/core/games/tree.c @@ -0,0 +1,196 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2003-2021 The igraph development 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., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_games.h" + +#include "igraph_constructors.h" +#include "igraph_interface.h" +#include "igraph_random.h" + +/* 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 generates each labelled tree with the same probability. + * + * + * Note that for n=0, the null graph is returned, + * which is not considered to be a tree by \ref igraph_is_tree(). + * + * \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üfer sequences uniformly, 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/src/rigraph/core/games/watts_strogatz.c b/src/rigraph/core/games/watts_strogatz.c new file mode 100644 index 0000000..1a6e730 --- /dev/null +++ b/src/rigraph/core/games/watts_strogatz.c @@ -0,0 +1,102 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2003-2021 The igraph development 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., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_games.h" + +#include "igraph_constructors.h" +#include "igraph_interface.h" + +/** + * \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. + * + * See also: Duncan J Watts and Steven H Strogatz: + * Collective dynamics of small world 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, e.g. 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; +} diff --git a/src/rigraph/core/graph/adjlist.c b/src/rigraph/core/graph/adjlist.c new file mode 100644 index 0000000..e3da88d --- /dev/null +++ b/src/rigraph/core/graph/adjlist.c @@ -0,0 +1,1192 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2003-2012 Gabor Csardi + 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 "core/interruption.h" + +#include /* memset */ +#include + +/** + * Helper function that simplifies a sorted adjacency vector by removing + * duplicate elements and optionally self-loops. + */ +static int igraph_i_simplify_sorted_int_adjacency_vector_in_place( + igraph_vector_int_t *v, igraph_integer_t index, igraph_neimode_t mode, + igraph_loops_t loops, igraph_multiple_t multiple +); + +/** + * Helper function that removes loops from an incidence vector (either both + * occurrences or only one of them). + */ +static int igraph_i_remove_loops_from_incidence_vector_in_place( + igraph_vector_int_t *v, const igraph_t *graph, igraph_loops_t loops +); + +/** + * \section about_adjlists + * + * 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 all pairs of vertices or calculating closeness centrality + * for all the vertices. + * + * The igraph_adjlist_t 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. + * + * 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 the number of + * vertices), but you don't know how many vertices you will visit + * during the computation. + * + * + * + * \example examples/simple/adjlist.c + * + */ + +/** + * \function igraph_adjlist_init + * \brief Constructs an adjacency list of vertices from a given graph. + * + * Creates 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 igraph_adjlist_t object. + * \param mode Constant specifying whether outgoing + * (IGRAPH_OUT), incoming (IGRAPH_IN), + * or both (IGRAPH_ALL) types of neighbors to include + * in the adjacency list. It is ignored for undirected networks. + * \param loops Specifies how to treat loop edges. IGRAPH_NO_LOOPS + * removes loop edges from the adjacency list. IGRAPH_LOOPS_ONCE + * makes each loop edge appear only once in the adjacency list of the + * corresponding vertex. IGRAPH_LOOPS_TWICE makes loop edges + * appear \em twice in the adjacency list of the corresponding vertex, + * but only if the graph is undirected or mode is set to + * IGRAPH_ALL. + * \param multiple Specifies how to treat multiple (parallel) edges. + * IGRAPH_NO_MULTIPLE collapses parallel edges into a single one; + * IGRAPH_MULTIPLE keeps the multiplicities of parallel edges + * so the same vertex will appear as many times in the adjacency list of + * another vertex as the number of parallel edges going between the two + * vertices. + * \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_loops_t loops, + igraph_multiple_t multiple) { + igraph_integer_t i; + igraph_vector_t tmp; + int j, n; + + if (mode != IGRAPH_IN && mode != IGRAPH_OUT && mode != IGRAPH_ALL) { + IGRAPH_ERROR("Cannot create adjacency list 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 adjacency list view", IGRAPH_ENOMEM); + } + + IGRAPH_FINALLY(igraph_adjlist_destroy, al); + + for (i = 0; i < al->length; i++) { + 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_CHECK(igraph_i_simplify_sorted_int_adjacency_vector_in_place( + &al->adjs[i], i, mode, loops, multiple + )); + } + + igraph_vector_destroy(&tmp); + IGRAPH_FINALLY_CLEAN(2); + + return 0; +} + +/** + * \function igraph_adjlist_init_empty + * \brief Initializes 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 igraph_adjlist_t 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 + * \brief 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 + * (IGRAPH_OUT), incoming (IGRAPH_IN), + * or both (IGRAPH_ALL) 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 + * \brief Deallocates an adjacency list. + * + * 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++) { + 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 + * \brief Returns the number of vertices in an adjacency list. + * + * \param al The adjacency list. + * \return The number of vertices in the adjacency list. + * + * Time complexity: O(1). + */ +igraph_integer_t igraph_adjlist_size(const igraph_adjlist_t *al) { + return al->length; +} + +/** + * \function igraph_adjlist_sort + * \brief Sorts 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 + * \brief Simplifies an adjacency list. + * + * Simplifies an adjacency list, i.e. removes 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, j, l, n, p; + igraph_vector_int_t *v; + + IGRAPH_WARNING( + "igraph_adjlist_remove_duplicate() is deprecated; use the constructor " + "arguments of igraph_adjlist_init() to specify whether you want loop " + "edges to appear once or twice in the adjacency list." + ); + + IGRAPH_UNUSED(graph); + + n = al->length; + for (i = 0; i < n; i++) { + v = &al->adjs[i]; + l = igraph_vector_int_size(v); + if (l > 0) { + p = 1; + 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 assume 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; + +} + +static int igraph_i_remove_loops_from_incidence_vector_in_place( + igraph_vector_int_t *v, const igraph_t *graph, igraph_loops_t loops +) { + long int i, length, eid, write_ptr; + igraph_vector_int_t *seen_loops = 0; + + /* In this function we make use of the fact that we are dealing with + * _incidence_ lists, and the only way for an edge ID to appear twice + * within an incidence list is if the edge is a loop edge; otherwise each + * element will be unique. + * + * Note that incidence vectors are not sorted by edge ID, so we need to + * look up the edges in the graph to decide whether they are loops or not. + * + * Also, it may be tempting to introduce a boolean in case of IGRAPH_LOOPS_ONCE, + * and flip it every time we see a loop to get rid of half of the occurrences, + * but the problem is that even if the same loop edge ID appears twice in + * the input list, they are not guaranteed to be next to each other; it + * may be the case that there are multiple loop edges, each edge appears + * twice, and we want to keep exactly one of them for each ID. That's why + * we have a "seen_loops" vector. + */ + + if (loops == IGRAPH_LOOPS_TWICE) { + /* Loop edges appear twice by default, nothing to do. */ + return IGRAPH_SUCCESS; + } + + length = igraph_vector_int_size(v); + if (length == 0) { + return IGRAPH_SUCCESS; + } + + if (loops == IGRAPH_LOOPS_ONCE) { + /* We need a helper vector */ + seen_loops = IGRAPH_CALLOC(1, igraph_vector_int_t); + IGRAPH_FINALLY(igraph_free, seen_loops); + IGRAPH_CHECK(igraph_vector_int_init(seen_loops, 0)); + IGRAPH_FINALLY(igraph_vector_int_destroy, seen_loops); + } else if (loops != IGRAPH_NO_LOOPS) { + IGRAPH_ERROR("Invalid value for 'loops' argument", IGRAPH_EINVAL); + } + + for (i = 0, write_ptr = 0; i < length; i++) { + eid = VECTOR(*v)[i]; + if (IGRAPH_FROM(graph, eid) == IGRAPH_TO(graph, eid)) { + /* Loop edge */ + if (seen_loops && !igraph_vector_int_contains(seen_loops, eid)) { + VECTOR(*v)[write_ptr++] = eid; + IGRAPH_CHECK(igraph_vector_int_push_back(seen_loops, eid)); + } + } else { + /* Not a loop edge */ + VECTOR(*v)[write_ptr++] = eid; + } + } + + /* Always succeeds since we never grow the vector */ + igraph_vector_int_resize(v, write_ptr); + + /* Destroy the helper vector */ + if (seen_loops) { + igraph_vector_int_destroy(seen_loops); + IGRAPH_FREE(seen_loops); + IGRAPH_FINALLY_CLEAN(2); + } + + return IGRAPH_SUCCESS; +} + +int igraph_inclist_remove_duplicate(const igraph_t *graph, igraph_inclist_t *il) { + long int i, n; + + IGRAPH_WARNING( + "igraph_inclist_remove_duplicate() is deprecated; use the constructor " + "arguments of igraph_inclist_init() to specify whether you want loop " + "edges to appear once or twice in the incidence list." + ); + + IGRAPH_UNUSED(graph); + + n = il->length; + for (i = 0; i < n; i++) { + IGRAPH_CHECK( + igraph_i_remove_loops_from_incidence_vector_in_place( + &il->incs[i], graph, IGRAPH_LOOPS_ONCE + ) + ); + } + + 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 + * \brief Initializes an incidence list. + * + * Creates 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. + * + * + * When \p mode is \c IGRAPH_IN or \c IGRAPH_OUT, each edge ID will appear + * in the incidence list \em once. When \p mode is \c IGRAPH_ALL, each edge ID + * will appear in the incidence list \em twice, once for the source vertex + * and once for the target edge. It also means that the edge IDs of loop edges + * may potentially appear \em twice for the \em same vertex. Use the \p loops + * argument to control whether this will be the case (\c IGRAPH_LOOPS_TWICE ) + * or not (\c IGRAPH_LOOPS_ONCE or \c IGRAPH_NO_LOOPS). + * + * \param graph The input graph. + * \param il Pointer to an uninitialized incidence list. + * \param mode Constant specifying whether incoming edges + * (IGRAPH_IN), outgoing edges (IGRAPH_OUT) or + * both (IGRAPH_ALL) to include in the incidence lists + * of directed graphs. It is ignored for undirected graphs. + * \param loops Specifies how to treat loop edges. IGRAPH_NO_LOOPS + * removes loop edges from the incidence list. IGRAPH_LOOPS_ONCE + * makes each loop edge appear only once in the incidence list of the + * corresponding vertex. IGRAPH_LOOPS_TWICE makes loop edges + * appear \em twice in the incidence list of the corresponding vertex, + * but only if the graph is undirected or mode is set to + * IGRAPH_ALL. + * \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_loops_t loops) { + 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]; + } + + if (loops != IGRAPH_LOOPS_TWICE) { + IGRAPH_CHECK( + igraph_i_remove_loops_from_incidence_vector_in_place(&il->incs[i], graph, loops) + ); + } + } + + igraph_vector_destroy(&tmp); + IGRAPH_FINALLY_CLEAN(2); + return 0; +} + +/** + * \function igraph_inclist_init_empty + * \brief Initializes 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 + * \brief 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 + * \brief 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_inclist_size + * \brief Returns the number of vertices in an incidence list. + * + * \param il The incidence list. + * \return The number of vertices in the incidence list. + * + * Time complexity: O(1). + */ +igraph_integer_t igraph_inclist_size(const igraph_inclist_t *il) { + return il->length; +} + +static int igraph_i_simplify_sorted_int_adjacency_vector_in_place( + igraph_vector_int_t *v, igraph_integer_t index, igraph_neimode_t mode, + igraph_loops_t loops, igraph_multiple_t multiple +) { + long int i, p = 0; + long int n = igraph_vector_int_size(v); + + if ( + multiple == IGRAPH_MULTIPLE && + ( + loops == IGRAPH_LOOPS_TWICE || + (loops == IGRAPH_LOOPS_ONCE && (mode == IGRAPH_IN || mode == IGRAPH_OUT)) + ) + ) { + /* nothing to simplify */ + return IGRAPH_SUCCESS; + } + + if (loops == IGRAPH_NO_LOOPS) { + if (multiple == IGRAPH_NO_MULTIPLE) { + /* We need to get rid of loops and multiple edges completely */ + for (i = 0; i < n; i++) { + if (VECTOR(*v)[i] != index && + (i == n - 1 || VECTOR(*v)[i + 1] != VECTOR(*v)[i])) { + VECTOR(*v)[p] = VECTOR(*v)[i]; + p++; + } + } + } else { + /* We need to get rid of loops but keep multiple edges */ + for (i = 0; i < n; i++) { + if (VECTOR(*v)[i] != index) { + VECTOR(*v)[p] = VECTOR(*v)[i]; + p++; + } + } + } + } else if (loops == IGRAPH_LOOPS_ONCE) { + if (multiple == IGRAPH_NO_MULTIPLE) { + /* We need to get rid of multiple edges completely (including + * multiple loop edges), but keep one edge from each loop edge */ + /* TODO(ntamas): think this through! */ + for (i = 0; i < n; i++) { + if (i == n - 1 || VECTOR(*v)[i + 1] != VECTOR(*v)[i]) { + VECTOR(*v)[p] = VECTOR(*v)[i]; + p++; + } + } + } else { + /* We need to keep one edge from each loop edge and we don't need to + * touch multiple edges. Note that we can get here only if + * mode == IGRAPH_ALL; if mode was IGRAPH_IN or IGRAPH_OUT, we would + * have bailed out earlier */ + for (i = 0; i < n; i++) { + VECTOR(*v)[p] = VECTOR(*v)[i]; + if (VECTOR(*v)[i] == index) { + /* this was a loop edge so if the next element is the same, we + * need to skip that */ + if (i < n-1 && VECTOR(*v)[i + 1] == index) { + i++; + } + } + p++; + } + } + } else if (loops == IGRAPH_LOOPS_TWICE && multiple == IGRAPH_NO_MULTIPLE) { + /* We need to get rid of multiple edges completely (including + * multiple loop edges), but keep both edge from each loop edge */ + /* TODO(ntamas): think this through! */ + for (i = 0; i < n; i++) { + if (i == n - 1 || VECTOR(*v)[i + 1] != VECTOR(*v)[i]) { + VECTOR(*v)[p] = VECTOR(*v)[i]; + p++; + } else { + /* Current item is the same as the next one, but if it is a + * loop edge, then the first one or two items are okay. We need + * to keep one if mode == IGRAPH_IN or mode == IGRAPH_OUT, + * otherwise we need to keep two */ + if (VECTOR(*v)[i] == index) { + VECTOR(*v)[p] = VECTOR(*v)[i]; + p++; + if (mode == IGRAPH_ALL) { + VECTOR(*v)[p] = VECTOR(*v)[i]; + p++; + } + /* skip over all the items corresponding to the loop edges */ + while (i < n && VECTOR(*v)[i] == index) { + i++; + } + i--; /* because the for loop also increases i by 1 */ + } + } + } + } else { + /* TODO; we don't use this combination yet */ + return IGRAPH_UNIMPLEMENTED; + } + + /* always succeeds since we are never growing the vector */ + igraph_vector_int_resize(v, p); + + return IGRAPH_SUCCESS; +} + + +/** + * \function igraph_lazy_adjlist_init + * \brief Initialized a lazy adjacency list. + * + * 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 + * (IGRAPH_IN), outgoing edges + * (IGRPAH_OUT) or both types of edges + * (IGRAPH_ALL) are considered. It is ignored for + * undirected graphs. + * \param simplify Constant, it gives whether to simplify the vectors + * in the adjacency list (IGRAPH_SIMPLIFY) or not + * (IGRAPH_DONT_SIMPLIFY). + * \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_loops_t loops, + igraph_multiple_t multiple) { + if (mode != IGRAPH_IN && mode != IGRAPH_OUT && mode != IGRAPH_ALL) { + IGRAPH_ERROR("Cannor create lazy adjacency list view", IGRAPH_EINVMODE); + } + + if (!igraph_is_directed(graph)) { + mode = IGRAPH_ALL; + } + + al->mode = mode; + al->loops = loops; + al->multiple = multiple; + al->graph = graph; + + al->length = igraph_vcount(graph); + al->adjs = IGRAPH_CALLOC(al->length, igraph_vector_int_t*); + + if (al->adjs == 0) { + IGRAPH_ERROR("Cannot create lazy adjacency list view", IGRAPH_ENOMEM); + } + + IGRAPH_FINALLY(igraph_free, al->adjs); + + IGRAPH_CHECK(igraph_vector_init(&al->dummy, 0)); + IGRAPH_FINALLY_CLEAN(1); + + return 0; +} + +/** + * \function igraph_lazy_adjlist_destroy + * \brief Deallocate a lazt adjacency list. + * + * 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_vector_destroy(&al->dummy); + IGRAPH_FREE(al->adjs); +} + +/** + * \function igraph_lazy_adjlist_clear + * \brief 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_int_destroy(al->adjs[i]); + IGRAPH_FREE(al->adjs[i]); + } + } +} + +/** + * \function igraph_lazy_adjlist_size + * \brief Returns the number of vertices in a lazy adjacency list. + * + * \param al The lazy adjacency list. + * \return The number of vertices in the lazy adjacency list. + * + * Time complexity: O(1). + */ +igraph_integer_t igraph_lazy_adjlist_size(const igraph_lazy_adjlist_t *al) { + return al->length; +} + +igraph_vector_int_t *igraph_i_lazy_adjlist_get_real(igraph_lazy_adjlist_t *al, + igraph_integer_t pno) { + igraph_integer_t no = pno; + long int i, n; + int ret; + + if (al->adjs[no] == 0) { + ret = igraph_neighbors(al->graph, &al->dummy, no, al->mode); + if (ret != 0) { + igraph_error("", IGRAPH_FILE_BASENAME, __LINE__, ret); + return 0; + } + + al->adjs[no] = IGRAPH_CALLOC(1, igraph_vector_int_t); + if (al->adjs[no] == 0) { + igraph_error("Lazy adjlist failed", IGRAPH_FILE_BASENAME, __LINE__, + IGRAPH_ENOMEM); + return 0; + } + + n = igraph_vector_size(&al->dummy); + ret = igraph_vector_int_init(al->adjs[no], n); + if (ret != 0) { + IGRAPH_FREE(al->adjs[no]); + igraph_error("", IGRAPH_FILE_BASENAME, __LINE__, ret); + return 0; + } + + for (i = 0; i < n; i++) { + VECTOR(*al->adjs[no])[i] = VECTOR(al->dummy)[i]; + } + + ret = igraph_i_simplify_sorted_int_adjacency_vector_in_place( + al->adjs[no], no, al->mode, al->loops, al->multiple + ); + if (ret != 0) { + igraph_vector_int_destroy(al->adjs[no]); + IGRAPH_FREE(al->adjs[no]); + igraph_error("", IGRAPH_FILE_BASENAME, __LINE__, ret); + return 0; + } + } + + return al->adjs[no]; +} + +/** + * \function igraph_lazy_inclist_init + * \brief 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. + * + * + * When \p mode is \c IGRAPH_IN or \c IGRAPH_OUT, each edge ID will appear + * in the incidence list \em once. When \p mode is \c IGRAPH_ALL, each edge ID + * will appear in the incidence list \em twice, once for the source vertex + * and once for the target edge. It also means that the edge IDs of loop edges + * will appear \em twice for the \em same vertex. + * + * \param graph The input graph. + * \param al Pointer to an uninitialized incidence list. + * \param mode Constant, it gives whether incoming edges + * (IGRAPH_IN), outgoing edges + * (IGRAPH_OUT) or both types of edges + * (IGRAPH_ALL) are considered. It is ignored for + * undirected graphs. + * \param loops Specifies how to treat loop edges. IGRAPH_NO_LOOPS + * removes loop edges from the incidence list. IGRAPH_LOOPS_ONCE + * makes each loop edge appear only once in the incidence list of the + * corresponding vertex. IGRAPH_LOOPS_TWICE makes loop edges + * appear \em twice in the incidence list of the corresponding vertex, + * but only if the graph is undirected or mode is set to + * IGRAPH_ALL. + * \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 *il, + igraph_neimode_t mode, + igraph_loops_t loops) { + + 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; + } + + il->graph = graph; + il->loops = loops; + il->mode = mode; + + il->length = igraph_vcount(graph); + il->incs = IGRAPH_CALLOC(il->length, igraph_vector_int_t*); + if (il->incs == 0) { + + IGRAPH_ERROR("Cannot create lazy incidence list view", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, il->incs); + + IGRAPH_CHECK(igraph_vector_init(&il->dummy, 0)); + IGRAPH_FINALLY_CLEAN(1); + + return 0; + +} + +/** + * \function igraph_lazy_inclist_destroy + * \brief Deallocates a lazy incidence list. + * + * 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_vector_destroy(&il->dummy); + IGRAPH_FREE(il->incs); +} + +/** + * \function igraph_lazy_inclist_clear + * \brief 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_int_destroy(il->incs[i]); + IGRAPH_FREE(il->incs[i]); + } + } +} + +/** + * \function igraph_lazy_inclist_size + * \brief Returns the number of vertices in a lazy incidence list. + * + * \param il The lazy incidence list. + * \return The number of vertices in the lazy incidence list. + * + * Time complexity: O(1). + */ +igraph_integer_t igraph_lazy_inclist_size(const igraph_lazy_inclist_t *il) { + return il->length; +} + +igraph_vector_int_t *igraph_i_lazy_inclist_get_real(igraph_lazy_inclist_t *il, + igraph_integer_t pno) { + igraph_integer_t no = pno; + int ret; + long int i, n; + + if (il->incs[no] == 0) { + ret = igraph_incident(il->graph, &il->dummy, no, il->mode); + if (ret != 0) { + igraph_error("", IGRAPH_FILE_BASENAME, __LINE__, ret); + return 0; + } + + il->incs[no] = IGRAPH_CALLOC(1, igraph_vector_int_t); + if (il->incs[no] == 0) { + igraph_error("Lazy incidence list query failed", IGRAPH_FILE_BASENAME, __LINE__, + IGRAPH_ENOMEM); + return 0; + } + + n = igraph_vector_size(&il->dummy); + ret = igraph_vector_int_init(il->incs[no], n); + if (ret != 0) { + IGRAPH_FREE(il->incs[no]); + igraph_error("", IGRAPH_FILE_BASENAME, __LINE__, ret); + return 0; + } + + for (i = 0; i < n; i++) { + VECTOR(*il->incs[no])[i] = VECTOR(il->dummy)[i]; + } + + if (il->loops != IGRAPH_LOOPS_TWICE) { + ret = igraph_i_remove_loops_from_incidence_vector_in_place(il->incs[no], il->graph, il->loops); + if (ret != 0) { + igraph_vector_int_destroy(il->incs[no]); + IGRAPH_FREE(il->incs[no]); + return 0; + } + } + } + + return il->incs[no]; +} diff --git a/src/rigraph/core/graph/attributes.c b/src/rigraph/core/graph/attributes.c new file mode 100644 index 0000000..ba40189 --- /dev/null +++ b/src/rigraph/core/graph/attributes.c @@ -0,0 +1,498 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2005-2012 Gabor Csardi + 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 "graph/attributes.h" + +#include "config.h" + +#include +#include + +/* 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_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_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_attribute_table_t * +igraph_i_set_attribute_table(const igraph_attribute_table_t * table) { + IGRAPH_WARNING("igraph_i_set_attribute_table is deprecated, use igraph_set_attribute_table."); + return igraph_set_attribute_table(table); +} + +igraph_bool_t igraph_has_attribute_table() { + return igraph_i_attribute_table != 0; +} + + +/** + * \function igraph_attribute_combination_init + * \brief Initialize attribute combination list. + * + * \param comb The uninitialized attribute combination list. + * \return Error code. + * + * Time complexity: O(1) + */ +int igraph_attribute_combination_init(igraph_attribute_combination_t *comb) { + IGRAPH_CHECK(igraph_vector_ptr_init(&comb->list, 0)); + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_attribute_combination_destroy + * \brief Destroy attribute combination list. + * + * \param comb The attribute combination list. + * + * Time complexity: O(n), where n is the number of records in the + attribute combination list. + */ +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); +} + +/** + * \function igraph_attribute_combination_add + * \brief Add combination record to attribute combination list. + * + * \param comb The attribute combination list. + * \param name The name of the attribute. If the name already exists + * the attribute combination record will be replaced. + * Use NULL to add a default combination record for all + * atributes not in the list. + * \param type The type of the attribute combination. See \ref + * igraph_attribute_combination_type_t for the options. + * \param func Function to be used if \p type is + * \c IGRAPH_ATTRIBUTE_COMBINE_FUNCTION. + * \return Error code. + * + * Time complexity: O(n), where n is the number of current attribute + * combinations. + */ +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 = NULL; + } else { + rec->name = strdup(name); + } + rec->type = type; + rec->func = func; + + IGRAPH_CHECK(igraph_vector_ptr_push_back(&comb->list, rec)); + + } + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_attribute_combination_remove + * \brief Remove a record from an attribute combination list. + * + * \param comb The attribute combination list. + * \param name The attribute name of the attribute combination record + * to remove. It will be ignored if the named attribute + * does not exist. It can be NULL to remove the default + * combination record. + * \return Error code. This currently always returns IGRAPH_SUCCESS. + * + * Time complexity: O(n), where n is the number of records in the attribute + combination list. + */ +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 IGRAPH_SUCCESS; +} + +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) { + func = va_arg(ap, igraph_function_pointer_t); + } + + if (strlen(name) == 0) { + name = 0; + } + + IGRAPH_CHECK(igraph_attribute_combination_add(comb, name, type, func)); + } + + va_end(ap); + + return 0; +} diff --git a/src/rigraph/core/graph/attributes.h b/src/rigraph/core/graph/attributes.h new file mode 100644 index 0000000..d0c8426 --- /dev/null +++ b/src/rigraph/core/graph/attributes.h @@ -0,0 +1,112 @@ +/* + IGraph library. + Copyright (C) 2021 The igraph development 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, see . +*/ + +#ifndef IGRAPH_GRAPH_ATTRIBUTES_H +#define IGRAPH_GRAPH_ATTRIBUTES_H + +#include "igraph_attributes.h" +#include "igraph_strvector.h" +#include "igraph_types.h" +#include "igraph_vector_ptr.h" + +#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); + +#endif /* IGRAPH_GRAPH_ATTRIBUTES_H */ diff --git a/src/rigraph/core/graph/basic_query.c b/src/rigraph/core/graph/basic_query.c new file mode 100644 index 0000000..34c4ef4 --- /dev/null +++ b/src/rigraph/core/graph/basic_query.c @@ -0,0 +1,63 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2005-2012 Gabor Csardi + 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" + +/** + * \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. + * + * + * 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/src/rigraph/core/graph/cattributes.c b/src/rigraph/core/graph/cattributes.c new file mode 100644 index 0000000..515c691 --- /dev/null +++ b/src/rigraph/core/graph/cattributes.c @@ -0,0 +1,4241 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2005-2012 Gabor Csardi + 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 "core/math.h" +#include "igraph_interface.h" +#include "igraph_random.h" + +#include + +/* 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. */ + +static 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; + +static 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; +} + + +static 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; +} + +static 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 */ + +static 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. */ + +static 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; +} + +static 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 = -1; + 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; +} + +static 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); +} + +static 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); + +static 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; +} + +static 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; +} + +static 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; +} + +static 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; +} + +static 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; +} + +static 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; +} + +static 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; +} + +static 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; +} + +static 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; +} + +static 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; +} + +static 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; +} + +static 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; +} + +static 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; +} + +static 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; +} + +static 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; +} + +static 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; +} + +static 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; +} + +static 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; +} + +static 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; +} + +static 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; +} + +static 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; +} + + +static 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_i_cattribute_permute_free(new_val); + IGRAPH_FINALLY_CLEAN(3); + + 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; i0) { */ +/* newlen++; */ +/* } */ +/* } */ +/* for (i=0; itype; */ +/* 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; i0) { */ +/* newlen++; */ +/* } */ +/* } */ +/* for (i=0; itype; */ +/* 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"); */ +/* } */ +/* } */ +/* } */ + +static 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 = -1; + 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; i0) { */ +/* newlen++; */ +/* } */ +/* } */ +/* for (i=0; itype; */ +/* 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"); */ +/* } */ +/* } */ + +/* } */ + +static 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; +} + +static 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(3); + + return 0; +} + +static 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; +} + +static 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); +} + +static 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; +} + +static 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]; + if (rec->type != IGRAPH_ATTRIBUTE_NUMERIC) { + IGRAPH_ERROR("Numeric graph attribute expected.", IGRAPH_EINVAL); + } + num = (igraph_vector_t*)rec->value; + IGRAPH_CHECK(igraph_vector_resize(value, 1)); + VECTOR(*value)[0] = VECTOR(*num)[0]; + + return 0; +} + +static 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]; + if (rec->type != IGRAPH_ATTRIBUTE_BOOLEAN) { + IGRAPH_ERROR("Boolean graph attribute expected.", IGRAPH_EINVAL); + } + log = (igraph_vector_bool_t*)rec->value; + IGRAPH_CHECK(igraph_vector_bool_resize(value, 1)); + VECTOR(*value)[0] = VECTOR(*log)[0]; + + return 0; +} + +static 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]; + if (rec->type != IGRAPH_ATTRIBUTE_STRING) { + IGRAPH_ERROR("String graph attribute expected.", IGRAPH_EINVAL); + } + 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; +} + +static 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]; + if (rec->type != IGRAPH_ATTRIBUTE_NUMERIC) { + IGRAPH_ERROR("Numeric vertex attribute expected.", IGRAPH_EINVAL); + } + 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; +} + +static 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]; + if (rec->type != IGRAPH_ATTRIBUTE_BOOLEAN) { + IGRAPH_ERROR("Boolean vertex attribute expected.", IGRAPH_EINVAL); + } + 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; +} + +static 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]; + if (rec->type != IGRAPH_ATTRIBUTE_STRING) { + IGRAPH_ERROR("String vertex attribute expected.", IGRAPH_EINVAL); + } + 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; +} + +static 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]; + if (rec->type != IGRAPH_ATTRIBUTE_NUMERIC) { + IGRAPH_ERROR("Numeric edge attribute expected.", IGRAPH_EINVAL); + } + 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; +} + +static 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]; + if (rec->type != IGRAPH_ATTRIBUTE_STRING) { + IGRAPH_ERROR("String edge attribute expected.", IGRAPH_EINVAL); + } + 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; +} + +static 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]; + if (rec->type != IGRAPH_ATTRIBUTE_BOOLEAN) { + IGRAPH_ERROR("Boolean edge attribute expected.", IGRAPH_EINVAL); + } + 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 + * 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: + * + * igraph_set_attribute_table(&igraph_cattribute_table); + * + * + * 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 + * main() and never touch it again. (Detaching + * the attribute handler might lead to memory leaks.) + * + * 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. + * + * The C attribute handler supports attaching real numbers and + * character strings as attributes. No vectors are allowed, i.e. every + * vertex might have an attribute called name, but it is + * not possible to have a coords graph (or other) + * attribute which is a vector of numbers. + * + * \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", IGRAPH_FILE_BASENAME, __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", IGRAPH_FILE_BASENAME, __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 const 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", IGRAPH_FILE_BASENAME, __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", IGRAPH_FILE_BASENAME, __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", IGRAPH_FILE_BASENAME, __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", IGRAPH_FILE_BASENAME, __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", IGRAPH_FILE_BASENAME, __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", IGRAPH_FILE_BASENAME, __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", IGRAPH_FILE_BASENAME, __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_bool_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 edges. + * + * 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 edges. + * + * 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 edges. + * + * 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; +} + +static 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/src/rigraph/core/graph/iterators.c b/src/rigraph/core/graph/iterators.c new file mode 100644 index 0000000..5d85fa5 --- /dev/null +++ b/src/rigraph/core/graph/iterators.c @@ -0,0 +1,1932 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2005-2012 Gabor Csardi + 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_interface.h" + +#include +#include + +/** + * \section about_iterators About selectors, iterators + * + * Everything about vertices and vertex selectors also applies + * to edges and edge selectors unless explicitly noted otherwise. + * + * 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. + * + * 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. + * + * 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. + * + * 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). + */ + +/** + * \section about_vertex_selectors + * + * Vertex selectors are created by vertex selector constructors, + * can be instantiated with \ref igraph_vit_create(), and are + * destroyed with \ref igraph_vs_destroy(). + */ + +/** + * \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. + * + * + * 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 (e.g. 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 -1 after the + * last vertex id. The behavior of the function is undefined if you + * don't use a -1 properly. + * + * + * Note that the vertex ids supplied will be parsed as + * int'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 -1 + * 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 igraph_vss). + * + * \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. + * E.g. 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. + * + * + * 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. + * + * + * 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-edges 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_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). + */ + +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. + * + * + * 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. + * + * + * 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. + * + * + * 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. + * + * + * 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). + */ + +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 */ +} + +/** + * \function igraph_es_seq + * \brief Edge selector, a sequence of edge ids. + * + * All edge ids between from and to will be + * included in the edge selection. This includes from and + * excludes to. + * + * \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 v, + * 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; +} + +/** + * \function igraph_es_path + * \brief Edge selector, edge ids on a path. + * + * This function takes a vector of vertices and creates a selector of + * edges between those vertices. Vector {0, 3, 4, 7} will select edges + * (0 -> 3), (3 -> 4), (4 -> 7). If these edges don't exist then trying + * to create an iterator using this selector will fail. + * + * \param es Pointer to an uninitialized edge selector object. + * \param v Pointer to a vector of vertex id's along the path. + * \param directed If edge directions should be taken into account. This + * will be ignored if the graph to select from is undirected. + * \return Error code. + * \sa \ref igraph_es_destroy() + * + * Time complexity: O(n), the number of vertices. + */ +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. + * + * + * 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 es 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; +} + +/** + * \function igraph_es_as_vector + * \brief Transform edge selector into vector. + * + * + * Call this function on an edge selector to transform it into a vector. + * This is only implemented for sequence and vector selectors. If the + * edges do not exist in the graph, this will result in an error. + * + * \param graph Pointer to a graph to check if the edges in the selector exist. + * \param es An edge selector object. + * \param v Pointer to initialized vector. The result will be stored here. + * + * Time complexity: O(n), the number of edges in the selector. + */ +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; +} + +static int igraph_i_es_pairs_size(const igraph_t *graph, + const igraph_es_t *es, igraph_integer_t *result); +static int igraph_i_es_path_size(const igraph_t *graph, + const igraph_es_t *es, igraph_integer_t *result); +static 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); + } +} + +static 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; +} + +static 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; +} + +static 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); +} + +/**************************************************/ + +static int igraph_i_eit_create_allfromto(const igraph_t *graph, + igraph_eit_t *eit, + igraph_neimode_t mode); +static int igraph_i_eit_pairs(const igraph_t *graph, + igraph_es_t es, igraph_eit_t *eit); +static int igraph_i_eit_multipairs(const igraph_t *graph, + igraph_es_t es, igraph_eit_t *eit); +static int igraph_i_eit_path(const igraph_t *graph, + igraph_es_t es, igraph_eit_t *eit); + +static 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 no_of_edges = igraph_ecount(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, no_of_edges)); + + 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(no_of_edges, 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; +} + +static 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; +} + +static 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; +} + +static 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. + * + * + * This function creates an edge iterator based on an edge selector + * and a graph. + * + * + * 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_EINVAL); + } + 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_EINVAL); + } + 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; + if (eit->start < 0) { + IGRAPH_ERROR("Cannot create iterator, invalid edge id.", IGRAPH_EINVAL); + } + if (eit->end < 0) { + IGRAPH_ERROR("Cannot create iterator, invalid edge id.", IGRAPH_EINVAL); + } + if (eit->start >= igraph_ecount(graph)) { + IGRAPH_ERROR("Cannot create iterator, starting edge greater than number of edges.", IGRAPH_EINVAL); + } + 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/src/rigraph/core/graph/neighbors.h b/src/rigraph/core/graph/neighbors.h new file mode 100644 index 0000000..88255b3 --- /dev/null +++ b/src/rigraph/core/graph/neighbors.h @@ -0,0 +1,42 @@ +/* + IGraph library. + Copyright (C) 2021 The igraph development 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, see . +*/ + +#ifndef IGRAPH_NEIGHBORS_H +#define IGRAPH_NEIGHBORS_H + +#include "igraph_constants.h" + +__BEGIN_DECLS + +IGRAPH_PRIVATE_EXPORT int igraph_i_neighbors(const igraph_t *graph, + igraph_vector_t *neis, + igraph_integer_t pnode, + igraph_neimode_t mode, + igraph_loops_t loops, + igraph_multiple_t multiple); + +IGRAPH_PRIVATE_EXPORT int igraph_i_incident(const igraph_t *graph, + igraph_vector_t *eids, + igraph_integer_t pnode, + igraph_neimode_t mode, + igraph_loops_t loops, + igraph_multiple_t multiple); + +__END_DECLS + +#endif /* IGRAPH_NEIGHBORS_H */ diff --git a/src/rigraph/core/graph/type_indexededgelist.c b/src/rigraph/core/graph/type_indexededgelist.c new file mode 100644 index 0000000..b5c94e8 --- /dev/null +++ b/src/rigraph/core/graph/type_indexededgelist.c @@ -0,0 +1,1964 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2005-2012 Gabor Csardi + 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_memory.h" + +#include "graph/attributes.h" +#include "graph/neighbors.h" + +/* Internal functions */ + +static 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 + * + * This is the very minimal API in \a igraph. All the other + * functions use this minimal set for creating and manipulating + * graphs. + * + * This is a very important principle since it makes possible to + * implement other data representations by implementing only this + * minimal set. + */ + +/** + * \ingroup interface + * \function igraph_empty + * \brief Creates an empty graph with some vertices and no edges. + * + * + * 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. + * + * + * 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. + * + * + * This function should be called for every graph object exactly once. + * + * + * 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. + * + * + * 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. + * + * + * 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. + * + * + * The edges are given in a vector, the + * first two elements define the first edge (the order is + * from, to for directed + * graphs). The vector + * should contain even number of integer numbers between zero and the + * number of vertices in the graph minus one (inclusive). If you also + * want to add new vertices, call igraph_add_vertices() first. + * \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. + * + * + * 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. + * + * + * 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. + * + * + * The edges to remove are given as an edge selector. + * + * + * This function cannot remove vertices, they will be kept, even if + * they lose all their edges. + * + * + * 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. + * + * + * This function changes the ids of the vertices (except in some very + * special cases, but these should not be relied on anyway). + * + * + * 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) { + if (!igraph_is_directed(graph) || mode == IGRAPH_ALL) { + return igraph_i_neighbors(graph, neis, pnode, mode, IGRAPH_LOOPS_TWICE, IGRAPH_MULTIPLE); + } else { + return igraph_i_neighbors(graph, neis, pnode, mode, IGRAPH_LOOPS_ONCE, IGRAPH_MULTIPLE); + } +} + +int igraph_i_neighbors(const igraph_t *graph, igraph_vector_t *neis, igraph_integer_t pnode, + igraph_neimode_t mode, igraph_loops_t loops, igraph_multiple_t multiple) { +#define DEDUPLICATE_IF_NEEDED(vertex, n) \ + if (should_filter_duplicates) { \ + if ((loops == IGRAPH_NO_LOOPS && vertex == pnode) || \ + (loops == IGRAPH_LOOPS_ONCE && vertex == pnode && last_added == pnode) || \ + (multiple == IGRAPH_NO_MULTIPLE && vertex == last_added)) { \ + length -= n; \ + continue; \ + } else { \ + last_added = vertex; \ + } \ + } + + long int length = 0, idx = 0; + long int i, j; + + long int node = pnode; + igraph_real_t last_added = -1; + igraph_bool_t should_filter_duplicates; + + if (node < 0 || node > igraph_vcount(graph) - 1) { + IGRAPH_ERROR("Given vertex is not in the graph.", IGRAPH_EINVVID); + } + if (mode != IGRAPH_OUT && mode != IGRAPH_IN && + mode != IGRAPH_ALL) { + IGRAPH_ERROR("Mode should be either IGRAPH_OUT, IGRAPH_IN or IGRAPH_ALL.", IGRAPH_EINVMODE); + } + + if (!igraph_is_directed(graph)) { + mode = IGRAPH_ALL; + } + + if (mode != IGRAPH_ALL && loops == IGRAPH_LOOPS_TWICE) { + IGRAPH_ERROR("For a directed graph (with directions not ignored), " + "IGRAPH_LOOPS_TWICE does not make sense.\n", IGRAPH_EINVAL); + } + /* Calculate needed space first & allocate it */ + /* Note that 'mode' is treated as a bit field here; it's okay because + * IGRAPH_ALL = IGRAPH_IN | IGRAPH_OUT, bit-wise */ + 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)); + + /* The loops below produce an ordering what is consistent with the + * ordering returned by igraph_neighbors(), and this should be preserved. + * We are dealing with two sorted lists; one for the successors and one + * for the predecessors. If we have requested only one of them, we have + * an easy job. If we have requested both, we need to merge the two lists + * to ensure that the output is sorted by the vertex IDs of the "other" + * endpoint of the affected edges. We don't need to merge if the graph + * is undirected, because in that case the data structure guarantees that + * the "out-edges" contain only (u, v) pairs where u <= v and the + * "in-edges" contains the rest, so the result is sorted even without + * merging. */ + if (!igraph_is_directed(graph) || mode != IGRAPH_ALL) { + /* graph is undirected or we did not ask for both directions in a + * directed graph; this is the easy case */ + + should_filter_duplicates = !(multiple == IGRAPH_MULTIPLE && + ((!igraph_is_directed(graph) && loops == IGRAPH_LOOPS_TWICE) || + (igraph_is_directed(graph) && loops != IGRAPH_NO_LOOPS))); + + if (mode & IGRAPH_OUT) { + j = (long int) VECTOR(graph->os)[node + 1]; + for (i = (long int) VECTOR(graph->os)[node]; i < j; i++) { + igraph_real_t to = VECTOR(graph->to)[ (long int)VECTOR(graph->oi)[i] ]; + DEDUPLICATE_IF_NEEDED(to, 1); + VECTOR(*neis)[idx++] = to; + } + } + if (mode & IGRAPH_IN) { + j = (long int) VECTOR(graph->is)[node + 1]; + for (i = (long int) VECTOR(graph->is)[node]; i < j; i++) { + igraph_real_t from = VECTOR(graph->from)[ (long int)VECTOR(graph->ii)[i] ]; + DEDUPLICATE_IF_NEEDED(from, 1); + VECTOR(*neis)[idx++] = from; + } + } + } else { + /* Both in- and out- neighbors in a directed graph, + we need to merge the two 'vectors' so the result is + correctly ordered. */ + long int j1 = (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]; + long int eid1, eid2; + long int n1, n2; + + should_filter_duplicates = !(multiple == IGRAPH_MULTIPLE && + loops == IGRAPH_LOOPS_TWICE); + + while (i1 < j1 && i2 < j2) { + eid1 = (long int) VECTOR(graph->oi)[i1]; + eid2 = (long int) VECTOR(graph->ii)[i2]; + n1 = (long int) VECTOR(graph->to)[eid1]; + n2 = (long int) VECTOR(graph->from)[eid2]; + if (n1 < n2) { + i1++; + DEDUPLICATE_IF_NEEDED(n1, 1); + VECTOR(*neis)[idx++] = n1; + } else if (n1 > n2) { + i2++; + DEDUPLICATE_IF_NEEDED(n2, 1); + VECTOR(*neis)[idx++] = n2; + } else { + i1++; + i2++; + DEDUPLICATE_IF_NEEDED(n1, 2); + VECTOR(*neis)[idx++] = n1; + if (should_filter_duplicates && ((loops == IGRAPH_LOOPS_ONCE && n1 == pnode && last_added == pnode) || + (multiple == IGRAPH_NO_MULTIPLE))) { + length--; + continue; + } + VECTOR(*neis)[idx++] = n2; + } + } + + while (i1 < j1) { + eid1 = (long int) VECTOR(graph->oi)[i1++]; + igraph_real_t to = (long int) VECTOR(graph->to)[eid1]; + DEDUPLICATE_IF_NEEDED(to, 1); + VECTOR(*neis)[idx++] = to; + } + + while (i2 < j2) { + eid2 = (long int) VECTOR(graph->ii)[i2++]; + igraph_real_t from = (long int) VECTOR(graph->from)[eid2]; + DEDUPLICATE_IF_NEEDED(from, 1); + VECTOR(*neis)[idx++] = from; + } + + } + IGRAPH_CHECK(igraph_vector_resize(neis, length)); + + return IGRAPH_SUCCESS; +#undef DEDUPLICATE_IF_NEEDED +} +/** + * \ingroup internal + * + */ + +static 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, TRUE if the graph is directed, + * FALSE 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. + * + * + * 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 (head) of + * the edge will be placed here for undirected (directed) graphs. + * \param to Pointer to an \type igraph_integer_t. The head (tail) of the + * edge will be placed here for undirected (directed) graphs. + * \return Error code. The current implementation always returns with + * success. + * \sa \ref igraph_get_eid() for the opposite operation; + * \ref igraph_edges() to get the endpoints of several edges; + * \ref IGRAPH_TO(), \ref IGRAPH_FROM() and \ref IGRAPH_OTHER() for + * a faster but non-error-checked version. + * + * Added in version 0.2. + * + * 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_FROM(graph, eid); + *to = IGRAPH_TO(graph, eid); + } else { + *from = IGRAPH_TO(graph, eid); + *to = IGRAPH_FROM(graph, eid); + } + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_edges + * \brief Gives the head and tail vertices of a series of edges. + * + * \param graph The graph object. + * \param eids Edge selector, the series of edges. + * \param edges Pointer to an initialized vector. The start and endpoints of + * each edge will be placed here. + * \return Error code. + * \sa \ref igraph_get_edgelist() to get the endpoints of all edges; + * \ref igraph_get_eids() and \ref igraph_get_eids_multi() + * for the opposite operation; + * \ref igraph_edge() for getting the endpoints of a single edge; + * \ref IGRAPH_TO(), \ref IGRAPH_FROM() and \ref IGRAPH_OTHER() for + * a faster but non-error-checked method. + * + * Time complexity: O(k) where k is the number of edges in the selector. + */ + +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 IGRAPH_SUCCESS; +} + +/* 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-startoi,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. + */ + +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. VECTOR(pairs)[0] and + * VECTOR(pairs)[1] give the first + * pair, VECTOR(pairs)[2] and + * VECTOR(pairs)[3] the second pair, etc. + * + * + * 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. + * + * + * 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. + * + * + * 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. + * + * + * 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-startoi,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. VECTOR(pairs)[0] and + * VECTOR(pairs)[1] give the first pair, + * VECTOR(pairs)[2] and VECTOR(pairs)[3] the + * second pair, etc. + * + * + * 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. + * + * + * 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. + * + * + * An error is triggered if both \c pairs and \c path are non-null + * pointers. + * + * + * 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_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. + * + * 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) { + if (!igraph_is_directed(graph) || mode == IGRAPH_ALL) { + return igraph_i_incident(graph, eids, pnode, mode, IGRAPH_LOOPS_TWICE, IGRAPH_MULTIPLE); + } else { + return igraph_i_incident(graph, eids, pnode, mode, IGRAPH_LOOPS_ONCE, IGRAPH_MULTIPLE); + } +} + +int igraph_i_incident(const igraph_t *graph, igraph_vector_t *eids, igraph_integer_t pnode, + igraph_neimode_t mode, igraph_loops_t loops, igraph_multiple_t multiple) { +#define DEDUPLICATE_IF_NEEDED(vertex, n) \ + if (should_filter_duplicates) { \ + if ((loops == IGRAPH_NO_LOOPS && vertex == pnode) || \ + (loops == IGRAPH_LOOPS_ONCE && vertex == pnode && last_added == pnode) || \ + (multiple == IGRAPH_NO_MULTIPLE && vertex == last_added)) { \ + length -= n; \ + continue; \ + } else { \ + last_added = vertex; \ + } \ + } + long int length = 0, idx = 0; + long int i, j; + + long int node = pnode; + igraph_real_t last_added = -1; + + igraph_bool_t should_filter_duplicates; + + if (node < 0 || node > igraph_vcount(graph) - 1) { + IGRAPH_ERROR("Given vertex is not in the graph.", IGRAPH_EINVVID); + } + if (mode != IGRAPH_OUT && mode != IGRAPH_IN && + mode != IGRAPH_ALL) { + IGRAPH_ERROR("Mode should be either IGRAPH_OUT, IGRAPH_IN or IGRAPH_ALL.", IGRAPH_EINVMODE); + } + + if (!igraph_is_directed(graph)) { + mode = IGRAPH_ALL; + } + + if (mode != IGRAPH_ALL && loops == IGRAPH_LOOPS_TWICE) { + IGRAPH_ERROR("For a directed graph (with directions not ignored), " + "IGRAPH_LOOPS_TWICE does not make sense.\n", IGRAPH_EINVAL); + } + + /* Calculate needed space first & allocate it */ + /* Note that 'mode' is treated as a bit field here; it's okay because + * IGRAPH_ALL = IGRAPH_IN | IGRAPH_OUT, bit-wise */ + 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)); + + /* The loops below produce an ordering what is consistent with the + * ordering returned by igraph_neighbors(), and this should be preserved. + * We are dealing with two sorted lists; one for the successors and one + * for the predecessors. If we have requested only one of them, we have + * an easy job. If we have requested both, we need to merge the two lists + * to ensure that the output is sorted by the vertex IDs of the "other" + * endpoint of the affected edges */ + if (!igraph_is_directed(graph) || mode != IGRAPH_ALL) { + /* We did not ask for both directions; this is the easy case */ + + should_filter_duplicates = !(multiple == IGRAPH_MULTIPLE && + ((!igraph_is_directed(graph) && loops == IGRAPH_LOOPS_TWICE) || + (igraph_is_directed(graph) && loops != IGRAPH_NO_LOOPS))); + + if (mode & IGRAPH_OUT) { + j = (long int) VECTOR(graph->os)[node + 1]; + for (i = (long int) VECTOR(graph->os)[node]; i < j; i++) { + long int edge = VECTOR(graph->oi)[i]; + igraph_real_t other = VECTOR(graph->to)[edge]; + DEDUPLICATE_IF_NEEDED(other, 1); + VECTOR(*eids)[idx++] = edge; + } + } + if (mode & IGRAPH_IN) { + j = (long int) VECTOR(graph->is)[node + 1]; + for (i = (long int) VECTOR(graph->is)[node]; i < j; i++) { + long int edge = VECTOR(graph->ii)[i]; + igraph_real_t other = VECTOR(graph->from)[edge]; + DEDUPLICATE_IF_NEEDED(other, 1); + VECTOR(*eids)[idx++] = edge; + } + } + } else { + /* both in- and out- neighbors in a directed graph, + we need to merge the two 'vectors' */ + long int j1 = (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]; + long int eid1, eid2; + long int n1, n2; + + should_filter_duplicates = !(multiple == IGRAPH_MULTIPLE && + loops == IGRAPH_LOOPS_TWICE); + + while (i1 < j1 && i2 < j2) { + eid1 = (long int) VECTOR(graph->oi)[i1]; + eid2 = (long int) VECTOR(graph->ii)[i2]; + n1 = (long int) VECTOR(graph->to)[eid1]; + n2 = (long int) VECTOR(graph->from)[eid2]; + if (n1 < n2) { + i1++; + DEDUPLICATE_IF_NEEDED(n1, 1); + VECTOR(*eids)[idx++] = eid1; + } else if (n1 > n2) { + i2++; + DEDUPLICATE_IF_NEEDED(n2, 1); + VECTOR(*eids)[idx++] = eid2; + } else { + i1++; + i2++; + DEDUPLICATE_IF_NEEDED(n2, 2); + VECTOR(*eids)[idx++] = eid1; + if (should_filter_duplicates && ((loops == IGRAPH_LOOPS_ONCE && n1 == pnode && last_added == pnode) || + (multiple == IGRAPH_NO_MULTIPLE))) { + length--; + continue; + } + VECTOR(*eids)[idx++] = eid2; + } + } + + while (i1 < j1) { + eid1 = VECTOR(graph->oi)[i1++]; + igraph_real_t to = VECTOR(graph->to)[eid1]; + DEDUPLICATE_IF_NEEDED(to, 1); + VECTOR(*eids)[idx++] = eid1; + } + + while (i2 < j2) { + eid2 = VECTOR(graph->ii)[i2++]; + igraph_real_t from = VECTOR(graph->from)[eid2]; + DEDUPLICATE_IF_NEEDED(from, 1); + VECTOR(*eids)[idx++] = eid2; + } + } + IGRAPH_CHECK(igraph_vector_resize(eids, length)); + return IGRAPH_SUCCESS; +#undef DEDUPLICATE_IF_NEEDED +} + + +/** + * \function igraph_is_same_graph + * \brief Are two graphs identical as labelled graphs? + * + * Two graphs are considered to be the same if they have the same vertex and edge sets. + * Graphs which are the same may have multiple different representations in igraph, + * hence the need for this function. + * + * + * This function verifies that the two graphs have the same directedness, the same + * number of vertices, and that they contain precisely the same edges (regardless of their ordering) + * when written in terms of vertex indices. Graph attributes are not taken into account. + * + * + * This concept is different from isomorphism. For example, the graphs + * 0-1, 2-1 and 1-2, 0-1 are considered the same + * because they only differ in the ordering of their edge lists and the ordering + * of vertices in an undirected edge. However, they are not the same as + * 0-2, 1-2, even though they are isomorphic to it. + * Note that this latter graph contains the edge 0-2 + * while the former two do not — thus their edge sets differ. + * + * \param graph1 The first graph object. + * \param graph2 The second graph object. + * \param res The result will be stored here. + * \return Error code. + * + * Time complexity: O(E), the number of edges in the graphs. + * + * \sa igraph_isomorphic() to test if two graphs are isomorphic. + */ + +int igraph_is_same_graph(const igraph_t *graph1, const igraph_t *graph2, igraph_bool_t *res) { + long int nv1 = igraph_vcount(graph1); + long int nv2 = igraph_vcount(graph2); + long int ne1 = igraph_ecount(graph1); + long int ne2 = igraph_ecount(graph2); + long int i, eid1, eid2; + + *res = 0; /* Assume that the graphs differ */ + + /* Check for same number of vertices/edges */ + if ((nv1 != nv2) || (ne1 != ne2)) { + return IGRAPH_SUCCESS; + } + + /* Check for same directedness */ + if (igraph_is_directed(graph1) != igraph_is_directed(graph2)) { + return IGRAPH_SUCCESS; + } + + /* Vertices have no names, so they must be 0 to nv - 1 */ + + /* Edges are double sorted in the current representations ii/oi of + * igraph_t (ii: by incoming, then outgoing, oi: vice versa), so + * we just need to check them one by one. If that representation + * changes, this part will need to change too. + * + * Furthermore, in the current representation the "source" of undirected + * edges always has a vertex index that is no larger than that of the + * "target". + */ + for (i = 0; i < ne1; i++) { + eid1 = (long int) VECTOR(graph1->ii)[i]; + eid2 = (long int) VECTOR(graph2->ii)[i]; + + /* Check they have the same source */ + if (IGRAPH_FROM(graph1, eid1) != IGRAPH_FROM(graph2, eid2)) { + return IGRAPH_SUCCESS; + } + + /* Check they have the same target */ + if (IGRAPH_TO(graph1, eid1) != IGRAPH_TO(graph2, eid2)) { + return IGRAPH_SUCCESS; + } + } + + *res = 1; /* No difference was found, graphs are the same */ + return IGRAPH_SUCCESS; +} diff --git a/src/rigraph/core/graph/visitors.c b/src/rigraph/core/graph/visitors.c new file mode 100644 index 0000000..82997c7 --- /dev/null +++ b/src/rigraph/core/graph/visitors.c @@ -0,0 +1,648 @@ +/* -*- mode: C -*- */ +/* + IGraph R package. + Copyright (C) 2006-2012 Gabor Csardi + 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" + +/** + * \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. + * + * + * 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. + * + * + * Consider using \ref igraph_bfs_simple instead if you set most of the output + * arguments provided by this function to a null pointer. + * + * \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_bfs_callback.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 && noroots > 0) { + 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_vector_size(restricted) > 0) { + 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, IGRAPH_LOOPS, IGRAPH_MULTIPLE)); + 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_int_t *neis = igraph_lazy_adjlist_get(&adjlist, + (igraph_integer_t) actvect); + long int i, n = igraph_vector_int_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_bfs_simple + * Breadth-first search, single-source version + * + * An alternative breadth-first search implementation to cater for the + * simpler use-cases when only a single breadth-first search has to be conducted + * from a source node and most of the output arguments from \ref igraph_bfs + * are not needed. It is allowed to supply null pointers as + * the output arguments the user is not interested in, in this case they will + * be ignored. + * + * \param graph The input graph. + * \param vid 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 vids If not a null pointer, then an initialized vector must be passed + * here. The ids of the vertices visited during the traversal will be + * stored here, in the same order as they were visited. + * \param layers If not a null pointer, then an initialized vector must be + * passed here. The i-th element of the vector will contain the index + * into \c vids where the vertices that are at distance i from the root + * are stored. In other words, if you are interested in the vertices that + * are at distance i from the root, you need to look in the \c vids + * vector from \c layers[i] to \c layers[i+1]. + * \param parents If not a null pointer, then an initialized vector must be + * passed here. The vector will be resized so its length is equal to the + * number of nodes, and it will contain the index of the parent node for + * each \em visited node. The values in the vector are undefined for + * vertices that were \em not visited. + * \return Error code. + * + * Time complexity: O(|V|+|E|), linear in the number of vertices and + * edges. + * + * \example examples/simple/igraph_bfs_simple.c + */ +int igraph_bfs_simple(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 num_visited = 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 */ + if (vids) { + igraph_vector_clear(vids); + } + if (layers) { + igraph_vector_clear(layers); + } + if (parents) { + 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)); + if (layers) { + IGRAPH_CHECK(igraph_vector_push_back(layers, num_visited)); + } + if (vids) { + IGRAPH_CHECK(igraph_vector_push_back(vids, vid)); + } + if (parents) { + VECTOR(*parents)[(long int)vid] = vid; + } + num_visited++; + 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; + if (parents) { + VECTOR(*parents)[neighbor] = actvect; + } + IGRAPH_CHECK(igraph_dqueue_push(&q, neighbor)); + IGRAPH_CHECK(igraph_dqueue_push(&q, actdist + 1)); + if (layers && lastlayer != actdist + 1) { + IGRAPH_CHECK(igraph_vector_push_back(layers, num_visited)); + } + if (vids) { + IGRAPH_CHECK(igraph_vector_push_back(vids, neighbor)); + } + num_visited++; + lastlayer = actdist + 1; + } + } /* for i in neis */ + } /* while ! dqueue_empty */ + + if (layers) { + IGRAPH_CHECK(igraph_vector_push_back(layers, num_visited)); + } + + 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. + * + * + * 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, IGRAPH_LOOPS, IGRAPH_MULTIPLE)); + 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_int_t *neis = + igraph_lazy_adjlist_get(&adjlist, (igraph_integer_t) actvect); + long int n = igraph_vector_int_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 = 0; + 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/src/rigraph/core/hrg/dendro.h b/src/rigraph/core/hrg/dendro.h new file mode 100644 index 0000000..a2d092b --- /dev/null +++ b/src/rigraph/core/hrg/dendro.h @@ -0,0 +1,313 @@ +/* -*- mode: C++ -*- */ +/* + IGraph library. + Copyright (C) 2011-2012 Gabor Csardi + 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 "hrg/graph.h" +#include "hrg/rbtree.h" +#include "hrg/splittree_eq.h" + +#include "igraph_hrg.h" + +#include +#include + +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; + std::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: + std::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) != std::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 + std::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) + std::string getSplit(const int); + // replace an existing split, O(1) + bool replaceSplit(const int, const std::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 + std::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 std::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/src/rigraph/core/hrg/graph.h b/src/rigraph/core/hrg/graph.h new file mode 100644 index 0000000..f990f93 --- /dev/null +++ b/src/rigraph/core/hrg/graph.h @@ -0,0 +1,167 @@ +/* -*- mode: C++ -*- */ +/* + IGraph library. + Copyright (C) 2011-2012 Gabor Csardi + 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 "hrg/rbtree.h" + +#include +#include +#include + +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: + std::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 + std::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 std::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/src/rigraph/core/hrg/graph_simp.h b/src/rigraph/core/hrg/graph_simp.h new file mode 100644 index 0000000..f14fa78 --- /dev/null +++ b/src/rigraph/core/hrg/graph_simp.h @@ -0,0 +1,160 @@ +/* -*- mode: C++ -*- */ +/* + IGraph library. + Copyright (C) 2011-2012 Gabor Csardi + 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 "hrg/rbtree.h" +#include "hrg/dendro.h" + +#include +#include + +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: + std::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 + std::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 std::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/src/rigraph/core/hrg/hrg.cc b/src/rigraph/core/hrg/hrg.cc new file mode 100644 index 0000000..d98d596 --- /dev/null +++ b/src/rigraph/core/hrg/hrg.cc @@ -0,0 +1,1071 @@ +/* -*- mode: C++ -*- */ +/* + IGraph library. + Copyright (C) 2010-2012 Gabor Csardi + 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_memory.h" +#include "igraph_attributes.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 + * + * 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. + * + * + * 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). + * + * + * + * 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). + * + * + * The igraph HRG implementation is heavily based on the code + * published by Aaron Clauset, at his website, + * http://tuvalu.santafe.edu/~aaronc/hierarchy/ + * + */ + +namespace fitHRG { +struct pblock { + double L; + int i; + int j; +}; +} + +static 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; +} + +static 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; +} + +static 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; +} + +static 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; +} + +static 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 \ref igraph_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, + igraph_integer_t 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 > 0 ? no_of_nodes - 1 : 0; + 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; +} + +static 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; +} + +static 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; +} + +static 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; +} + +static 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; +} + +static 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 > 0 ? (no_of_nodes - 1) / 2 : 0; + 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(°, 0); + + // Every vertex, except for the root must have in-degree one. + IGRAPH_CHECK(igraph_degree(graph, °, 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, °, 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(°); + IGRAPH_FINALLY_CLEAN(4); + + return 0; +} diff --git a/src/rigraph/core/hrg/hrg_types.cc b/src/rigraph/core/hrg/hrg_types.cc new file mode 100644 index 0000000..467c2b9 --- /dev/null +++ b/src/rigraph/core/hrg/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 std; +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 left_index = VECTOR(hrg->left)[i]; + int right_index = VECTOR(hrg->right)[i]; + + if (left_index < 0) { + internal[i].L = &internal[-left_index - 1]; + internal[-left_index - 1].M = &internal[i]; + } else { + internal[i].L = &leaf[left_index]; + leaf[left_index].M = &internal[i]; + } + + if (right_index < 0) { + internal[i].R = &internal[-right_index - 1]; + internal[-right_index - 1].M = &internal[i]; + } else { + internal[i].R = &leaf[right_index]; + leaf[right_index].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; + } +} + +// ********************************************************************** + +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/src/rigraph/core/hrg/rbtree.h b/src/rigraph/core/hrg/rbtree.h new file mode 100644 index 0000000..85f4ee7 --- /dev/null +++ b/src/rigraph/core/hrg/rbtree.h @@ -0,0 +1,160 @@ +/* -*- mode: C++ -*- */ +/* + IGraph library. + Copyright (C) 2011-2012 Gabor Csardi + 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 + +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/src/rigraph/core/hrg/splittree_eq.h b/src/rigraph/core/hrg/splittree_eq.h new file mode 100644 index 0000000..8acab77 --- /dev/null +++ b/src/rigraph/core/hrg/splittree_eq.h @@ -0,0 +1,183 @@ +/* -*- mode: C++ -*- */ +/* + IGraph library. + Copyright (C) 2011-2012 Gabor Csardi + 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 + +namespace fitHRG { + +// ******** Basic Structures ********************************************* + +#ifndef IGRAPH_HRG_SLIST +#define IGRAPH_HRG_SLIST +class slist { +public: + std::string x; // stored elementd in linked-list + slist* next; // pointer to next elementd + slist(): x(""), next(0) { } + ~slist() { } +}; +#endif + +class keyValuePairSplit { +public: + std::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: + std::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 std::string); + // returns T if searchKey found, and points foundNode at the + // corresponding node + elementsp* findItem(const std::string); + // update total_count and total_weight + void finishedThisRound(); + // insert a new key with stored value + bool insertItem(std::string, double); + void clearTree(); + // delete a node with given key + void deleteItem(std::string); + // delete the entire tree + void deleteTree(); + // return array of keys in tree + std::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/src/rigraph/core/internal/glpk_support.c b/src/rigraph/core/internal/glpk_support.c new file mode 100644 index 0000000..257f218 --- /dev/null +++ b/src/rigraph/core/internal/glpk_support.c @@ -0,0 +1,170 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2011-2012 Gabor Csardi + 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 "internal/glpk_support.h" + +#ifdef HAVE_GLPK + +#include "igraph_error.h" + +#include "core/interruption.h" + +#include + +IGRAPH_THREAD_LOCAL igraph_i_glpk_error_info_t igraph_i_glpk_error_info; + +/* glp_at_error() was added in GLPK 4.57. Due to the R interface, we need to + * support ancient GLPK versions like GLPK 4.38 so we need to guard the + * invocation of glp_at_error(). Note that this is a temporary workaround only + * for sake of supporting R 4.1, so it is enabled only if USING_R is defined */ +#ifdef USING_R +# define HAS_GLP_AT_ERROR (GLP_MAJOR_VERSION > 4 || (GLP_MAJOR_VERSION == 4 && GLP_MINOR_VERSION >= 57)) +#else +# define HAS_GLP_AT_ERROR 1 +#endif + +int igraph_i_glpk_terminal_hook(void *info, const char *s) { + IGRAPH_UNUSED(info); + + if (igraph_i_interruption_handler && + !igraph_i_glpk_error_info.is_interrupted && + igraph_allow_interruption(NULL) != IGRAPH_SUCCESS) { + /* If an interruption has already occurred, do not set another error, + to avoid an infinite loop between the term_hook (this function) + and the error_hook. */ + igraph_i_glpk_error_info.is_interrupted = 1; + glp_error("GLPK was interrupted."); /* This dummy message is never printed */ +#if HAS_GLP_AT_ERROR + } else if (glp_at_error()) { + /* Copy the error messages into a buffer for later reporting */ + /* We must use glp_at_error() instead of igraph_i_glpk_error_info.is_error + * to determine if a message is an error message, as the reporting function is + * called before the error function. */ + const size_t n = sizeof(igraph_i_glpk_error_info.msg) / sizeof(char) - 1; + while (*s != '\0' && igraph_i_glpk_error_info.msg_ptr < igraph_i_glpk_error_info.msg + n) { + *(igraph_i_glpk_error_info.msg_ptr++) = *(s++); + } + *igraph_i_glpk_error_info.msg_ptr = '\0'; +#endif + } + + return 1; /* Non-zero return value signals to GLPK not to print to the terminal */ +} + +void igraph_i_glpk_error_hook(void *info) { + IGRAPH_UNUSED(info); + igraph_i_glpk_error_info.is_error = 1; + glp_free_env(); + longjmp(igraph_i_glpk_error_info.jmp, 1); +} + +void igraph_i_glpk_interruption_hook(glp_tree *tree, void *info) { + IGRAPH_UNUSED(info); + + /* This is a callback function meant to be used with glp_intopt(), + in order to support interruption. It is essentially a GLPK-compatible + replacement for 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); + } + } +} + +/** + * \ingroup internal + * \function igraph_i_glp_delete_prob + * \brief Safe replacement for glp_delete_prob(). + * + * This function is meant to be used with IGRAPH_FINALLY() + * in conjunction with glp_create_prob(). + * + * When using GLPK, normally glp_delete_prob() is used to free + * problems created with glp_create_prob(). However, when GLPK + * encounters an error, the error handler installed by igraph + * will call glp_free_env() which invalidates all problems. + * Calling glp_delete_prob() would then lead to a crash. + * This replacement function avoids this situation by first + * checking if GLPK is at an error state. + */ +void igraph_i_glp_delete_prob(glp_prob *p) { + if (! igraph_i_glpk_error_info.is_error) { + glp_delete_prob(p); + } +} + +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); +} + +#else + +int igraph_glpk_dummy() { + /* get rid of "ISO C requires a translation unit to contain at least one + * declaration" warning */ + return 'd' + 'u' + 'm' + 'm' + 'y'; +} + +#endif diff --git a/src/rigraph/core/internal/glpk_support.h b/src/rigraph/core/internal/glpk_support.h new file mode 100644 index 0000000..9d0ae73 --- /dev/null +++ b/src/rigraph/core/internal/glpk_support.h @@ -0,0 +1,141 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2007-2012 Gabor Csardi + 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 +#include + +typedef struct igraph_i_glpk_error_info_s { + jmp_buf jmp; /* used for bailing when there is a GLPK error */ + int is_interrupted; /* Boolean; true if there was an interruption */ + int is_error; /* Boolean; true if the error hook was called */ + char msg[4096]; /* GLPK error messages are collected here */ + char *msg_ptr; /* Points to the end (null terminator) of msg */ +} igraph_i_glpk_error_info_t; + +extern IGRAPH_THREAD_LOCAL igraph_i_glpk_error_info_t igraph_i_glpk_error_info; + +int igraph_i_glpk_check(int retval, const char* message); +void igraph_i_glpk_interruption_hook(glp_tree *tree, void *info); +void igraph_i_glpk_error_hook(void *info); +int igraph_i_glpk_terminal_hook(void *info, const char *s); +void igraph_i_glp_delete_prob(glp_prob *p); + +#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) + +/** + * \ingroup internal + * \define IGRAPH_GLPK_SETUP + * + * Use this macro at the start of igraph functions that use GLPK routines + * directly. + * + * - IGRAPH_GLPK_SETUP() must be called in all top-level functions that + * use GLPK, before beginning to use any GLPK functions. + * + * - Do NOT call glp_term_out(OFF) as interruption support relies on + * the terminal hook being called. + * + * - This must be a macro and not a function, as jumping into a function + * that has already returned with longjmp() is not possible. + * + * This setup step is necessary in order to support interruption, as + * well as to handle fatal GLPK errors gracefully. See here for details: + * + * https://lists.gnu.org/archive/html/help-glpk/2019-10/msg00000.html + * + * Interruption support for GLPK is essential because it is practically + * impossible to predict how long it will take to solve a problem. It + * may take less than a second or it may never finish in practice. + * + * It does the following: + * + * - Initialize the data structure where we keep track of GLPK's current + * error and interruption state, \c igraph_i_glpk_error_info. + * - Set an error hook and a terminal hook for GLPK. + * - Provide a return point for the longjmp() called from the error hook. + * + * There are two interruption mechanisms we can use with GLPK. glp_intopt() + * supports a callback function which can signal a request for interruption. + * However, glp_intopt() internally calls glp_simplex(), which may again + * take a very long time. + * + * The recommended way to interrupt glp_simplex() is to check for interruption + * from the terminal hook, which is normally meant for intercepting output. + * This interruption is possible only as often as there is output, which may + * be at intervals of a few seconds in practice. + * + * Interruption is achieved by setting an error with glp_error(), which + * triggers a call to the error hook. From the error hook, we free all + * GLPK resources using glp_free_env() and do a longjmp(). + * + * The use of these mechanisms makes it unsafe to use igraph's GLPK-reliant + * functions from a process which also uses GLPK for other purposes. + * To avoid this problem, GLPK should ideally be linked to igraph statically. + */ +#define IGRAPH_GLPK_SETUP() \ + do { \ + glp_error_hook(igraph_i_glpk_error_hook, NULL); \ + glp_term_hook(igraph_i_glpk_terminal_hook, NULL); \ + igraph_i_glpk_error_info.is_interrupted = 0; \ + igraph_i_glpk_error_info.is_error = 0; \ + igraph_i_glpk_error_info.msg_ptr = igraph_i_glpk_error_info.msg; \ + if (setjmp(igraph_i_glpk_error_info.jmp)) { \ + if (igraph_i_glpk_error_info.is_interrupted) { \ + return IGRAPH_INTERRUPTED; \ + } else { \ + if (igraph_i_glpk_error_info.msg_ptr != igraph_i_glpk_error_info.msg) { \ + while ( *(igraph_i_glpk_error_info.msg_ptr - 1) == '\n' && \ + igraph_i_glpk_error_info.msg_ptr > igraph_i_glpk_error_info.msg ) { \ + igraph_i_glpk_error_info.msg_ptr--; \ + } \ + *igraph_i_glpk_error_info.msg_ptr = '\0'; \ + igraph_error(igraph_i_glpk_error_info.msg, IGRAPH_FILE_BASENAME, __LINE__, IGRAPH_EGLP); \ + } else if (igraph_i_glpk_error_info.is_error) { \ + /* This branch can never be reached unless compiled with USING_R and using */ \ + /* the hack to support pre-4.57 GLPK versions. See comments in glpk_support.c. */ \ + igraph_error("Error while running GLPK solver.", IGRAPH_FILE_BASENAME, __LINE__, IGRAPH_EGLP); \ + } \ + return IGRAPH_EGLP; \ + } \ + } \ + } while (0) + +#endif + +#endif diff --git a/src/rigraph/core/internal/gmp_internal.h b/src/rigraph/core/internal/gmp_internal.h new file mode 100644 index 0000000..e7f6b7e --- /dev/null +++ b/src/rigraph/core/internal/gmp_internal.h @@ -0,0 +1,34 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2020 The igraph development team + + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public 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_GMP_H +#define IGRAPH_GMP_H + +#include "config.h" + +#ifdef INTERNAL_GMP +#include "mini-gmp/mini-gmp.h" +#else +#include +#endif + +#endif diff --git a/src/rigraph/core/internal/hacks.c b/src/rigraph/core/internal/hacks.c new file mode 100644 index 0000000..d6653e0 --- /dev/null +++ b/src/rigraph/core/internal/hacks.c @@ -0,0 +1,53 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2010-2012 Gabor Csardi + 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 "internal/hacks.h" + +#include +#include + +/* 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/src/leidenalg/igraph-R/igraph_memory.h b/src/rigraph/core/internal/hacks.h similarity index 61% rename from src/leidenalg/igraph-R/igraph_memory.h rename to src/rigraph/core/internal/hacks.h index 02fbf0a..559ca40 100644 --- a/src/leidenalg/igraph-R/igraph_memory.h +++ b/src/rigraph/core/internal/hacks.h @@ -1,54 +1,60 @@ /* -*- mode: C -*- */ -/* +/* IGraph library. Copyright (C) 2003-2012 Gabor Csardi 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 + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#ifndef REST_MEMORY_H -#define REST_MEMORY_H +#ifndef IGRAPH_HACKS_INTERNAL_H +#define IGRAPH_HACKS_INTERNAL_H -#include +#include "config.h" #undef __BEGIN_DECLS #undef __END_DECLS #ifdef __cplusplus -# define __BEGIN_DECLS extern "C" { -# define __END_DECLS } + #define __BEGIN_DECLS extern "C" { + #define __END_DECLS } #else -# define __BEGIN_DECLS /* empty */ -# define __END_DECLS /* empty */ + #define __BEGIN_DECLS /* empty */ + #define __END_DECLS /* empty */ #endif __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 HAVE_STRDUP + #define strdup igraph_i_strdup + char* igraph_i_strdup(const char *s); +#endif -/* #ifndef IGRAPH_NO_CALLOC */ -/* # define Calloc igraph_Calloc */ -/* # define Realloc igraph_Realloc */ -/* # define Free igraph_Free */ -/* #endif */ +#ifndef HAVE_STPCPY + #define stpcpy igraph_i_stpcpy + char* igraph_i_stpcpy(char* s1, const char* s2); +#endif -int igraph_free(void *p); +#ifndef HAVE_STRCASECMP + #ifdef HAVE__STRICMP + #define strcasecmp _stricmp + #else + #error "igraph needs strcasecmp() or _stricmp()" + #endif +#endif __END_DECLS diff --git a/src/rigraph/core/internal/lsap.c b/src/rigraph/core/internal/lsap.c new file mode 100644 index 0000000..bdbe96c --- /dev/null +++ b/src/rigraph/core/internal/lsap.c @@ -0,0 +1,672 @@ + +#include "igraph_lsap.h" +#include "igraph_error.h" + +/* #include */ +#include +#include /* INT_MAX */ +#include /* DBL_MAX */ +#include + +/* 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 */ +static AP *ap_create_problem(double *t, int n); +/* static AP *ap_create_problem_from_matrix(double **t, int n); */ +/* static AP *ap_read_problem(char *file); */ +static void ap_free(AP *p); + +static int ap_assignment(AP *p, int *res); +/* static int ap_costmatrix(AP *p, double **m); */ +/* static int ap_datamatrix(AP *p, double **m); */ +/* static int ap_iterations(AP *p); */ +static int ap_hungarian(AP *p); +/* static double ap_mincost(AP *p); */ +/* static void ap_print_solution(AP *p); */ +/* static void ap_show_data(AP *p); */ +/* static int ap_size(AP *p); */ +/* static int ap_time(AP *p); */ + +/* error reporting */ +/* static void ap_error(char *message); */ + +/* private functions */ +static void preprocess(AP *p); +static void preassign(AP *p); +static int cover(AP *p, int *ri, int *ci); +static 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 assignment, 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 */ +/*******************************************************************/ + +#if 0 +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; rows = %d, cols = %d.", 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; +} +#endif + +#if 0 +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; +} +#endif + +/* 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; + } + } +} + +/** + * \function igraph_solve_lsap + * \brief Solve a balanced linear assignment problem. + * + * This functions solves a linear assinment problem using the Hungarian + * method. A number of tasks, an equal number of agents, and the cost + * of each agent to perform the tasks is given. This function then + * assigns one task to each agent in such a way that the total cost is + * minimized. + * + * + * If the cost should be maximized instead of minimized, the cost matrix + * should be negated. + * + * + * To solve an unbalanced assignment problem, where the number of agents + * is greater than the number of tasks, an extra task with zero cost + * should be added. + * + * \param c The assignment problem, where the number of rows is the + * number of agents, the number of columns is the number of + * tasks, and each element is the cost of an agent to perform + * the task. + * \param n The number of rows and columns of \p c. + * \param p An initialized vector which will store the result. The nth + * entry gives the task the nth agent is assigned to minimize + * the total cost. + * \return Error code. + * + * Time complexity: O(n^3), where n is the number of agents. + */ +int igraph_solve_lsap(igraph_matrix_t *c, igraph_integer_t n, + igraph_vector_int_t *p) { + AP *ap; + + if(n != igraph_matrix_nrow(c)) { + IGRAPH_ERRORF("n (%" IGRAPH_PRId ") " + "not equal to number of agents (%ld).", IGRAPH_EINVAL, + n, igraph_matrix_nrow(c)); + } + if(n != igraph_matrix_ncol(c)) { + IGRAPH_ERRORF("n (%" IGRAPH_PRId ") " + "not equal to number of tasks (%ld).", IGRAPH_EINVAL, + n, igraph_matrix_ncol(c)); + } + 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 IGRAPH_SUCCESS; +} diff --git a/src/rigraph/core/internal/pstdint.h b/src/rigraph/core/internal/pstdint.h new file mode 100644 index 0000000..bd5697f --- /dev/null +++ b/src/rigraph/core/internal/pstdint.h @@ -0,0 +1,817 @@ +/* A portable stdint.h + **************************************************************************** + * BSD License: + **************************************************************************** + * + * Copyright (c) 2005-2007 Paul Hsieh + * 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + * + **************************************************************************** + * + * Version 0.1.11 + * + * The ANSI C standard committee, for the C99 standard, specified the + * inclusion of a new standard include file called stdint.h. This is + * a very useful and long desired include file which contains several + * very precise definitions for integer scalar types that is + * critically important for making portable several classes of + * applications including cryptography, hashing, variable length + * integer libraries and so on. But for most developers its likely + * useful just for programming sanity. + * + * The problem is that most compiler vendors have decided not to + * implement the C99 standard, and the next C++ language standard + * (which has a lot more mindshare these days) will be a long time in + * coming and its unknown whether or not it will include stdint.h or + * how much adoption it will have. Either way, it will be a long time + * before all compilers come with a stdint.h and it also does nothing + * for the extremely large number of compilers available today which + * do not include this file, or anything comparable to it. + * + * So that's what this file is all about. Its an attempt to build a + * single universal include file that works on as many platforms as + * possible to deliver what stdint.h is supposed to. A few things + * that should be noted about this file: + * + * 1) It is not guaranteed to be portable and/or present an identical + * interface on all platforms. The extreme variability of the + * ANSI C standard makes this an impossibility right from the + * very get go. Its really only meant to be useful for the vast + * majority of platforms that possess the capability of + * implementing usefully and precisely defined, standard sized + * integer scalars. Systems which are not intrinsically 2s + * complement may produce invalid constants. + * + * 2) There is an unavoidable use of non-reserved symbols. + * + * 3) Other standard include files are invoked. + * + * 4) This file may come in conflict with future platforms that do + * include stdint.h. The hope is that one or the other can be + * used with no real difference. + * + * 5) In the current verison, if your platform can't represent + * int32_t, int16_t and int8_t, it just dumps out with a compiler + * error. + * + * 6) 64 bit integers may or may not be defined. Test for their + * presence with the test: #ifdef INT64_MAX or #ifdef UINT64_MAX. + * Note that this is different from the C99 specification which + * requires the existence of 64 bit support in the compiler. If + * this is not defined for your platform, yet it is capable of + * dealing with 64 bits then it is because this file has not yet + * been extended to cover all of your system's capabilities. + * + * 7) (u)intptr_t may or may not be defined. Test for its presence + * with the test: #ifdef PTRDIFF_MAX. If this is not defined + * for your platform, then it is because this file has not yet + * been extended to cover all of your system's capabilities, not + * because its optional. + * + * 8) The following might not been defined even if your platform is + * capable of defining it: + * + * WCHAR_MIN + * WCHAR_MAX + * (u)int64_t + * PTRDIFF_MIN + * PTRDIFF_MAX + * (u)intptr_t + * + * 9) The following have not been defined: + * + * WINT_MIN + * WINT_MAX + * + * 10) The criteria for defining (u)int_least(*)_t isn't clear, + * except for systems which don't have a type that precisely + * defined 8, 16, or 32 bit types (which this include file does + * not support anyways). Default definitions have been given. + * + * 11) The criteria for defining (u)int_fast(*)_t isn't something I + * would trust to any particular compiler vendor or the ANSI C + * committee. It is well known that "compatible systems" are + * commonly created that have very different performance + * characteristics from the systems they are compatible with, + * especially those whose vendors make both the compiler and the + * system. Default definitions have been given, but its strongly + * recommended that users never use these definitions for any + * reason (they do *NOT* deliver any serious guarantee of + * improved performance -- not in this file, nor any vendor's + * stdint.h). + * + * 12) The following macros: + * + * PRINTF_INTMAX_MODIFIER + * PRINTF_INT64_MODIFIER + * PRINTF_INT32_MODIFIER + * PRINTF_INT16_MODIFIER + * PRINTF_LEAST64_MODIFIER + * PRINTF_LEAST32_MODIFIER + * PRINTF_LEAST16_MODIFIER + * PRINTF_INTPTR_MODIFIER + * + * are strings which have been defined as the modifiers required + * for the "d", "u" and "x" printf formats to correctly output + * (u)intmax_t, (u)int64_t, (u)int32_t, (u)int16_t, (u)least64_t, + * (u)least32_t, (u)least16_t and (u)intptr_t types respectively. + * PRINTF_INTPTR_MODIFIER is not defined for some systems which + * provide their own stdint.h. PRINTF_INT64_MODIFIER is not + * defined if INT64_MAX is not defined. These are an extension + * beyond what C99 specifies must be in stdint.h. + * + * In addition, the following macros are defined: + * + * PRINTF_INTMAX_HEX_WIDTH + * PRINTF_INT64_HEX_WIDTH + * PRINTF_INT32_HEX_WIDTH + * PRINTF_INT16_HEX_WIDTH + * PRINTF_INT8_HEX_WIDTH + * PRINTF_INTMAX_DEC_WIDTH + * PRINTF_INT64_DEC_WIDTH + * PRINTF_INT32_DEC_WIDTH + * PRINTF_INT16_DEC_WIDTH + * PRINTF_INT8_DEC_WIDTH + * + * Which specifies the maximum number of characters required to + * print the number of that type in either hexadecimal or decimal. + * These are an extension beyond what C99 specifies must be in + * stdint.h. + * + * Compilers tested (all with 0 warnings at their highest respective + * settings): Borland Turbo C 2.0, WATCOM C/C++ 11.0 (16 bits and 32 + * bits), Microsoft Visual C++ 6.0 (32 bit), Microsoft Visual Studio + * .net (VC7), Intel C++ 4.0, GNU gcc v3.3.3 + * + * This file should be considered a work in progress. Suggestions for + * improvements, especially those which increase coverage are strongly + * encouraged. + * + * Acknowledgements + * + * The following people have made significant contributions to the + * development and testing of this file: + * + * Chris Howie + * John Steele Scott + * Dave Thorup + * + */ + +#include +#include +#include + +/* + * For gcc with _STDINT_H, fill in the PRINTF_INT*_MODIFIER macros, and + * do nothing else. On the Mac OS X version of gcc this is _STDINT_H_. + */ + +#if ((defined(__STDC__) && __STDC__ && __STDC_VERSION__ >= 199901L) || (defined (__WATCOMC__) && (defined (_STDINT_H_INCLUDED) || __WATCOMC__ >= 1250)) || (defined(__GNUC__) && (defined(_STDINT_H) || defined(_STDINT_H_)) )) && !defined (_PSTDINT_H_INCLUDED) + #include + #define _PSTDINT_H_INCLUDED + #ifndef PRINTF_INT64_MODIFIER + #define PRINTF_INT64_MODIFIER "ll" + #endif + #ifndef PRINTF_INT32_MODIFIER + #define PRINTF_INT32_MODIFIER "l" + #endif + #ifndef PRINTF_INT16_MODIFIER + #define PRINTF_INT16_MODIFIER "h" + #endif + #ifndef PRINTF_INTMAX_MODIFIER + #define PRINTF_INTMAX_MODIFIER PRINTF_INT64_MODIFIER + #endif + #ifndef PRINTF_INT64_HEX_WIDTH + #define PRINTF_INT64_HEX_WIDTH "16" + #endif + #ifndef PRINTF_INT32_HEX_WIDTH + #define PRINTF_INT32_HEX_WIDTH "8" + #endif + #ifndef PRINTF_INT16_HEX_WIDTH + #define PRINTF_INT16_HEX_WIDTH "4" + #endif + #ifndef PRINTF_INT8_HEX_WIDTH + #define PRINTF_INT8_HEX_WIDTH "2" + #endif + #ifndef PRINTF_INT64_DEC_WIDTH + #define PRINTF_INT64_DEC_WIDTH "20" + #endif + #ifndef PRINTF_INT32_DEC_WIDTH + #define PRINTF_INT32_DEC_WIDTH "10" + #endif + #ifndef PRINTF_INT16_DEC_WIDTH + #define PRINTF_INT16_DEC_WIDTH "5" + #endif + #ifndef PRINTF_INT8_DEC_WIDTH + #define PRINTF_INT8_DEC_WIDTH "3" + #endif + #ifndef PRINTF_INTMAX_HEX_WIDTH + #define PRINTF_INTMAX_HEX_WIDTH PRINTF_INT64_HEX_WIDTH + #endif + #ifndef PRINTF_INTMAX_DEC_WIDTH + #define PRINTF_INTMAX_DEC_WIDTH PRINTF_INT64_DEC_WIDTH + #endif + + /* + * Something really weird is going on with Open Watcom. Just pull some of + * these duplicated definitions from Open Watcom's stdint.h file for now. + */ + + #if defined (__WATCOMC__) && __WATCOMC__ >= 1250 + #if !defined (INT64_C) + #define INT64_C(x) (x + (INT64_MAX - INT64_MAX)) + #endif + #if !defined (UINT64_C) + #define UINT64_C(x) (x + (UINT64_MAX - UINT64_MAX)) + #endif + #if !defined (INT32_C) + #define INT32_C(x) (x + (INT32_MAX - INT32_MAX)) + #endif + #if !defined (UINT32_C) + #define UINT32_C(x) (x + (UINT32_MAX - UINT32_MAX)) + #endif + #if !defined (INT16_C) + #define INT16_C(x) (x) + #endif + #if !defined (UINT16_C) + #define UINT16_C(x) (x) + #endif + #if !defined (INT8_C) + #define INT8_C(x) (x) + #endif + #if !defined (UINT8_C) + #define UINT8_C(x) (x) + #endif + #if !defined (UINT64_MAX) + #define UINT64_MAX 18446744073709551615ULL + #endif + #if !defined (INT64_MAX) + #define INT64_MAX 9223372036854775807LL + #endif + #if !defined (UINT32_MAX) + #define UINT32_MAX 4294967295UL + #endif + #if !defined (INT32_MAX) + #define INT32_MAX 2147483647L + #endif + #if !defined (INTMAX_MAX) + #define INTMAX_MAX INT64_MAX + #endif + #if !defined (INTMAX_MIN) + #define INTMAX_MIN INT64_MIN + #endif + #endif +#endif + +#ifndef _PSTDINT_H_INCLUDED + #define _PSTDINT_H_INCLUDED + + #ifndef SIZE_MAX + #define SIZE_MAX (~(size_t)0) + #endif + + /* + * Deduce the type assignments from limits.h under the assumption that + * integer sizes in bits are powers of 2, and follow the ANSI + * definitions. + */ + + #ifndef UINT8_MAX + #define UINT8_MAX 0xff + #endif + #ifndef uint8_t + #if (UCHAR_MAX == UINT8_MAX) || defined (S_SPLINT_S) + typedef unsigned char uint8_t; + #define UINT8_C(v) ((uint8_t) v) + #else + # error "Platform not supported" + #endif + #endif + + #ifndef INT8_MAX + #define INT8_MAX 0x7f + #endif + #ifndef INT8_MIN + #define INT8_MIN INT8_C(0x80) + #endif + #ifndef int8_t + #if (SCHAR_MAX == INT8_MAX) || defined (S_SPLINT_S) + typedef signed char int8_t; + #define INT8_C(v) ((int8_t) v) + #else + # error "Platform not supported" + #endif + #endif + + #ifndef UINT16_MAX + #define UINT16_MAX 0xffff + #endif + #ifndef uint16_t + #if (UINT_MAX == UINT16_MAX) || defined (S_SPLINT_S) + typedef unsigned int uint16_t; + #ifndef PRINTF_INT16_MODIFIER + #define PRINTF_INT16_MODIFIER "" + #endif + #define UINT16_C(v) ((uint16_t) (v)) + #elif (USHRT_MAX == UINT16_MAX) + typedef unsigned short uint16_t; + #define UINT16_C(v) ((uint16_t) (v)) + #ifndef PRINTF_INT16_MODIFIER + #define PRINTF_INT16_MODIFIER "h" + #endif + #else + #error "Platform not supported" + #endif + #endif + + #ifndef INT16_MAX + #define INT16_MAX 0x7fff + #endif + #ifndef INT16_MIN + #define INT16_MIN INT16_C(0x8000) + #endif + #ifndef int16_t + #if (INT_MAX == INT16_MAX) || defined (S_SPLINT_S) + typedef signed int int16_t; + #define INT16_C(v) ((int16_t) (v)) + #ifndef PRINTF_INT16_MODIFIER + #define PRINTF_INT16_MODIFIER "" + #endif + #elif (SHRT_MAX == INT16_MAX) + typedef signed short int16_t; + #define INT16_C(v) ((int16_t) (v)) + #ifndef PRINTF_INT16_MODIFIER + #define PRINTF_INT16_MODIFIER "h" + #endif + #else + #error "Platform not supported" + #endif + #endif + + #ifndef UINT32_MAX + #define UINT32_MAX (0xffffffffUL) + #endif + #ifndef uint32_t + #if (ULONG_MAX == UINT32_MAX) || defined (S_SPLINT_S) + typedef unsigned long uint32_t; + #define UINT32_C(v) v ## UL + #ifndef PRINTF_INT32_MODIFIER + #define PRINTF_INT32_MODIFIER "l" + #endif + #elif (UINT_MAX == UINT32_MAX) + typedef unsigned int uint32_t; + #ifndef PRINTF_INT32_MODIFIER + #define PRINTF_INT32_MODIFIER "" + #endif + #define UINT32_C(v) v ## U + #elif (USHRT_MAX == UINT32_MAX) + typedef unsigned short uint32_t; + #define UINT32_C(v) ((unsigned short) (v)) + #ifndef PRINTF_INT32_MODIFIER + #define PRINTF_INT32_MODIFIER "" + #endif + #else + #error "Platform not supported" + #endif + #endif + + #ifndef INT32_MAX + #define INT32_MAX (0x7fffffffL) + #endif + #ifndef INT32_MIN + #define INT32_MIN INT32_C(0x80000000) + #endif + #ifndef int32_t + #if (LONG_MAX == INT32_MAX) || defined (S_SPLINT_S) + typedef signed long int32_t; + #define INT32_C(v) v ## L + #ifndef PRINTF_INT32_MODIFIER + #define PRINTF_INT32_MODIFIER "l" + #endif + #elif (INT_MAX == INT32_MAX) + typedef signed int int32_t; + #define INT32_C(v) v + #ifndef PRINTF_INT32_MODIFIER + #define PRINTF_INT32_MODIFIER "" + #endif + #elif (SHRT_MAX == INT32_MAX) + typedef signed short int32_t; + #define INT32_C(v) ((short) (v)) + #ifndef PRINTF_INT32_MODIFIER + #define PRINTF_INT32_MODIFIER "" + #endif + #else + #error "Platform not supported" + #endif + #endif + + /* + * The macro stdint_int64_defined is temporarily used to record + * whether or not 64 integer support is available. It must be + * defined for any 64 integer extensions for new platforms that are + * added. + */ + + #undef stdint_int64_defined + #if (defined(__STDC__) && defined(__STDC_VERSION__)) || defined (S_SPLINT_S) + #if (__STDC__ && __STDC_VERSION >= 199901L) || defined (S_SPLINT_S) + #define stdint_int64_defined + typedef long long int64_t; + typedef unsigned long long uint64_t; + #define UINT64_C(v) v ## ULL + #define INT64_C(v) v ## LL + #ifndef PRINTF_INT64_MODIFIER + #define PRINTF_INT64_MODIFIER "ll" + #endif + #endif + #endif + + #if !defined (stdint_int64_defined) + #if defined(__GNUC__) + #define stdint_int64_defined + __extension__ typedef long long int64_t; + __extension__ typedef unsigned long long uint64_t; + #define UINT64_C(v) v ## ULL + #define INT64_C(v) v ## LL + #ifndef PRINTF_INT64_MODIFIER + #define PRINTF_INT64_MODIFIER "ll" + #endif + #elif defined(__MWERKS__) || defined (__SUNPRO_C) || defined (__SUNPRO_CC) || defined (__APPLE_CC__) || defined (_LONG_LONG) || defined (_CRAYC) || defined (S_SPLINT_S) + #define stdint_int64_defined + typedef long long int64_t; + typedef unsigned long long uint64_t; + #define UINT64_C(v) v ## ULL + #define INT64_C(v) v ## LL + #ifndef PRINTF_INT64_MODIFIER + #define PRINTF_INT64_MODIFIER "ll" + #endif + #elif (defined(__WATCOMC__) && defined(__WATCOM_INT64__)) || (defined(_MSC_VER) && _INTEGRAL_MAX_BITS >= 64) || (defined (__BORLANDC__) && __BORLANDC__ > 0x460) || defined (__alpha) || defined (__DECC) + #define stdint_int64_defined + typedef __int64 int64_t; + typedef unsigned __int64 uint64_t; + #define UINT64_C(v) v ## UI64 + #define INT64_C(v) v ## I64 + #ifndef PRINTF_INT64_MODIFIER + #define PRINTF_INT64_MODIFIER "I64" + #endif + #endif + #endif + + #if !defined (LONG_LONG_MAX) && defined (INT64_C) + #define LONG_LONG_MAX INT64_C (9223372036854775807) + #endif + #ifndef ULONG_LONG_MAX + #define ULONG_LONG_MAX UINT64_C (18446744073709551615) + #endif + + #if !defined (INT64_MAX) && defined (INT64_C) + #define INT64_MAX INT64_C (9223372036854775807) + #endif + #if !defined (INT64_MIN) && defined (INT64_C) + #define INT64_MIN INT64_C (-9223372036854775808) + #endif + #if !defined (UINT64_MAX) && defined (INT64_C) + #define UINT64_MAX UINT64_C (18446744073709551615) + #endif + + /* + * Width of hexadecimal for number field. + */ + + #ifndef PRINTF_INT64_HEX_WIDTH + #define PRINTF_INT64_HEX_WIDTH "16" + #endif + #ifndef PRINTF_INT32_HEX_WIDTH + #define PRINTF_INT32_HEX_WIDTH "8" + #endif + #ifndef PRINTF_INT16_HEX_WIDTH + #define PRINTF_INT16_HEX_WIDTH "4" + #endif + #ifndef PRINTF_INT8_HEX_WIDTH + #define PRINTF_INT8_HEX_WIDTH "2" + #endif + + #ifndef PRINTF_INT64_DEC_WIDTH + #define PRINTF_INT64_DEC_WIDTH "20" + #endif + #ifndef PRINTF_INT32_DEC_WIDTH + #define PRINTF_INT32_DEC_WIDTH "10" + #endif + #ifndef PRINTF_INT16_DEC_WIDTH + #define PRINTF_INT16_DEC_WIDTH "5" + #endif + #ifndef PRINTF_INT8_DEC_WIDTH + #define PRINTF_INT8_DEC_WIDTH "3" + #endif + + /* + * Ok, lets not worry about 128 bit integers for now. Moore's law says + * we don't need to worry about that until about 2040 at which point + * we'll have bigger things to worry about. + */ + + #ifdef stdint_int64_defined + typedef int64_t intmax_t; + typedef uint64_t uintmax_t; + #define INTMAX_MAX INT64_MAX + #define INTMAX_MIN INT64_MIN + #define UINTMAX_MAX UINT64_MAX + #define UINTMAX_C(v) UINT64_C(v) + #define INTMAX_C(v) INT64_C(v) + #ifndef PRINTF_INTMAX_MODIFIER + #define PRINTF_INTMAX_MODIFIER PRINTF_INT64_MODIFIER + #endif + #ifndef PRINTF_INTMAX_HEX_WIDTH + #define PRINTF_INTMAX_HEX_WIDTH PRINTF_INT64_HEX_WIDTH + #endif + #ifndef PRINTF_INTMAX_DEC_WIDTH + #define PRINTF_INTMAX_DEC_WIDTH PRINTF_INT64_DEC_WIDTH + #endif + #else + typedef int32_t intmax_t; + typedef uint32_t uintmax_t; + #define INTMAX_MAX INT32_MAX + #define UINTMAX_MAX UINT32_MAX + #define UINTMAX_C(v) UINT32_C(v) + #define INTMAX_C(v) INT32_C(v) + #ifndef PRINTF_INTMAX_MODIFIER + #define PRINTF_INTMAX_MODIFIER PRINTF_INT32_MODIFIER + #endif + #ifndef PRINTF_INTMAX_HEX_WIDTH + #define PRINTF_INTMAX_HEX_WIDTH PRINTF_INT32_HEX_WIDTH + #endif + #ifndef PRINTF_INTMAX_DEC_WIDTH + #define PRINTF_INTMAX_DEC_WIDTH PRINTF_INT32_DEC_WIDTH + #endif + #endif + + /* + * Because this file currently only supports platforms which have + * precise powers of 2 as bit sizes for the default integers, the + * least definitions are all trivial. Its possible that a future + * version of this file could have different definitions. + */ + + #ifndef stdint_least_defined + typedef int8_t int_least8_t; + typedef uint8_t uint_least8_t; + typedef int16_t int_least16_t; + typedef uint16_t uint_least16_t; + typedef int32_t int_least32_t; + typedef uint32_t uint_least32_t; + #define PRINTF_LEAST32_MODIFIER PRINTF_INT32_MODIFIER + #define PRINTF_LEAST16_MODIFIER PRINTF_INT16_MODIFIER + #define UINT_LEAST8_MAX UINT8_MAX + #define INT_LEAST8_MAX INT8_MAX + #define UINT_LEAST16_MAX UINT16_MAX + #define INT_LEAST16_MAX INT16_MAX + #define UINT_LEAST32_MAX UINT32_MAX + #define INT_LEAST32_MAX INT32_MAX + #define INT_LEAST8_MIN INT8_MIN + #define INT_LEAST16_MIN INT16_MIN + #define INT_LEAST32_MIN INT32_MIN + #ifdef stdint_int64_defined + typedef int64_t int_least64_t; + typedef uint64_t uint_least64_t; + #define PRINTF_LEAST64_MODIFIER PRINTF_INT64_MODIFIER + #define UINT_LEAST64_MAX UINT64_MAX + #define INT_LEAST64_MAX INT64_MAX + #define INT_LEAST64_MIN INT64_MIN + #endif + #endif + #undef stdint_least_defined + + /* + * The ANSI C committee pretending to know or specify anything about + * performance is the epitome of misguided arrogance. The mandate of + * this file is to *ONLY* ever support that absolute minimum + * definition of the fast integer types, for compatibility purposes. + * No extensions, and no attempt to suggest what may or may not be a + * faster integer type will ever be made in this file. Developers are + * warned to stay away from these types when using this or any other + * stdint.h. + */ + + typedef int_least8_t int_fast8_t; + typedef uint_least8_t uint_fast8_t; + typedef int_least16_t int_fast16_t; + typedef uint_least16_t uint_fast16_t; + typedef int_least32_t int_fast32_t; + typedef uint_least32_t uint_fast32_t; + #define UINT_FAST8_MAX UINT_LEAST8_MAX + #define INT_FAST8_MAX INT_LEAST8_MAX + #define UINT_FAST16_MAX UINT_LEAST16_MAX + #define INT_FAST16_MAX INT_LEAST16_MAX + #define UINT_FAST32_MAX UINT_LEAST32_MAX + #define INT_FAST32_MAX INT_LEAST32_MAX + #define INT_FAST8_MIN INT_LEAST8_MIN + #define INT_FAST16_MIN INT_LEAST16_MIN + #define INT_FAST32_MIN INT_LEAST32_MIN + #ifdef stdint_int64_defined + typedef int_least64_t int_fast64_t; + typedef uint_least64_t uint_fast64_t; + #define UINT_FAST64_MAX UINT_LEAST64_MAX + #define INT_FAST64_MAX INT_LEAST64_MAX + #define INT_FAST64_MIN INT_LEAST64_MIN + #endif + + #undef stdint_int64_defined + + /* + * Whatever piecemeal, per compiler thing we can do about the wchar_t + * type limits. + */ + + #if defined(__WATCOMC__) || defined(_MSC_VER) || defined (__GNUC__) + #include + #ifndef WCHAR_MIN + #define WCHAR_MIN 0 + #endif + #ifndef WCHAR_MAX + #define WCHAR_MAX ((wchar_t)-1) + #endif + #endif + + /* + * Whatever piecemeal, per compiler/platform thing we can do about the + * (u)intptr_t types and limits. + */ + + #if defined (_MSC_VER) && defined (_UINTPTR_T_DEFINED) + #define STDINT_H_UINTPTR_T_DEFINED + #endif + + #ifndef STDINT_H_UINTPTR_T_DEFINED + #if defined (__alpha__) || defined (__ia64__) || defined (__x86_64__) || defined (_WIN64) + #define stdint_intptr_bits 64 + #elif defined (__WATCOMC__) || defined (__TURBOC__) + #if defined(__TINY__) || defined(__SMALL__) || defined(__MEDIUM__) + #define stdint_intptr_bits 16 + #else + #define stdint_intptr_bits 32 + #endif + #elif defined (__i386__) || defined (_WIN32) || defined (WIN32) + #define stdint_intptr_bits 32 + #elif defined (__INTEL_COMPILER) + /* TODO -- what will Intel do about x86-64? */ + #endif + + #ifdef stdint_intptr_bits + #define stdint_intptr_glue3_i(a,b,c) a##b##c + #define stdint_intptr_glue3(a,b,c) stdint_intptr_glue3_i(a,b,c) + #ifndef PRINTF_INTPTR_MODIFIER + #define PRINTF_INTPTR_MODIFIER stdint_intptr_glue3(PRINTF_INT,stdint_intptr_bits,_MODIFIER) + #endif + #ifndef PTRDIFF_MAX + #define PTRDIFF_MAX stdint_intptr_glue3(INT,stdint_intptr_bits,_MAX) + #endif + #ifndef PTRDIFF_MIN + #define PTRDIFF_MIN stdint_intptr_glue3(INT,stdint_intptr_bits,_MIN) + #endif + #ifndef UINTPTR_MAX + #define UINTPTR_MAX stdint_intptr_glue3(UINT,stdint_intptr_bits,_MAX) + #endif + #ifndef INTPTR_MAX + #define INTPTR_MAX stdint_intptr_glue3(INT,stdint_intptr_bits,_MAX) + #endif + #ifndef INTPTR_MIN + #define INTPTR_MIN stdint_intptr_glue3(INT,stdint_intptr_bits,_MIN) + #endif + #ifndef INTPTR_C + #define INTPTR_C(x) stdint_intptr_glue3(INT,stdint_intptr_bits,_C)(x) + #endif + #ifndef UINTPTR_C + #define UINTPTR_C(x) stdint_intptr_glue3(UINT,stdint_intptr_bits,_C)(x) + #endif + typedef stdint_intptr_glue3(uint, stdint_intptr_bits, _t) uintptr_t; + typedef stdint_intptr_glue3( int, stdint_intptr_bits, _t) intptr_t; + #else + /* TODO -- This following is likely wrong for some platforms, and does + nothing for the definition of uintptr_t. */ + typedef ptrdiff_t intptr_t; + #endif + #define STDINT_H_UINTPTR_T_DEFINED + #endif + + /* + * Assumes sig_atomic_t is signed and we have a 2s complement machine. + */ + + #ifndef SIG_ATOMIC_MAX + #define SIG_ATOMIC_MAX ((((sig_atomic_t) 1) << (sizeof (sig_atomic_t)*CHAR_BIT-1)) - 1) + #endif + +#endif + +#if defined (__TEST_PSTDINT_FOR_CORRECTNESS) + +/* + * Please compile with the maximum warning settings to make sure macros are not + * defined more than once. + */ + +#include +#include +#include + +#define glue3_aux(x,y,z) x ## y ## z +#define glue3(x,y,z) glue3_aux(x,y,z) + +#define DECLU(bits) glue3(uint,bits,_t) glue3(u,bits,=) glue3(UINT,bits,_C) (0); +#define DECLI(bits) glue3(int,bits,_t) glue3(i,bits,=) glue3(INT,bits,_C) (0); + +#define DECL(us,bits) glue3(DECL,us,) (bits) + +#define TESTUMAX(bits) glue3(u,bits,=) glue3(~,u,bits); if (glue3(UINT,bits,_MAX) glue3(!=,u,bits)) printf ("Something wrong with UINT%d_MAX\n", bits) + +int main () { + DECL(I, 8) + DECL(U, 8) + DECL(I, 16) + DECL(U, 16) + DECL(I, 32) + DECL(U, 32) +#ifdef INT64_MAX + DECL(I, 64) + DECL(U, 64) +#endif + intmax_t imax = INTMAX_C(0); + uintmax_t umax = UINTMAX_C(0); + char str0[256], str1[256]; + + sprintf (str0, "%d %x\n", 0, ~0); + + sprintf (str1, "%d %x\n", i8, ~0); + if (0 != strcmp (str0, str1)) { + printf ("Something wrong with i8 : %s\n", str1); + } + sprintf (str1, "%u %x\n", u8, ~0); + if (0 != strcmp (str0, str1)) { + printf ("Something wrong with u8 : %s\n", str1); + } + sprintf (str1, "%d %x\n", i16, ~0); + if (0 != strcmp (str0, str1)) { + printf ("Something wrong with i16 : %s\n", str1); + } + sprintf (str1, "%u %x\n", u16, ~0); + if (0 != strcmp (str0, str1)) { + printf ("Something wrong with u16 : %s\n", str1); + } + sprintf (str1, "%" PRINTF_INT32_MODIFIER "d %x\n", i32, ~0); + if (0 != strcmp (str0, str1)) { + printf ("Something wrong with i32 : %s\n", str1); + } + sprintf (str1, "%" PRINTF_INT32_MODIFIER "u %x\n", u32, ~0); + if (0 != strcmp (str0, str1)) { + printf ("Something wrong with u32 : %s\n", str1); + } +#ifdef INT64_MAX + sprintf (str1, "%" PRINTF_INT64_MODIFIER "d %x\n", i64, ~0); + if (0 != strcmp (str0, str1)) { + printf ("Something wrong with i64 : %s\n", str1); + } +#endif + sprintf (str1, "%" PRINTF_INTMAX_MODIFIER "d %x\n", imax, ~0); + if (0 != strcmp (str0, str1)) { + printf ("Something wrong with imax : %s\n", str1); + } + sprintf (str1, "%" PRINTF_INTMAX_MODIFIER "u %x\n", umax, ~0); + if (0 != strcmp (str0, str1)) { + printf ("Something wrong with umax : %s\n", str1); + } + + TESTUMAX(8); + TESTUMAX(16); + TESTUMAX(32); +#ifdef INT64_MAX + TESTUMAX(64); +#endif + + return EXIT_SUCCESS; +} + +#endif diff --git a/src/rigraph/core/internal/qsort.c b/src/rigraph/core/internal/qsort.c new file mode 100644 index 0000000..f877beb --- /dev/null +++ b/src/rigraph/core/internal/qsort.c @@ -0,0 +1,262 @@ +/*- + * SPDX-License-Identifier: BSD-3-Clause + * + * 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. 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. + */ + +/* This file originates from the following URL: + * + * https://cgit.freebsd.org/src/commit/lib/libc/stdlib/qsort.c?id=7f8f79a5c444a565a32b0c6578b07f8d496f6c49 + * + * Create a diff against the revision given above to see what we have changed + * to facilitate inclusion into igraph + */ + +#include "igraph_qsort.h" + +#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 + +#if defined(I_AM_QSORT_R) +typedef int cmp_t(void *, const void *, const void *); +#elif defined(I_AM_QSORT_S) +typedef int cmp_t(const void *, const void *, void *); +#else +typedef int cmp_t(const void *, const void *); +#endif +static inline char *med3(char *, char *, char *, cmp_t *, void *); + +#define MIN(a, b) ((a) < (b) ? a : b) + +/* + * Qsort routine from Bentley & McIlroy's "Engineering a Sort Function". + */ + +static inline void +swapfunc(char *a, char *b, size_t es) +{ + char t; + + do { + t = *a; + *a++ = *b; + *b++ = t; + } while (--es > 0); +} + +#define vecswap(a, b, n) \ + if ((n) > 0) swapfunc(a, b, n) + +#if defined(I_AM_QSORT_R) +#define CMP(t, x, y) (cmp((t), (x), (y))) +#elif defined(I_AM_QSORT_S) +#define CMP(t, x, y) (cmp((x), (y), (t))) +#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 +#if !defined(I_AM_QSORT_R) && !defined(I_AM_QSORT_S) +__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 )); +} + +/* + * The actual qsort() implementation is static to avoid preemptible calls when + * recursing. Also give them different names for improved debugging. + */ +#if defined(I_AM_QSORT_R) +#define local_qsort local_qsort_r +#elif defined(I_AM_QSORT_S) +#define local_qsort local_qsort_s +#endif +static void +local_qsort(void *a, size_t n, size_t es, cmp_t *cmp, void *thunk) +{ + char *pa, *pb, *pc, *pd, *pl, *pm, *pn; + size_t d1, d2; + int cmp_result; + int swap_cnt; + +loop: + 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) + swapfunc(pl, pl - es, es); + return; + } + pm = (char *)a + (n / 2) * es; + if (n > 7) { + pl = a; + pn = (char *)a + (n - 1) * es; + if (n > 40) { + size_t 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); + } + swapfunc(a, pm, es); + pa = pb = (char *)a + es; + + pc = pd = (char *)a + (n - 1) * es; + for (;;) { + while (pb <= pc && (cmp_result = CMP(thunk, pb, a)) <= 0) { + if (cmp_result == 0) { + swap_cnt = 1; + swapfunc(pa, pb, es); + pa += es; + } + pb += es; + } + while (pb <= pc && (cmp_result = CMP(thunk, pc, a)) >= 0) { + if (cmp_result == 0) { + swap_cnt = 1; + swapfunc(pc, pd, es); + pd -= es; + } + pc -= es; + } + if (pb > pc) + break; + swapfunc(pb, pc, es); + 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) + swapfunc(pl, pl - es, es); + return; + } + + pn = (char *)a + n * es; + d1 = MIN(pa - (char *)a, pb - pa); + vecswap(a, pb - d1, d1); + /* + * Cast es to preserve signedness of right-hand side of MIN() + * expression, to avoid sign ambiguity in the implied comparison. es + * is safely within [0, SSIZE_MAX]. + */ + d1 = MIN(pd - pc, pn - pd - (ptrdiff_t)es); + vecswap(pb, pn - d1, d1); + + d1 = pb - pa; + d2 = pd - pc; + if (d1 <= d2) { + /* Recurse on left partition, then iterate on right partition */ + if (d1 > es) { + local_qsort(a, d1 / es, es, cmp, thunk); + } + if (d2 > es) { + /* Iterate rather than recurse to save stack space */ + /* qsort(pn - d2, d2 / es, es, cmp); */ + a = pn - d2; + n = d2 / es; + goto loop; + } + } else { + /* Recurse on right partition, then iterate on left partition */ + if (d2 > es) { + local_qsort(pn - d2, d2 / es, es, cmp, thunk); + } + if (d1 > es) { + /* Iterate rather than recurse to save stack space */ + /* qsort(a, d1 / es, es, cmp); */ + n = d1 / es; + goto loop; + } + } +} + +#if defined(I_AM_QSORT_R) +void +igraph_qsort_r(void *a, size_t n, size_t es, void *thunk, cmp_t *cmp) +{ + local_qsort_r(a, n, es, cmp, thunk); +} +#elif defined(I_AM_QSORT_S) +errno_t +igraph_qsort_s(void *a, rsize_t n, rsize_t es, cmp_t *cmp, void *thunk) +{ + if (n > RSIZE_MAX) { + __throw_constraint_handler_s("qsort_s : n > RSIZE_MAX", EINVAL); + return (EINVAL); + } else if (es > RSIZE_MAX) { + __throw_constraint_handler_s("qsort_s : es > RSIZE_MAX", + EINVAL); + return (EINVAL); + } else if (n != 0) { + if (a == NULL) { + __throw_constraint_handler_s("qsort_s : a == NULL", + EINVAL); + return (EINVAL); + } else if (cmp == NULL) { + __throw_constraint_handler_s("qsort_s : cmp == NULL", + EINVAL); + return (EINVAL); + } + } + + local_qsort_s(a, n, es, cmp, thunk); + return (0); +} +#else +void +igraph_qsort(void *a, size_t n, size_t es, cmp_t *cmp) +{ + local_qsort(a, n, es, cmp, NULL); +} +#endif diff --git a/src/rigraph/core/internal/qsort_r.c b/src/rigraph/core/internal/qsort_r.c new file mode 100644 index 0000000..f7c0e54 --- /dev/null +++ b/src/rigraph/core/internal/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/src/rigraph/core/internal/zeroin.c b/src/rigraph/core/internal/zeroin.c new file mode 100644 index 0000000..3d1d8d0 --- /dev/null +++ b/src/rigraph/core/internal/zeroin.c @@ -0,0 +1,204 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2007-2012 Gabor Csardi + 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_nongraph.h" +#include "igraph_types.h" + +#include "core/interruption.h" + +#include +#include + +#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/src/rigraph/core/io/dimacs.c b/src/rigraph/core/io/dimacs.c new file mode 100644 index 0000000..c615b26 --- /dev/null +++ b/src/rigraph/core/io/dimacs.c @@ -0,0 +1,318 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2005-2020 The igraph development 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., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_foreign.h" + +#include "igraph_constructors.h" +#include "igraph_interface.h" +#include "igraph_iterators.h" + +#include "core/interruption.h" + +#include + +/** + * \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/ + * + * + * 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 c the line is a comment line and it is + * ignored. There is one problem line (p 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 + * (min, max or asn), the + * number of vertices and number of edges in the graph. + * Exactly two node identification lines are expected + * (n), 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 s (=source) or t + * (=target). Arc lines start with a and have three + * fields: the source vertex, the target vertex and the edge capacity. + * + * + * 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 + * NULL. + * \param target Pointer to an integer, the (igraph) id of the target + * node will be stored here. It is ignored if NULL. + * \param capacity Pointer to an initialized vector, the capacity of + * the edges will be stored here if not NULL. + * \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; +} + +/** + * \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/ + * + * + * 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; +} diff --git a/src/rigraph/core/io/dl-header.h b/src/rigraph/core/io/dl-header.h new file mode 100644 index 0000000..fe4d340 --- /dev/null +++ b/src/rigraph/core/io/dl-header.h @@ -0,0 +1,44 @@ +/* + IGraph library. + Copyright (C) 2009-2012 Gabor Csardi + 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_error.h" +#include "igraph_types.h" + +#include "core/trie.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/src/rigraph/core/io/dl-lexer.c b/src/rigraph/core/io/dl-lexer.c new file mode 100644 index 0000000..e4ae5d6 --- /dev/null +++ b/src/rigraph/core/io/dl-lexer.c @@ -0,0 +1,2478 @@ +#line 2 "src/core/io/dl-lexer.c" + +#line 4 "src/core/io/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 6 +#define YY_FLEX_SUBMINOR_VERSION 4 +#if YY_FLEX_SUBMINOR_VERSION > 0 +#define FLEX_BETA +#endif + +#ifdef yy_create_buffer +#define igraph_dl_yy_create_buffer_ALREADY_DEFINED +#else +#define yy_create_buffer igraph_dl_yy_create_buffer +#endif + +#ifdef yy_delete_buffer +#define igraph_dl_yy_delete_buffer_ALREADY_DEFINED +#else +#define yy_delete_buffer igraph_dl_yy_delete_buffer +#endif + +#ifdef yy_scan_buffer +#define igraph_dl_yy_scan_buffer_ALREADY_DEFINED +#else +#define yy_scan_buffer igraph_dl_yy_scan_buffer +#endif + +#ifdef yy_scan_string +#define igraph_dl_yy_scan_string_ALREADY_DEFINED +#else +#define yy_scan_string igraph_dl_yy_scan_string +#endif + +#ifdef yy_scan_bytes +#define igraph_dl_yy_scan_bytes_ALREADY_DEFINED +#else +#define yy_scan_bytes igraph_dl_yy_scan_bytes +#endif + +#ifdef yy_init_buffer +#define igraph_dl_yy_init_buffer_ALREADY_DEFINED +#else +#define yy_init_buffer igraph_dl_yy_init_buffer +#endif + +#ifdef yy_flush_buffer +#define igraph_dl_yy_flush_buffer_ALREADY_DEFINED +#else +#define yy_flush_buffer igraph_dl_yy_flush_buffer +#endif + +#ifdef yy_load_buffer_state +#define igraph_dl_yy_load_buffer_state_ALREADY_DEFINED +#else +#define yy_load_buffer_state igraph_dl_yy_load_buffer_state +#endif + +#ifdef yy_switch_to_buffer +#define igraph_dl_yy_switch_to_buffer_ALREADY_DEFINED +#else +#define yy_switch_to_buffer igraph_dl_yy_switch_to_buffer +#endif + +#ifdef yypush_buffer_state +#define igraph_dl_yypush_buffer_state_ALREADY_DEFINED +#else +#define yypush_buffer_state igraph_dl_yypush_buffer_state +#endif + +#ifdef yypop_buffer_state +#define igraph_dl_yypop_buffer_state_ALREADY_DEFINED +#else +#define yypop_buffer_state igraph_dl_yypop_buffer_state +#endif + +#ifdef yyensure_buffer_stack +#define igraph_dl_yyensure_buffer_stack_ALREADY_DEFINED +#else +#define yyensure_buffer_stack igraph_dl_yyensure_buffer_stack +#endif + +#ifdef yylex +#define igraph_dl_yylex_ALREADY_DEFINED +#else +#define yylex igraph_dl_yylex +#endif + +#ifdef yyrestart +#define igraph_dl_yyrestart_ALREADY_DEFINED +#else +#define yyrestart igraph_dl_yyrestart +#endif + +#ifdef yylex_init +#define igraph_dl_yylex_init_ALREADY_DEFINED +#else +#define yylex_init igraph_dl_yylex_init +#endif + +#ifdef yylex_init_extra +#define igraph_dl_yylex_init_extra_ALREADY_DEFINED +#else +#define yylex_init_extra igraph_dl_yylex_init_extra +#endif + +#ifdef yylex_destroy +#define igraph_dl_yylex_destroy_ALREADY_DEFINED +#else +#define yylex_destroy igraph_dl_yylex_destroy +#endif + +#ifdef yyget_debug +#define igraph_dl_yyget_debug_ALREADY_DEFINED +#else +#define yyget_debug igraph_dl_yyget_debug +#endif + +#ifdef yyset_debug +#define igraph_dl_yyset_debug_ALREADY_DEFINED +#else +#define yyset_debug igraph_dl_yyset_debug +#endif + +#ifdef yyget_extra +#define igraph_dl_yyget_extra_ALREADY_DEFINED +#else +#define yyget_extra igraph_dl_yyget_extra +#endif + +#ifdef yyset_extra +#define igraph_dl_yyset_extra_ALREADY_DEFINED +#else +#define yyset_extra igraph_dl_yyset_extra +#endif + +#ifdef yyget_in +#define igraph_dl_yyget_in_ALREADY_DEFINED +#else +#define yyget_in igraph_dl_yyget_in +#endif + +#ifdef yyset_in +#define igraph_dl_yyset_in_ALREADY_DEFINED +#else +#define yyset_in igraph_dl_yyset_in +#endif + +#ifdef yyget_out +#define igraph_dl_yyget_out_ALREADY_DEFINED +#else +#define yyget_out igraph_dl_yyget_out +#endif + +#ifdef yyset_out +#define igraph_dl_yyset_out_ALREADY_DEFINED +#else +#define yyset_out igraph_dl_yyset_out +#endif + +#ifdef yyget_leng +#define igraph_dl_yyget_leng_ALREADY_DEFINED +#else +#define yyget_leng igraph_dl_yyget_leng +#endif + +#ifdef yyget_text +#define igraph_dl_yyget_text_ALREADY_DEFINED +#else +#define yyget_text igraph_dl_yyget_text +#endif + +#ifdef yyget_lineno +#define igraph_dl_yyget_lineno_ALREADY_DEFINED +#else +#define yyget_lineno igraph_dl_yyget_lineno +#endif + +#ifdef yyset_lineno +#define igraph_dl_yyset_lineno_ALREADY_DEFINED +#else +#define yyset_lineno igraph_dl_yyset_lineno +#endif + +#ifdef yyget_column +#define igraph_dl_yyget_column_ALREADY_DEFINED +#else +#define yyget_column igraph_dl_yyget_column +#endif + +#ifdef yyset_column +#define igraph_dl_yyset_column_ALREADY_DEFINED +#else +#define yyset_column igraph_dl_yyset_column +#endif + +#ifdef yywrap +#define igraph_dl_yywrap_ALREADY_DEFINED +#else +#define yywrap igraph_dl_yywrap +#endif + +#ifdef yyget_lval +#define igraph_dl_yyget_lval_ALREADY_DEFINED +#else +#define yyget_lval igraph_dl_yyget_lval +#endif + +#ifdef yyset_lval +#define igraph_dl_yyset_lval_ALREADY_DEFINED +#else +#define yyset_lval igraph_dl_yyset_lval +#endif + +#ifdef yyget_lloc +#define igraph_dl_yyget_lloc_ALREADY_DEFINED +#else +#define yyget_lloc igraph_dl_yyget_lloc +#endif + +#ifdef yyset_lloc +#define igraph_dl_yyset_lloc_ALREADY_DEFINED +#else +#define yyset_lloc igraph_dl_yyset_lloc +#endif + +#ifdef yyalloc +#define igraph_dl_yyalloc_ALREADY_DEFINED +#else +#define yyalloc igraph_dl_yyalloc +#endif + +#ifdef yyrealloc +#define igraph_dl_yyrealloc_ALREADY_DEFINED +#else +#define yyrealloc igraph_dl_yyrealloc +#endif + +#ifdef yyfree +#define igraph_dl_yyfree_ALREADY_DEFINED +#else +#define yyfree igraph_dl_yyfree +#endif + +/* First, we deal with platform-specific or compiler-specific issues. */ + +/* begin standard C headers. */ +#include +#include +#include +#include + +/* end standard C headers. */ + +/* flex integer type definitions */ + +#ifndef FLEXINT_H +#define FLEXINT_H + +/* C99 systems have . 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 +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; +#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; + +/* 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 + +#ifndef SIZE_MAX +#define SIZE_MAX (~(size_t)0) +#endif + +#endif /* ! C99 */ + +#endif /* ! FLEXINT_H */ + +/* begin standard C++ headers. */ + +/* TODO: this is always defined, so inline it */ +#define yyconst const + +#if defined(__GNUC__) && __GNUC__ >= 3 +#define yynoreturn __attribute__((__noreturn__)) +#else +#define yynoreturn +#endif + +/* Returned upon end-of-file. */ +#define YY_NULL 0 + +/* Promotes a possibly negative, possibly signed char to an + * integer in range [0..255] for use as an array index. + */ +#define YY_SC_TO_UI(c) ((YY_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 yyrestart( yyin , yyscanner ) +#define YY_END_OF_BUFFER_CHAR 0 + +/* Size of default input buffer. */ +#ifndef YY_BUF_SIZE +#ifdef __ia64__ +/* On IA-64, the buffer size is 16k, not 8k. + * Moreover, YY_BUF_SIZE is 2*YY_READ_BUF_SIZE in the general case. + * Ditto for the __ia64__ case accordingly. + */ +#define YY_BUF_SIZE 32768 +#else +#define YY_BUF_SIZE 16384 +#endif /* __ia64__ */ +#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) + #define YY_LINENO_REWIND_TO(ptr) + +/* 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. + */ + int yy_buf_size; + + /* Number of characters read into yy_ch_buf, not including EOB + * characters. + */ + int 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 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 yyrestart ( FILE *input_file , yyscan_t yyscanner ); +void yy_switch_to_buffer ( YY_BUFFER_STATE new_buffer , yyscan_t yyscanner ); +YY_BUFFER_STATE yy_create_buffer ( FILE *file, int size , yyscan_t yyscanner ); +void yy_delete_buffer ( YY_BUFFER_STATE b , yyscan_t yyscanner ); +void yy_flush_buffer ( YY_BUFFER_STATE b , yyscan_t yyscanner ); +void yypush_buffer_state ( YY_BUFFER_STATE new_buffer , yyscan_t yyscanner ); +void yypop_buffer_state ( yyscan_t yyscanner ); + +static void yyensure_buffer_stack ( yyscan_t yyscanner ); +static void yy_load_buffer_state ( yyscan_t yyscanner ); +static void yy_init_buffer ( YY_BUFFER_STATE b, FILE *file , yyscan_t yyscanner ); +#define YY_FLUSH_BUFFER yy_flush_buffer( YY_CURRENT_BUFFER , yyscanner) + +YY_BUFFER_STATE yy_scan_buffer ( char *base, yy_size_t size , yyscan_t yyscanner ); +YY_BUFFER_STATE yy_scan_string ( const char *yy_str , yyscan_t yyscanner ); +YY_BUFFER_STATE yy_scan_bytes ( const char *bytes, int len , yyscan_t yyscanner ); + +void *yyalloc ( yy_size_t , yyscan_t yyscanner ); +void *yyrealloc ( void *, yy_size_t , yyscan_t yyscanner ); +void yyfree ( void * , yyscan_t yyscanner ); + +#define yy_new_buffer yy_create_buffer +#define yy_set_interactive(is_interactive) \ + { \ + if ( ! YY_CURRENT_BUFFER ){ \ + yyensure_buffer_stack (yyscanner); \ + YY_CURRENT_BUFFER_LVALUE = \ + 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 ){\ + yyensure_buffer_stack (yyscanner); \ + YY_CURRENT_BUFFER_LVALUE = \ + 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(yyscanner) (/*CONSTCOND*/1) +#define YY_SKIP_YYWRAP +typedef flex_uint8_t 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 __attribute__((unused)) yy_fatal_error ( const 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 = (int) (yy_cp - yy_bp); \ + yyg->yy_hold_char = *yy_cp; \ + *yy_cp = '\0'; \ + yyg->yy_c_buf_p = yy_cp; +#define YY_NUM_RULES 24 +#define YY_END_OF_BUFFER 25 +/* 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 const flex_int16_t yy_accept[129] = + { 0, + 0, 0, 0, 0, 0, 0, 18, 18, 21, 21, + 25, 23, 22, 1, 1, 4, 23, 23, 23, 23, + 12, 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 const YY_CHAR 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 const YY_CHAR 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 const flex_int16_t yy_base[138] = + { 0, + 0, 22, 44, 64, 84, 94, 104, 114, 124, 134, + 287, 288, 4, 282, 282, 2, 1, 260, 269, 15, + 29, 288, 39, 50, 0, 288, 34, 0, 52, 19, + 64, 0, 54, 51, 74, 288, 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, 288, 246, 246, 189, 193, 195, 197, 199, + 205, 218, 209, 288, 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, 288, 247, 250, + 253, 256, 259, 262, 7, 6, 0 + } ; + +static const flex_int16_t yy_def[138] = + { 0, + 129, 129, 130, 130, 131, 131, 132, 132, 133, 133, + 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, + 134, 128, 134, 134, 135, 128, 135, 136, 128, 136, + 136, 137, 128, 137, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 134, 128, 134, 134, 135, 128, 136, + 128, 136, 136, 136, 137, 128, 137, 128, 128, 128, + 128, 128, 134, 134, 136, 136, 136, 128, 128, 128, + 134, 134, 128, 128, 128, 134, 134, 128, 128, 134, + 128, 128, 128, 128, 128, 82, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, + + 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 0, 128, 128, + 128, 128, 128, 128, 128, 128, 128 + } ; + +static const flex_int16_t yy_nxt[318] = + { 0, + 55, 13, 14, 15, 13, 35, 50, 48, 35, 16, + 16, 37, 37, 128, 38, 17, 42, 18, 128, 42, + 19, 39, 20, 13, 14, 15, 13, 43, 52, 52, + 45, 16, 16, 45, 125, 49, 121, 17, 49, 18, + 45, 120, 19, 45, 20, 12, 14, 15, 22, 119, + 22, 45, 46, 51, 45, 56, 51, 118, 56, 23, + 57, 57, 117, 47, 24, 12, 14, 15, 22, 116, + 22, 115, 53, 52, 52, 35, 37, 37, 35, 23, + 54, 65, 65, 114, 24, 26, 14, 15, 26, 59, + 12, 113, 59, 27, 27, 26, 14, 15, 26, 62, + + 12, 112, 62, 27, 27, 29, 14, 15, 29, 45, + 12, 30, 45, 31, 31, 29, 14, 15, 29, 45, + 12, 30, 45, 31, 31, 33, 14, 15, 33, 45, + 12, 111, 45, 34, 34, 33, 14, 15, 33, 42, + 12, 110, 42, 34, 34, 45, 49, 109, 45, 49, + 43, 108, 51, 56, 63, 51, 56, 107, 64, 53, + 52, 52, 57, 57, 66, 106, 66, 54, 67, 67, + 59, 62, 45, 59, 62, 45, 45, 67, 67, 45, + 65, 65, 67, 67, 71, 45, 45, 54, 45, 45, + 45, 72, 105, 45, 45, 76, 81, 45, 83, 81, + + 85, 83, 104, 85, 103, 77, 81, 82, 84, 81, + 83, 85, 124, 83, 85, 124, 102, 82, 80, 86, + 122, 126, 86, 122, 126, 90, 90, 101, 122, 122, + 123, 122, 122, 124, 87, 88, 124, 100, 127, 126, + 89, 127, 126, 127, 99, 98, 127, 12, 12, 12, + 21, 21, 21, 25, 25, 25, 28, 28, 28, 32, + 32, 32, 44, 44, 97, 96, 95, 94, 93, 92, + 91, 79, 78, 75, 74, 73, 70, 69, 68, 61, + 60, 58, 41, 40, 36, 36, 128, 11, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, + + 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128 + } ; + +static const flex_int16_t yy_chk[318] = + { 0, + 137, 1, 1, 1, 1, 13, 136, 135, 13, 1, + 1, 16, 16, 0, 17, 1, 20, 1, 0, 20, + 1, 17, 1, 2, 2, 2, 2, 20, 30, 30, + 21, 2, 2, 21, 120, 27, 117, 2, 27, 2, + 23, 116, 2, 23, 2, 3, 3, 3, 3, 115, + 3, 24, 23, 29, 24, 33, 29, 114, 33, 3, + 34, 34, 113, 24, 3, 4, 4, 4, 4, 112, + 4, 111, 31, 31, 31, 35, 37, 37, 35, 4, + 31, 53, 53, 110, 4, 5, 5, 5, 5, 39, + 5, 109, 39, 5, 5, 6, 6, 6, 6, 43, + + 6, 108, 43, 6, 6, 7, 7, 7, 7, 44, + 7, 7, 44, 7, 7, 8, 8, 8, 8, 45, + 8, 8, 45, 8, 8, 9, 9, 9, 9, 46, + 9, 107, 46, 9, 9, 10, 10, 10, 10, 42, + 10, 106, 42, 10, 10, 47, 49, 105, 47, 49, + 42, 104, 51, 56, 46, 51, 56, 103, 47, 52, + 52, 52, 57, 57, 54, 102, 54, 52, 54, 54, + 59, 62, 63, 59, 62, 63, 64, 66, 66, 64, + 65, 65, 67, 67, 63, 71, 72, 65, 71, 72, + 76, 64, 101, 76, 77, 71, 78, 77, 79, 78, + + 80, 79, 100, 80, 99, 72, 81, 78, 79, 81, + 83, 85, 119, 83, 85, 119, 98, 81, 77, 82, + 118, 121, 82, 118, 121, 83, 85, 97, 122, 123, + 118, 122, 123, 124, 82, 82, 124, 96, 125, 126, + 82, 125, 126, 127, 95, 94, 127, 129, 129, 129, + 130, 130, 130, 131, 131, 131, 132, 132, 132, 133, + 133, 133, 134, 134, 93, 92, 91, 90, 89, 88, + 87, 75, 74, 70, 69, 68, 61, 60, 58, 41, + 40, 38, 19, 18, 15, 14, 11, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, + + 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128 + } ; + +/* 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/core/io/dl-lexer.l" +/* + IGraph library. + Copyright (C) 2007-2012 Gabor Csardi + 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/core/io/dl-lexer.l" + +/* + IGraph library. + Copyright (C) 2007-2012 Gabor Csardi + 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 +#include + +#include "io/dl-header.h" +#include "io/dl-parser.h" + +#define YY_EXTRA_TYPE igraph_i_dl_parsedata_t* +#define YY_USER_ACTION yylloc->first_line = yylineno; +#define YY_FATAL_ERROR(msg) IGRAPH_FATAL("Error in DL parser: " # msg) +#ifdef USING_R +#define fprintf(file, msg, ...) (1) +#ifdef stdout +# undef stdout +#endif +#define stdout 0 +#endif +#line 827 "src/core/io/dl-lexer.c" +#define YY_NO_INPUT 1 + +#line 830 "src/core/io/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 +#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; + int yy_n_chars; + int 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 yylex_init (yyscan_t* scanner); + +int 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 yylex_destroy ( yyscan_t yyscanner ); + +int yyget_debug ( yyscan_t yyscanner ); + +void yyset_debug ( int debug_flag , yyscan_t yyscanner ); + +YY_EXTRA_TYPE yyget_extra ( yyscan_t yyscanner ); + +void yyset_extra ( YY_EXTRA_TYPE user_defined , yyscan_t yyscanner ); + +FILE *yyget_in ( yyscan_t yyscanner ); + +void yyset_in ( FILE * _in_str , yyscan_t yyscanner ); + +FILE *yyget_out ( yyscan_t yyscanner ); + +void yyset_out ( FILE * _out_str , yyscan_t yyscanner ); + + yy_size_t yyget_leng ( yyscan_t yyscanner ); + +char *yyget_text ( yyscan_t yyscanner ); + +int yyget_lineno ( yyscan_t yyscanner ); + +void yyset_lineno ( int _line_number , yyscan_t yyscanner ); + +int yyget_column ( yyscan_t yyscanner ); + +void yyset_column ( int _column_no , yyscan_t yyscanner ); + +YYSTYPE * yyget_lval ( yyscan_t yyscanner ); + +void yyset_lval ( YYSTYPE * yylval_param , yyscan_t yyscanner ); + + YYLTYPE *yyget_lloc ( yyscan_t yyscanner ); + + void 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 yywrap ( yyscan_t yyscanner ); +#else +extern int yywrap ( yyscan_t yyscanner ); +#endif +#endif + +#ifndef YY_NO_UNPUT + +#endif + +#ifndef yytext_ptr +static void yy_flex_strncpy ( char *, const char *, int , yyscan_t yyscanner); +#endif + +#ifdef YY_NEED_STRLEN +static int yy_flex_strlen ( const 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 +#ifdef __ia64__ +/* On IA-64, the buffer size is 16k, not 8k */ +#define YY_READ_BUF_SIZE 16384 +#else +#define YY_READ_BUF_SIZE 8192 +#endif /* __ia64__ */ +#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 do { if (fwrite( yytext, (size_t) yyleng, 1, yyout )) {} } while (0) +#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 = '*'; \ + int 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 = (int) fread(buf, 1, (yy_size_t) 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 yylex \ + (YYSTYPE * yylval_param, YYLTYPE * yylloc_param , yyscan_t yyscanner); + +#define YY_DECL int 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 /*LINTED*/break; +#endif + +#define YY_RULE_SETUP \ + YY_USER_ACTION + +/** The main scanner function which does all the work. + */ +YY_DECL +{ + yy_state_type yy_current_state; + char *yy_cp, *yy_bp; + int yy_act; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + 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 ) { + yyensure_buffer_stack (yyscanner); + YY_CURRENT_BUFFER_LVALUE = + yy_create_buffer( yyin, YY_BUF_SIZE , yyscanner); + } + + yy_load_buffer_state( yyscanner ); + } + + { +#line 80 "src/core/io/dl-lexer.l" + + +#line 1119 "src/core/io/dl-lexer.c" + + while ( /*CONSTCOND*/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 + { + 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 >= 129 ) + yy_c = yy_meta[yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c]; + ++yy_cp; + } + while ( yy_base[yy_current_state] != 288 ); + +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 82 "src/core/io/dl-lexer.l" +{ return NEWLINE; } + YY_BREAK +case 2: +YY_RULE_SETUP +#line 84 "src/core/io/dl-lexer.l" +{ return DL; } + YY_BREAK +case 3: +YY_RULE_SETUP +#line 85 "src/core/io/dl-lexer.l" +{ + return NEQ; } + YY_BREAK +case 4: +YY_RULE_SETUP +#line 87 "src/core/io/dl-lexer.l" +{ return NUM; } + YY_BREAK +case 5: +YY_RULE_SETUP +#line 89 "src/core/io/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 100 "src/core/io/dl-lexer.l" +{ BEGIN(LABELM); return LABELS; } + YY_BREAK +case 7: +YY_RULE_SETUP +#line 101 "src/core/io/dl-lexer.l" +{ + return LABELSEMBEDDED; } + YY_BREAK +case 8: +YY_RULE_SETUP +#line 103 "src/core/io/dl-lexer.l" +{ + yyextra->mode=0; return FORMATFULLMATRIX; } + YY_BREAK +case 9: +YY_RULE_SETUP +#line 105 "src/core/io/dl-lexer.l" +{ + yyextra->mode=1; return FORMATEDGELIST1; } + YY_BREAK +case 10: +YY_RULE_SETUP +#line 107 "src/core/io/dl-lexer.l" +{ + yyextra->mode=2; return FORMATNODELIST1; } + YY_BREAK +case 11: +YY_RULE_SETUP +#line 110 "src/core/io/dl-lexer.l" +{ /* eaten up */ } + YY_BREAK +case 12: +YY_RULE_SETUP +#line 111 "src/core/io/dl-lexer.l" +{ return LABEL; } + YY_BREAK +case 13: +YY_RULE_SETUP +#line 113 "src/core/io/dl-lexer.l" +{ return DIGIT; } + YY_BREAK +case 14: +YY_RULE_SETUP +#line 114 "src/core/io/dl-lexer.l" +{ return LABEL; } + YY_BREAK +case 15: +YY_RULE_SETUP +#line 115 "src/core/io/dl-lexer.l" +{ } + YY_BREAK +case 16: +YY_RULE_SETUP +#line 117 "src/core/io/dl-lexer.l" +{ return NUM; } + YY_BREAK +case 17: +YY_RULE_SETUP +#line 118 "src/core/io/dl-lexer.l" +{ return LABEL; } + YY_BREAK +case 18: +YY_RULE_SETUP +#line 119 "src/core/io/dl-lexer.l" +{ } + YY_BREAK +case 19: +YY_RULE_SETUP +#line 121 "src/core/io/dl-lexer.l" +{ return NUM; } + YY_BREAK +case 20: +YY_RULE_SETUP +#line 122 "src/core/io/dl-lexer.l" +{ return LABEL; } + YY_BREAK +case 21: +YY_RULE_SETUP +#line 123 "src/core/io/dl-lexer.l" +{ } + YY_BREAK +case 22: +YY_RULE_SETUP +#line 125 "src/core/io/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 127 "src/core/io/dl-lexer.l" +{ + if (yyextra->eof) { + yyterminate(); + } else { + yyextra->eof=1; + BEGIN(INITIAL); + return EOFF; + } + } + YY_BREAK +case 23: +YY_RULE_SETUP +#line 137 "src/core/io/dl-lexer.l" +{ return 0; } + YY_BREAK +case 24: +YY_RULE_SETUP +#line 138 "src/core/io/dl-lexer.l" +YY_FATAL_ERROR( "flex scanner jammed" ); + YY_BREAK +#line 1327 "src/core/io/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 + * 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 ( 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 user's declarations */ +} /* end of 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; + char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf; + char *source = yyg->yytext_ptr; + 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 + { + int 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_LVALUE; + + int yy_c_buf_p_offset = + (int) (yyg->yy_c_buf_p - b->yy_ch_buf); + + if ( b->yy_is_our_buffer ) + { + int 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. */ + yyrealloc( (void *) b->yy_ch_buf, + (yy_size_t) (b->yy_buf_size + 2) , yyscanner ); + } + else + /* Can't grow it, we don't own it. */ + b->yy_ch_buf = NULL; + + 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; + 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 ((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. */ + int new_size = yyg->yy_n_chars + number_to_move + (yyg->yy_n_chars >> 1); + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) yyrealloc( + (void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf, (yy_size_t) new_size , yyscanner ); + if ( ! YY_CURRENT_BUFFER_LVALUE->yy_ch_buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_get_next_buffer()" ); + /* "- 2" to take care of EOB's */ + YY_CURRENT_BUFFER_LVALUE->yy_buf_size = (int) (new_size - 2); + } + + 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) +{ + yy_state_type yy_current_state; + 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 ) + { + 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 >= 129 ) + yy_c = yy_meta[yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + 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) +{ + int yy_is_jam; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* This var may be unused depending upon options. */ + char *yy_cp = yyg->yy_c_buf_p; + + 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 >= 129 ) + yy_c = yy_meta[yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c]; + yy_is_jam = (yy_current_state == 128); + + (void)yyg; + return yy_is_jam ? 0 : yy_current_state; +} + +#ifndef YY_NO_UNPUT + +#endif + +#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 */ + int offset = (int) (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. */ + yyrestart( yyin , yyscanner); + + /*FALLTHROUGH*/ + + case EOB_ACT_END_OF_FILE: + { + if ( 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 yyrestart (FILE * input_file , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + if ( ! YY_CURRENT_BUFFER ){ + yyensure_buffer_stack (yyscanner); + YY_CURRENT_BUFFER_LVALUE = + yy_create_buffer( yyin, YY_BUF_SIZE , yyscanner); + } + + yy_init_buffer( YY_CURRENT_BUFFER, input_file , yyscanner); + yy_load_buffer_state( yyscanner ); +} + +/** Switch to a different input buffer. + * @param new_buffer The new input buffer. + * @param yyscanner The scanner object. + */ + void 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 + * yypop_buffer_state(); + * yypush_buffer_state(new_buffer); + */ + 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; + yy_load_buffer_state( yyscanner ); + + /* We don't actually know whether we did this switch during + * EOF (yywrap()) processing, but the only time this flag + * is looked at is after yywrap() is called, so it's safe + * to go ahead and always set it. + */ + yyg->yy_did_buffer_switch_on_eof = 1; +} + +static void 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 yy_create_buffer (FILE * file, int size , yyscan_t yyscanner) +{ + YY_BUFFER_STATE b; + + b = (YY_BUFFER_STATE) yyalloc( sizeof( struct yy_buffer_state ) , yyscanner ); + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in 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 *) yyalloc( (yy_size_t) (b->yy_buf_size + 2) , yyscanner ); + if ( ! b->yy_ch_buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); + + b->yy_is_our_buffer = 1; + + yy_init_buffer( b, file , yyscanner); + + return b; +} + +/** Destroy the buffer. + * @param b a buffer created with yy_create_buffer() + * @param yyscanner The scanner object. + */ + void 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 ) + yyfree( (void *) b->yy_ch_buf , yyscanner ); + + yyfree( (void *) b , yyscanner ); +} + +/* Initializes or reinitializes a buffer. + * This function is sometimes called more than once on the same buffer, + * such as during a yyrestart() or at EOF. + */ + static void yy_init_buffer (YY_BUFFER_STATE b, FILE * file , yyscan_t yyscanner) + +{ + int oerrno = errno; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + yy_flush_buffer( b , yyscanner); + + b->yy_input_file = file; + b->yy_fill_buffer = 1; + + /* If b is the current buffer, then yy_init_buffer was _probably_ + * called from 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 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 ) + 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 yypush_buffer_state (YY_BUFFER_STATE new_buffer , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + if (new_buffer == NULL) + return; + + yyensure_buffer_stack(yyscanner); + + /* This block is copied from 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 yy_switch_to_buffer. */ + 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 yypop_buffer_state (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + if (!YY_CURRENT_BUFFER) + return; + + 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) { + 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 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; /* After all that talk, this was set to 1 anyways... */ + yyg->yy_buffer_stack = (struct yy_buffer_state**)yyalloc + (num_to_alloc * sizeof(struct yy_buffer_state*) + , yyscanner); + if ( ! yyg->yy_buffer_stack ) + YY_FATAL_ERROR( "out of dynamic memory in 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. */ + yy_size_t grow_size = 8 /* arbitrary grow size */; + + num_to_alloc = yyg->yy_buffer_stack_max + grow_size; + yyg->yy_buffer_stack = (struct yy_buffer_state**)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 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 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 NULL; + + b = (YY_BUFFER_STATE) yyalloc( sizeof( struct yy_buffer_state ) , yyscanner ); + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in yy_scan_buffer()" ); + + b->yy_buf_size = (int) (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 = NULL; + 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; + + yy_switch_to_buffer( b , yyscanner ); + + return b; +} + +/** Setup the input buffer state to scan a string. The next call to 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 + * yy_scan_bytes() instead. + */ +YY_BUFFER_STATE yy_scan_string (const char * yystr , yyscan_t yyscanner) +{ + + return yy_scan_bytes( yystr, (int) strlen(yystr) , yyscanner); +} + +/** Setup the input buffer state to scan the given bytes. The next call to yylex() will + * scan from a @e copy of @a bytes. + * @param yybytes the byte buffer to scan + * @param _yybytes_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 yy_scan_bytes (const char * yybytes, int _yybytes_len , yyscan_t yyscanner) +{ + YY_BUFFER_STATE b; + char *buf; + yy_size_t n; + int i; + + /* Get memory for full buffer, including space for trailing EOB's. */ + n = (yy_size_t) (_yybytes_len + 2); + buf = (char *) yyalloc( n , yyscanner ); + if ( ! buf ) + YY_FATAL_ERROR( "out of dynamic memory in 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 = yy_scan_buffer( buf, n , yyscanner); + if ( ! b ) + YY_FATAL_ERROR( "bad buffer in 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 __attribute__((unused)) yy_fatal_error (const char* msg , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + (void)yyg; + fprintf( stderr, "%s\n", msg ); +} + +/* 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 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 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 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 *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 *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 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 *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 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 line number + * @param yyscanner The scanner object. + */ +void 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( "yyset_lineno called with no buffer" ); + + yylineno = _line_number; +} + +/** Set the current column. + * @param _column_no column number + * @param yyscanner The scanner object. + */ +void 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( "yyset_column called with no buffer" ); + + 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 yy_switch_to_buffer + */ +void yyset_in (FILE * _in_str , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yyin = _in_str ; +} + +void yyset_out (FILE * _out_str , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yyout = _out_str ; +} + +int yyget_debug (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yy_flex_debug; +} + +void 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 * yyget_lval (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yylval; +} + +void yyset_lval (YYSTYPE * yylval_param , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yylval = yylval_param; +} + +YYLTYPE *yyget_lloc (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yylloc; +} + +void yyset_lloc (YYLTYPE * yylloc_param , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yylloc = yylloc_param; +} + +/* User-visible API */ + +/* 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 yylex_init(yyscan_t* ptr_yy_globals) +{ + if (ptr_yy_globals == NULL){ + errno = EINVAL; + return 1; + } + + *ptr_yy_globals = (yyscan_t) 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 ); +} + +/* yylex_init_extra has the same functionality as 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 yyalloc in + * the yyextra field. + */ +int yylex_init_extra( YY_EXTRA_TYPE yy_user_defined, yyscan_t* ptr_yy_globals ) +{ + struct yyguts_t dummy_yyguts; + + yyset_extra (yy_user_defined, &dummy_yyguts); + + if (ptr_yy_globals == NULL){ + errno = EINVAL; + return 1; + } + + *ptr_yy_globals = (yyscan_t) 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)); + + 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 yylex_destroy(), so don't allocate here. + */ + + yyg->yy_buffer_stack = NULL; + yyg->yy_buffer_stack_top = 0; + yyg->yy_buffer_stack_max = 0; + yyg->yy_c_buf_p = NULL; + 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 = NULL; + yyout = NULL; +#endif + + /* For future reference: Set errno on error, since we are called by + * yylex_init() + */ + return 0; +} + +/* yylex_destroy is for both reentrant and non-reentrant scanners. */ +int yylex_destroy (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + /* Pop the buffer stack, destroying each element. */ + while(YY_CURRENT_BUFFER){ + yy_delete_buffer( YY_CURRENT_BUFFER , yyscanner ); + YY_CURRENT_BUFFER_LVALUE = NULL; + yypop_buffer_state(yyscanner); + } + + /* Destroy the stack itself. */ + yyfree(yyg->yy_buffer_stack , yyscanner); + yyg->yy_buffer_stack = NULL; + + /* Destroy the start condition stack. */ + 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 + * yylex() is called, initialization will occur. */ + yy_init_globals( yyscanner); + + /* Destroy the main struct (reentrant only). */ + yyfree ( yyscanner , yyscanner ); + yyscanner = NULL; + return 0; +} + +/* + * Internal utility routines. + */ + +#ifndef yytext_ptr +static void yy_flex_strncpy (char* s1, const char * s2, int n , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + (void)yyg; + + int i; + for ( i = 0; i < n; ++i ) + s1[i] = s2[i]; +} +#endif + +#ifdef YY_NEED_STRLEN +static int yy_flex_strlen (const char * s , yyscan_t yyscanner) +{ + int n; + for ( n = 0; s[n]; ++n ) + ; + + return n; +} +#endif + +void *yyalloc (yy_size_t size , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + (void)yyg; + return malloc(size); +} + +void *yyrealloc (void * ptr, yy_size_t size , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + (void)yyg; + + /* 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 realloc(ptr, size); +} + +void yyfree (void * ptr , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + (void)yyg; + free( (char *) ptr ); /* see yyrealloc() for (char *) cast */ +} + +#define YYTABLES_NAME "yytables" + +#line 138 "src/core/io/dl-lexer.l" diff --git a/src/rigraph/core/io/dl-lexer.h b/src/rigraph/core/io/dl-lexer.h new file mode 100644 index 0000000..e41453b --- /dev/null +++ b/src/rigraph/core/io/dl-lexer.h @@ -0,0 +1,737 @@ +#ifndef igraph_dl_yyHEADER_H +#define igraph_dl_yyHEADER_H 1 +#define igraph_dl_yyIN_HEADER 1 + +#line 6 "src/core/io/dl-lexer.h" + +#line 8 "src/core/io/dl-lexer.h" + +#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 6 +#define YY_FLEX_SUBMINOR_VERSION 4 +#if YY_FLEX_SUBMINOR_VERSION > 0 +#define FLEX_BETA +#endif + +#ifdef yy_create_buffer +#define igraph_dl_yy_create_buffer_ALREADY_DEFINED +#else +#define yy_create_buffer igraph_dl_yy_create_buffer +#endif + +#ifdef yy_delete_buffer +#define igraph_dl_yy_delete_buffer_ALREADY_DEFINED +#else +#define yy_delete_buffer igraph_dl_yy_delete_buffer +#endif + +#ifdef yy_scan_buffer +#define igraph_dl_yy_scan_buffer_ALREADY_DEFINED +#else +#define yy_scan_buffer igraph_dl_yy_scan_buffer +#endif + +#ifdef yy_scan_string +#define igraph_dl_yy_scan_string_ALREADY_DEFINED +#else +#define yy_scan_string igraph_dl_yy_scan_string +#endif + +#ifdef yy_scan_bytes +#define igraph_dl_yy_scan_bytes_ALREADY_DEFINED +#else +#define yy_scan_bytes igraph_dl_yy_scan_bytes +#endif + +#ifdef yy_init_buffer +#define igraph_dl_yy_init_buffer_ALREADY_DEFINED +#else +#define yy_init_buffer igraph_dl_yy_init_buffer +#endif + +#ifdef yy_flush_buffer +#define igraph_dl_yy_flush_buffer_ALREADY_DEFINED +#else +#define yy_flush_buffer igraph_dl_yy_flush_buffer +#endif + +#ifdef yy_load_buffer_state +#define igraph_dl_yy_load_buffer_state_ALREADY_DEFINED +#else +#define yy_load_buffer_state igraph_dl_yy_load_buffer_state +#endif + +#ifdef yy_switch_to_buffer +#define igraph_dl_yy_switch_to_buffer_ALREADY_DEFINED +#else +#define yy_switch_to_buffer igraph_dl_yy_switch_to_buffer +#endif + +#ifdef yypush_buffer_state +#define igraph_dl_yypush_buffer_state_ALREADY_DEFINED +#else +#define yypush_buffer_state igraph_dl_yypush_buffer_state +#endif + +#ifdef yypop_buffer_state +#define igraph_dl_yypop_buffer_state_ALREADY_DEFINED +#else +#define yypop_buffer_state igraph_dl_yypop_buffer_state +#endif + +#ifdef yyensure_buffer_stack +#define igraph_dl_yyensure_buffer_stack_ALREADY_DEFINED +#else +#define yyensure_buffer_stack igraph_dl_yyensure_buffer_stack +#endif + +#ifdef yylex +#define igraph_dl_yylex_ALREADY_DEFINED +#else +#define yylex igraph_dl_yylex +#endif + +#ifdef yyrestart +#define igraph_dl_yyrestart_ALREADY_DEFINED +#else +#define yyrestart igraph_dl_yyrestart +#endif + +#ifdef yylex_init +#define igraph_dl_yylex_init_ALREADY_DEFINED +#else +#define yylex_init igraph_dl_yylex_init +#endif + +#ifdef yylex_init_extra +#define igraph_dl_yylex_init_extra_ALREADY_DEFINED +#else +#define yylex_init_extra igraph_dl_yylex_init_extra +#endif + +#ifdef yylex_destroy +#define igraph_dl_yylex_destroy_ALREADY_DEFINED +#else +#define yylex_destroy igraph_dl_yylex_destroy +#endif + +#ifdef yyget_debug +#define igraph_dl_yyget_debug_ALREADY_DEFINED +#else +#define yyget_debug igraph_dl_yyget_debug +#endif + +#ifdef yyset_debug +#define igraph_dl_yyset_debug_ALREADY_DEFINED +#else +#define yyset_debug igraph_dl_yyset_debug +#endif + +#ifdef yyget_extra +#define igraph_dl_yyget_extra_ALREADY_DEFINED +#else +#define yyget_extra igraph_dl_yyget_extra +#endif + +#ifdef yyset_extra +#define igraph_dl_yyset_extra_ALREADY_DEFINED +#else +#define yyset_extra igraph_dl_yyset_extra +#endif + +#ifdef yyget_in +#define igraph_dl_yyget_in_ALREADY_DEFINED +#else +#define yyget_in igraph_dl_yyget_in +#endif + +#ifdef yyset_in +#define igraph_dl_yyset_in_ALREADY_DEFINED +#else +#define yyset_in igraph_dl_yyset_in +#endif + +#ifdef yyget_out +#define igraph_dl_yyget_out_ALREADY_DEFINED +#else +#define yyget_out igraph_dl_yyget_out +#endif + +#ifdef yyset_out +#define igraph_dl_yyset_out_ALREADY_DEFINED +#else +#define yyset_out igraph_dl_yyset_out +#endif + +#ifdef yyget_leng +#define igraph_dl_yyget_leng_ALREADY_DEFINED +#else +#define yyget_leng igraph_dl_yyget_leng +#endif + +#ifdef yyget_text +#define igraph_dl_yyget_text_ALREADY_DEFINED +#else +#define yyget_text igraph_dl_yyget_text +#endif + +#ifdef yyget_lineno +#define igraph_dl_yyget_lineno_ALREADY_DEFINED +#else +#define yyget_lineno igraph_dl_yyget_lineno +#endif + +#ifdef yyset_lineno +#define igraph_dl_yyset_lineno_ALREADY_DEFINED +#else +#define yyset_lineno igraph_dl_yyset_lineno +#endif + +#ifdef yyget_column +#define igraph_dl_yyget_column_ALREADY_DEFINED +#else +#define yyget_column igraph_dl_yyget_column +#endif + +#ifdef yyset_column +#define igraph_dl_yyset_column_ALREADY_DEFINED +#else +#define yyset_column igraph_dl_yyset_column +#endif + +#ifdef yywrap +#define igraph_dl_yywrap_ALREADY_DEFINED +#else +#define yywrap igraph_dl_yywrap +#endif + +#ifdef yyget_lval +#define igraph_dl_yyget_lval_ALREADY_DEFINED +#else +#define yyget_lval igraph_dl_yyget_lval +#endif + +#ifdef yyset_lval +#define igraph_dl_yyset_lval_ALREADY_DEFINED +#else +#define yyset_lval igraph_dl_yyset_lval +#endif + +#ifdef yyget_lloc +#define igraph_dl_yyget_lloc_ALREADY_DEFINED +#else +#define yyget_lloc igraph_dl_yyget_lloc +#endif + +#ifdef yyset_lloc +#define igraph_dl_yyset_lloc_ALREADY_DEFINED +#else +#define yyset_lloc igraph_dl_yyset_lloc +#endif + +#ifdef yyalloc +#define igraph_dl_yyalloc_ALREADY_DEFINED +#else +#define yyalloc igraph_dl_yyalloc +#endif + +#ifdef yyrealloc +#define igraph_dl_yyrealloc_ALREADY_DEFINED +#else +#define yyrealloc igraph_dl_yyrealloc +#endif + +#ifdef yyfree +#define igraph_dl_yyfree_ALREADY_DEFINED +#else +#define yyfree igraph_dl_yyfree +#endif + +/* First, we deal with platform-specific or compiler-specific issues. */ + +/* begin standard C headers. */ +#include +#include +#include +#include + +/* end standard C headers. */ + +/* flex integer type definitions */ + +#ifndef FLEXINT_H +#define FLEXINT_H + +/* C99 systems have . 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 +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; +#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; + +/* 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 + +#ifndef SIZE_MAX +#define SIZE_MAX (~(size_t)0) +#endif + +#endif /* ! C99 */ + +#endif /* ! FLEXINT_H */ + +/* begin standard C++ headers. */ + +/* TODO: this is always defined, so inline it */ +#define yyconst const + +#if defined(__GNUC__) && __GNUC__ >= 3 +#define yynoreturn __attribute__((__noreturn__)) +#else +#define yynoreturn +#endif + +/* 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 + +/* Size of default input buffer. */ +#ifndef YY_BUF_SIZE +#ifdef __ia64__ +/* On IA-64, the buffer size is 16k, not 8k. + * Moreover, YY_BUF_SIZE is 2*YY_READ_BUF_SIZE in the general case. + * Ditto for the __ia64__ case accordingly. + */ +#define YY_BUF_SIZE 32768 +#else +#define YY_BUF_SIZE 16384 +#endif /* __ia64__ */ +#endif + +#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 + +#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. + */ + int yy_buf_size; + + /* Number of characters read into yy_ch_buf, not including EOB + * characters. + */ + int 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; + + }; +#endif /* !YY_STRUCT_YY_BUFFER_STATE */ + +void yyrestart ( FILE *input_file , yyscan_t yyscanner ); +void yy_switch_to_buffer ( YY_BUFFER_STATE new_buffer , yyscan_t yyscanner ); +YY_BUFFER_STATE yy_create_buffer ( FILE *file, int size , yyscan_t yyscanner ); +void yy_delete_buffer ( YY_BUFFER_STATE b , yyscan_t yyscanner ); +void yy_flush_buffer ( YY_BUFFER_STATE b , yyscan_t yyscanner ); +void yypush_buffer_state ( YY_BUFFER_STATE new_buffer , yyscan_t yyscanner ); +void yypop_buffer_state ( yyscan_t yyscanner ); + +YY_BUFFER_STATE yy_scan_buffer ( char *base, yy_size_t size , yyscan_t yyscanner ); +YY_BUFFER_STATE yy_scan_string ( const char *yy_str , yyscan_t yyscanner ); +YY_BUFFER_STATE yy_scan_bytes ( const char *bytes, int len , yyscan_t yyscanner ); + +void *yyalloc ( yy_size_t , yyscan_t yyscanner ); +void *yyrealloc ( void *, yy_size_t , yyscan_t yyscanner ); +void yyfree ( void * , yyscan_t yyscanner ); + +#define igraph_dl_yywrap(yyscanner) (/*CONSTCOND*/1) +#define YY_SKIP_YYWRAP + +#define yytext_ptr yytext_r + +#ifdef YY_HEADER_EXPORT_START_CONDITIONS +#define INITIAL 0 +#define LABELM 1 +#define FULLMATRIX 2 +#define EDGELIST 3 +#define NODELIST 4 + +#endif + +#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 +#endif + +#ifndef YY_EXTRA_TYPE +#define YY_EXTRA_TYPE void * +#endif + +int yylex_init (yyscan_t* scanner); + +int 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 yylex_destroy ( yyscan_t yyscanner ); + +int yyget_debug ( yyscan_t yyscanner ); + +void yyset_debug ( int debug_flag , yyscan_t yyscanner ); + +YY_EXTRA_TYPE yyget_extra ( yyscan_t yyscanner ); + +void yyset_extra ( YY_EXTRA_TYPE user_defined , yyscan_t yyscanner ); + +FILE *yyget_in ( yyscan_t yyscanner ); + +void yyset_in ( FILE * _in_str , yyscan_t yyscanner ); + +FILE *yyget_out ( yyscan_t yyscanner ); + +void yyset_out ( FILE * _out_str , yyscan_t yyscanner ); + + int yyget_leng ( yyscan_t yyscanner ); + +char *yyget_text ( yyscan_t yyscanner ); + +int yyget_lineno ( yyscan_t yyscanner ); + +void yyset_lineno ( int _line_number , yyscan_t yyscanner ); + +int yyget_column ( yyscan_t yyscanner ); + +void yyset_column ( int _column_no , yyscan_t yyscanner ); + +YYSTYPE * yyget_lval ( yyscan_t yyscanner ); + +void yyset_lval ( YYSTYPE * yylval_param , yyscan_t yyscanner ); + + YYLTYPE *yyget_lloc ( yyscan_t yyscanner ); + + void 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 yywrap ( yyscan_t yyscanner ); +#else +extern int yywrap ( yyscan_t yyscanner ); +#endif +#endif + +#ifndef yytext_ptr +static void yy_flex_strncpy ( char *, const char *, int , yyscan_t yyscanner); +#endif + +#ifdef YY_NEED_STRLEN +static int yy_flex_strlen ( const char * , yyscan_t yyscanner); +#endif + +#ifndef YY_NO_INPUT + +#endif + +/* Amount of stuff to slurp up with each read. */ +#ifndef YY_READ_BUF_SIZE +#ifdef __ia64__ +/* On IA-64, the buffer size is 16k, not 8k */ +#define YY_READ_BUF_SIZE 16384 +#else +#define YY_READ_BUF_SIZE 8192 +#endif /* __ia64__ */ +#endif + +/* Number of entries by which start-condition stack grows. */ +#ifndef YY_START_STACK_INCR +#define YY_START_STACK_INCR 25 +#endif + +/* 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 yylex \ + (YYSTYPE * yylval_param, YYLTYPE * yylloc_param , yyscan_t yyscanner); + +#define YY_DECL int yylex \ + (YYSTYPE * yylval_param, YYLTYPE * yylloc_param , yyscan_t yyscanner) +#endif /* !YY_DECL */ + +/* yy_get_previous_state - get the state just before the EOB char was reached */ + +#undef YY_NEW_FILE +#undef YY_FLUSH_BUFFER +#undef yy_set_bol +#undef yy_new_buffer +#undef yy_set_interactive +#undef YY_DO_BEFORE_ACTION + +#ifdef YY_DECL_IS_OURS +#undef YY_DECL_IS_OURS +#undef YY_DECL +#endif + +#ifndef igraph_dl_yy_create_buffer_ALREADY_DEFINED +#undef yy_create_buffer +#endif +#ifndef igraph_dl_yy_delete_buffer_ALREADY_DEFINED +#undef yy_delete_buffer +#endif +#ifndef igraph_dl_yy_scan_buffer_ALREADY_DEFINED +#undef yy_scan_buffer +#endif +#ifndef igraph_dl_yy_scan_string_ALREADY_DEFINED +#undef yy_scan_string +#endif +#ifndef igraph_dl_yy_scan_bytes_ALREADY_DEFINED +#undef yy_scan_bytes +#endif +#ifndef igraph_dl_yy_init_buffer_ALREADY_DEFINED +#undef yy_init_buffer +#endif +#ifndef igraph_dl_yy_flush_buffer_ALREADY_DEFINED +#undef yy_flush_buffer +#endif +#ifndef igraph_dl_yy_load_buffer_state_ALREADY_DEFINED +#undef yy_load_buffer_state +#endif +#ifndef igraph_dl_yy_switch_to_buffer_ALREADY_DEFINED +#undef yy_switch_to_buffer +#endif +#ifndef igraph_dl_yypush_buffer_state_ALREADY_DEFINED +#undef yypush_buffer_state +#endif +#ifndef igraph_dl_yypop_buffer_state_ALREADY_DEFINED +#undef yypop_buffer_state +#endif +#ifndef igraph_dl_yyensure_buffer_stack_ALREADY_DEFINED +#undef yyensure_buffer_stack +#endif +#ifndef igraph_dl_yylex_ALREADY_DEFINED +#undef yylex +#endif +#ifndef igraph_dl_yyrestart_ALREADY_DEFINED +#undef yyrestart +#endif +#ifndef igraph_dl_yylex_init_ALREADY_DEFINED +#undef yylex_init +#endif +#ifndef igraph_dl_yylex_init_extra_ALREADY_DEFINED +#undef yylex_init_extra +#endif +#ifndef igraph_dl_yylex_destroy_ALREADY_DEFINED +#undef yylex_destroy +#endif +#ifndef igraph_dl_yyget_debug_ALREADY_DEFINED +#undef yyget_debug +#endif +#ifndef igraph_dl_yyset_debug_ALREADY_DEFINED +#undef yyset_debug +#endif +#ifndef igraph_dl_yyget_extra_ALREADY_DEFINED +#undef yyget_extra +#endif +#ifndef igraph_dl_yyset_extra_ALREADY_DEFINED +#undef yyset_extra +#endif +#ifndef igraph_dl_yyget_in_ALREADY_DEFINED +#undef yyget_in +#endif +#ifndef igraph_dl_yyset_in_ALREADY_DEFINED +#undef yyset_in +#endif +#ifndef igraph_dl_yyget_out_ALREADY_DEFINED +#undef yyget_out +#endif +#ifndef igraph_dl_yyset_out_ALREADY_DEFINED +#undef yyset_out +#endif +#ifndef igraph_dl_yyget_leng_ALREADY_DEFINED +#undef yyget_leng +#endif +#ifndef igraph_dl_yyget_text_ALREADY_DEFINED +#undef yyget_text +#endif +#ifndef igraph_dl_yyget_lineno_ALREADY_DEFINED +#undef yyget_lineno +#endif +#ifndef igraph_dl_yyset_lineno_ALREADY_DEFINED +#undef yyset_lineno +#endif +#ifndef igraph_dl_yyget_column_ALREADY_DEFINED +#undef yyget_column +#endif +#ifndef igraph_dl_yyset_column_ALREADY_DEFINED +#undef yyset_column +#endif +#ifndef igraph_dl_yywrap_ALREADY_DEFINED +#undef yywrap +#endif +#ifndef igraph_dl_yyget_lval_ALREADY_DEFINED +#undef yyget_lval +#endif +#ifndef igraph_dl_yyset_lval_ALREADY_DEFINED +#undef yyset_lval +#endif +#ifndef igraph_dl_yyget_lloc_ALREADY_DEFINED +#undef yyget_lloc +#endif +#ifndef igraph_dl_yyset_lloc_ALREADY_DEFINED +#undef yyset_lloc +#endif +#ifndef igraph_dl_yyalloc_ALREADY_DEFINED +#undef yyalloc +#endif +#ifndef igraph_dl_yyrealloc_ALREADY_DEFINED +#undef yyrealloc +#endif +#ifndef igraph_dl_yyfree_ALREADY_DEFINED +#undef yyfree +#endif +#ifndef igraph_dl_yytext_ALREADY_DEFINED +#undef yytext +#endif +#ifndef igraph_dl_yyleng_ALREADY_DEFINED +#undef yyleng +#endif +#ifndef igraph_dl_yyin_ALREADY_DEFINED +#undef yyin +#endif +#ifndef igraph_dl_yyout_ALREADY_DEFINED +#undef yyout +#endif +#ifndef igraph_dl_yy_flex_debug_ALREADY_DEFINED +#undef yy_flex_debug +#endif +#ifndef igraph_dl_yylineno_ALREADY_DEFINED +#undef yylineno +#endif +#ifndef igraph_dl_yytables_fload_ALREADY_DEFINED +#undef yytables_fload +#endif +#ifndef igraph_dl_yytables_destroy_ALREADY_DEFINED +#undef yytables_destroy +#endif +#ifndef igraph_dl_yyTABLES_NAME_ALREADY_DEFINED +#undef yyTABLES_NAME +#endif + +#line 138 "src/core/io/dl-lexer.l" + +#line 736 "src/core/io/dl-lexer.h" +#undef igraph_dl_yyIN_HEADER +#endif /* igraph_dl_yyHEADER_H */ diff --git a/src/rigraph/core/io/dl-lexer.l b/src/rigraph/core/io/dl-lexer.l new file mode 100644 index 0000000..a1f6974 --- /dev/null +++ b/src/rigraph/core/io/dl-lexer.l @@ -0,0 +1,137 @@ +/* + IGraph library. + Copyright (C) 2007-2012 Gabor Csardi + 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 + +*/ + +%{ + +/* + IGraph library. + Copyright (C) 2007-2012 Gabor Csardi + 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 +#include + +#include "io/dl-header.h" +#include "io/parsers/dl-parser.h" + +#define YY_EXTRA_TYPE igraph_i_dl_parsedata_t* +#define YY_USER_ACTION yylloc->first_line = yylineno; +#define YY_FATAL_ERROR(msg) IGRAPH_FATAL("Error in DL parser: " # msg) +#ifdef USING_R +#define fprintf(file, msg, ...) (1) +#ifdef stdout +# undef stdout +#endif +#define stdout 0 +#endif +%} + +%option noyywrap +%option prefix="igraph_dl_yy" +%option nounput +%option noinput +%option nodefault +%option reentrant +%option bison-bridge +%option bison-locations + +digit [0-9] +whitespace [ \t\v\f] + +%x LABELM FULLMATRIX EDGELIST NODELIST + +%% + +<*>\n\r|\r\n|\r|\n { return NEWLINE; } + +[dD][lL]{whitespace}+ { return DL; } +[nN]{whitespace}*[=]{whitespace}* { + return NEQ; } +{digit}+ { return NUM; } + +[dD][aA][tT][aA][:] { + switch (yyextra->mode) { + case 0: BEGIN(FULLMATRIX); + break; + case 1: BEGIN(EDGELIST); + break; + case 2: BEGIN(NODELIST); + break; + } + return DATA; } + +[lL][aA][bB][eE][lL][sS]: { BEGIN(LABELM); return LABELS; } +[lL][aA][bB][eE][lL][sS]{whitespace}+[eE][mM][bB][eE][dD][dD][eE][dD]:?{whitespace}* { + return LABELSEMBEDDED; } +[fF][oO][rR][mM][aA][tT]{whitespace}*[=]{whitespace}*[fF][uU][lL][lL][mM][aA][tT][rR][iI][xX]{whitespace}* { + yyextra->mode=0; return FORMATFULLMATRIX; } +[fF][oO][rR][mM][aA][tT]{whitespace}*[=]{whitespace}*[eE][dD][gG][eE][lL][iI][sS][tT][1]{whitespace}* { + yyextra->mode=1; return FORMATEDGELIST1; } +[fF][oO][rR][mM][aA][tT]{whitespace}*[=]{whitespace}*[nN][oO][dD][eE][lL][iI][sS][tT][1]{whitespace}* { + yyextra->mode=2; return FORMATNODELIST1; } + +[, ] { /* eaten up */ } +[^, \t\n\r\f\v]+{whitespace}* { return LABEL; } + +{digit}{whitespace}* { return DIGIT; } +[^ \t\n\r\v\f,]+ { return LABEL; } +{whitespace} { } + +\-?{digit}+(\.{digit}+)?([eE](\+|\-)?{digit}+)? { return NUM; } +[^ \t\n\r\v\f,]+ { return LABEL; } +{whitespace}* { } + +{digit}+ { return NUM; } +[^ \t\r\n\v\f,]+ { return LABEL; } +{whitespace}* { } + +{whitespace}+ { /* eaten up */ } + +<> { + if (yyextra->eof) { + yyterminate(); + } else { + yyextra->eof=1; + BEGIN(INITIAL); + return EOFF; + } + } + +<*>. { return 0; } diff --git a/src/rigraph/core/io/dl-parser.c b/src/rigraph/core/io/dl-parser.c new file mode 100644 index 0000000..08e822a --- /dev/null +++ b/src/rigraph/core/io/dl-parser.c @@ -0,0 +1,2237 @@ +/* A Bison parser, made by GNU Bison 3.5.1. */ + +/* Bison implementation for Yacc-like parsers in C + + Copyright (C) 1984, 1989-1990, 2000-2015, 2018-2020 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 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 . */ + +/* 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. */ + +/* Undocumented macros, especially those whose name start with YY_, + are private implementation details. Do not rely on them. */ + +/* Identify Bison output. */ +#define YYBISON 1 + +/* Bison version. */ +#define YYBISON_VERSION "3.5.1" + +/* Skeleton name. */ +#define YYSKELETON_NAME "yacc.c" + +/* Pure parsers. */ +#define YYPURE 1 + +/* Push parsers. */ +#define YYPUSH 0 + +/* Pull parsers. */ +#define YYPULL 1 + + +/* Substitute the variable and function names. */ +#define yyparse igraph_dl_yyparse +#define yylex igraph_dl_yylex +#define yyerror igraph_dl_yyerror +#define yydebug igraph_dl_yydebug +#define yynerrs igraph_dl_yynerrs + +/* First part of user prologue. */ +#line 23 "src/core/io/dl-parser.y" + + +/* + IGraph library. + Copyright (C) 2009-2012 Gabor Csardi + 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 "core/math.h" +#include "internal/hacks.h" +#include "io/dl-header.h" +#include "io/dl-parser.h" +#include "io/dl-lexer.h" + +#include + +int igraph_dl_yyerror(YYLTYPE* locp, igraph_i_dl_parsedata_t* context, + const char *s); +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 + + +#line 125 "yy.tab.c" + +# ifndef YY_CAST +# ifdef __cplusplus +# define YY_CAST(Type, Val) static_cast (Val) +# define YY_REINTERPRET_CAST(Type, Val) reinterpret_cast (Val) +# else +# define YY_CAST(Type, Val) ((Type) (Val)) +# define YY_REINTERPRET_CAST(Type, Val) ((Type) (Val)) +# endif +# endif +# ifndef YY_NULLPTR +# if defined __cplusplus +# if 201103L <= __cplusplus +# define YY_NULLPTR nullptr +# else +# define YY_NULLPTR 0 +# endif +# else +# define YY_NULLPTR ((void*)0) +# endif +# endif + +/* Enabling verbose error messages. */ +#ifdef YYERROR_VERBOSE +# undef YYERROR_VERBOSE +# define YYERROR_VERBOSE 1 +#else +# define YYERROR_VERBOSE 1 +#endif + +/* Use api.header.include to #include this header + instead of duplicating it here. */ +#ifndef YY_IGRAPH_DL_YY_YY_TAB_H_INCLUDED +# define YY_IGRAPH_DL_YY_YY_TAB_H_INCLUDED +/* Debug traces. */ +#ifndef YYDEBUG +# define YYDEBUG 0 +#endif +#if YYDEBUG +extern int igraph_dl_yydebug; +#endif + +/* Token type. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + 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 + +/* Value type. */ +#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED +union YYSTYPE +{ +#line 83 "src/core/io/dl-parser.y" + + long int integer; + igraph_real_t real; + +#line 199 "yy.tab.c" + +}; +typedef union YYSTYPE YYSTYPE; +# define YYSTYPE_IS_TRIVIAL 1 +# define YYSTYPE_IS_DECLARED 1 +#endif + +/* Location type. */ +#if ! defined YYLTYPE && ! defined YYLTYPE_IS_DECLARED +typedef struct YYLTYPE YYLTYPE; +struct YYLTYPE +{ + int first_line; + int first_column; + int last_line; + int last_column; +}; +# define YYLTYPE_IS_DECLARED 1 +# define YYLTYPE_IS_TRIVIAL 1 +#endif + + + +int igraph_dl_yyparse (igraph_i_dl_parsedata_t* context); + +#endif /* !YY_IGRAPH_DL_YY_YY_TAB_H_INCLUDED */ + + + +#ifdef short +# undef short +#endif + +/* On compilers that do not define __PTRDIFF_MAX__ etc., make sure + and (if available) are included + so that the code can choose integer types of a good width. */ + +#ifndef __PTRDIFF_MAX__ +# include /* INFRINGES ON USER NAME SPACE */ +# if defined __STDC_VERSION__ && 199901 <= __STDC_VERSION__ +# include /* INFRINGES ON USER NAME SPACE */ +# define YY_STDINT_H +# endif +#endif + +/* Narrow types that promote to a signed type and that can represent a + signed or unsigned integer of at least N bits. In tables they can + save space and decrease cache pressure. Promoting to a signed type + helps avoid bugs in integer arithmetic. */ + +#ifdef __INT_LEAST8_MAX__ +typedef __INT_LEAST8_TYPE__ yytype_int8; +#elif defined YY_STDINT_H +typedef int_least8_t yytype_int8; +#else +typedef signed char yytype_int8; +#endif + +#ifdef __INT_LEAST16_MAX__ +typedef __INT_LEAST16_TYPE__ yytype_int16; +#elif defined YY_STDINT_H +typedef int_least16_t yytype_int16; +#else +typedef short yytype_int16; +#endif + +#if defined __UINT_LEAST8_MAX__ && __UINT_LEAST8_MAX__ <= __INT_MAX__ +typedef __UINT_LEAST8_TYPE__ yytype_uint8; +#elif (!defined __UINT_LEAST8_MAX__ && defined YY_STDINT_H \ + && UINT_LEAST8_MAX <= INT_MAX) +typedef uint_least8_t yytype_uint8; +#elif !defined __UINT_LEAST8_MAX__ && UCHAR_MAX <= INT_MAX +typedef unsigned char yytype_uint8; +#else +typedef short yytype_uint8; +#endif + +#if defined __UINT_LEAST16_MAX__ && __UINT_LEAST16_MAX__ <= __INT_MAX__ +typedef __UINT_LEAST16_TYPE__ yytype_uint16; +#elif (!defined __UINT_LEAST16_MAX__ && defined YY_STDINT_H \ + && UINT_LEAST16_MAX <= INT_MAX) +typedef uint_least16_t yytype_uint16; +#elif !defined __UINT_LEAST16_MAX__ && USHRT_MAX <= INT_MAX +typedef unsigned short yytype_uint16; +#else +typedef int yytype_uint16; +#endif + +#ifndef YYPTRDIFF_T +# if defined __PTRDIFF_TYPE__ && defined __PTRDIFF_MAX__ +# define YYPTRDIFF_T __PTRDIFF_TYPE__ +# define YYPTRDIFF_MAXIMUM __PTRDIFF_MAX__ +# elif defined PTRDIFF_MAX +# ifndef ptrdiff_t +# include /* INFRINGES ON USER NAME SPACE */ +# endif +# define YYPTRDIFF_T ptrdiff_t +# define YYPTRDIFF_MAXIMUM PTRDIFF_MAX +# else +# define YYPTRDIFF_T long +# define YYPTRDIFF_MAXIMUM LONG_MAX +# endif +#endif + +#ifndef YYSIZE_T +# ifdef __SIZE_TYPE__ +# define YYSIZE_T __SIZE_TYPE__ +# elif defined size_t +# define YYSIZE_T size_t +# elif defined __STDC_VERSION__ && 199901 <= __STDC_VERSION__ +# include /* INFRINGES ON USER NAME SPACE */ +# define YYSIZE_T size_t +# else +# define YYSIZE_T unsigned +# endif +#endif + +#define YYSIZE_MAXIMUM \ + YY_CAST (YYPTRDIFF_T, \ + (YYPTRDIFF_MAXIMUM < YY_CAST (YYSIZE_T, -1) \ + ? YYPTRDIFF_MAXIMUM \ + : YY_CAST (YYSIZE_T, -1))) + +#define YYSIZEOF(X) YY_CAST (YYPTRDIFF_T, sizeof (X)) + +/* Stored state numbers (used for stacks). */ +typedef yytype_uint8 yy_state_t; + +/* State numbers in computations. */ +typedef int yy_state_fast_t; + +#ifndef YY_ +# if defined YYENABLE_NLS && YYENABLE_NLS +# if ENABLE_NLS +# include /* INFRINGES ON USER NAME SPACE */ +# define YY_(Msgid) dgettext ("bison-runtime", Msgid) +# endif +# endif +# ifndef YY_ +# define YY_(Msgid) Msgid +# endif +#endif + +#ifndef YY_ATTRIBUTE_PURE +# if defined __GNUC__ && 2 < __GNUC__ + (96 <= __GNUC_MINOR__) +# define YY_ATTRIBUTE_PURE __attribute__ ((__pure__)) +# else +# define YY_ATTRIBUTE_PURE +# endif +#endif + +#ifndef YY_ATTRIBUTE_UNUSED +# if defined __GNUC__ && 2 < __GNUC__ + (7 <= __GNUC_MINOR__) +# define YY_ATTRIBUTE_UNUSED __attribute__ ((__unused__)) +# else +# define YY_ATTRIBUTE_UNUSED +# 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 + +#if defined __GNUC__ && ! defined __ICC && 407 <= __GNUC__ * 100 + __GNUC_MINOR__ +/* Suppress an incorrect diagnostic about yylval being uninitialized. */ +# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN \ + _Pragma ("GCC diagnostic push") \ + _Pragma ("GCC diagnostic ignored \"-Wuninitialized\"") \ + _Pragma ("GCC diagnostic ignored \"-Wmaybe-uninitialized\"") +# define YY_IGNORE_MAYBE_UNINITIALIZED_END \ + _Pragma ("GCC diagnostic pop") +#else +# define YY_INITIAL_VALUE(Value) Value +#endif +#ifndef YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN +# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN +# define YY_IGNORE_MAYBE_UNINITIALIZED_END +#endif +#ifndef YY_INITIAL_VALUE +# define YY_INITIAL_VALUE(Value) /* Nothing. */ +#endif + +#if defined __cplusplus && defined __GNUC__ && ! defined __ICC && 6 <= __GNUC__ +# define YY_IGNORE_USELESS_CAST_BEGIN \ + _Pragma ("GCC diagnostic push") \ + _Pragma ("GCC diagnostic ignored \"-Wuseless-cast\"") +# define YY_IGNORE_USELESS_CAST_END \ + _Pragma ("GCC diagnostic pop") +#endif +#ifndef YY_IGNORE_USELESS_CAST_BEGIN +# define YY_IGNORE_USELESS_CAST_BEGIN +# define YY_IGNORE_USELESS_CAST_END +#endif + + +#define YY_ASSERT(E) ((void) (0 && (E))) + +#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 /* INFRINGES ON USER NAME SPACE */ +# elif defined _AIX +# define YYSTACK_ALLOC __alloca +# elif defined _MSC_VER +# include /* INFRINGES ON USER NAME SPACE */ +# define alloca _alloca +# else +# define YYSTACK_ALLOC alloca +# if ! defined _ALLOCA_H && ! defined EXIT_SUCCESS +# include /* INFRINGES ON USER NAME SPACE */ + /* Use EXIT_SUCCESS as a witness for stdlib.h. */ +# ifndef EXIT_SUCCESS +# define EXIT_SUCCESS 0 +# endif +# endif +# endif +# endif +# endif + +# ifdef YYSTACK_ALLOC + /* Pacify GCC's 'empty if-body' warning. */ +# define YYSTACK_FREE(Ptr) do { /* empty */; } while (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 EXIT_SUCCESS \ + && ! ((defined YYMALLOC || defined malloc) \ + && (defined YYFREE || defined free))) +# include /* INFRINGES ON USER NAME SPACE */ +# ifndef EXIT_SUCCESS +# define EXIT_SUCCESS 0 +# endif +# endif +# ifndef YYMALLOC +# define YYMALLOC malloc +# if ! defined malloc && ! defined EXIT_SUCCESS +void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# ifndef YYFREE +# define YYFREE free +# if ! defined free && ! defined EXIT_SUCCESS +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 +{ + yy_state_t yyss_alloc; + YYSTYPE yyvs_alloc; + YYLTYPE yyls_alloc; +}; + +/* The size of the maximum gap between one aligned stack and the next. */ +# define YYSTACK_GAP_MAXIMUM (YYSIZEOF (union yyalloc) - 1) + +/* The size of an array large to enough to hold all stacks, each with + N elements. */ +# define YYSTACK_BYTES(N) \ + ((N) * (YYSIZEOF (yy_state_t) + YYSIZEOF (YYSTYPE) \ + + YYSIZEOF (YYLTYPE)) \ + + 2 * YYSTACK_GAP_MAXIMUM) + +# define YYCOPY_NEEDED 1 + +/* 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_alloc, Stack) \ + do \ + { \ + YYPTRDIFF_T yynewbytes; \ + YYCOPY (&yyptr->Stack_alloc, Stack, yysize); \ + Stack = &yyptr->Stack_alloc; \ + yynewbytes = yystacksize * YYSIZEOF (*Stack) + YYSTACK_GAP_MAXIMUM; \ + yyptr += yynewbytes / YYSIZEOF (*yyptr); \ + } \ + while (0) + +#endif + +#if defined YYCOPY_NEEDED && YYCOPY_NEEDED +/* Copy COUNT objects from SRC to DST. The source and destination do + not overlap. */ +# ifndef YYCOPY +# if defined __GNUC__ && 1 < __GNUC__ +# define YYCOPY(Dst, Src, Count) \ + __builtin_memcpy (Dst, Src, YY_CAST (YYSIZE_T, (Count)) * sizeof (*(Src))) +# else +# define YYCOPY(Dst, Src, Count) \ + do \ + { \ + YYPTRDIFF_T yyi; \ + for (yyi = 0; yyi < (Count); yyi++) \ + (Dst)[yyi] = (Src)[yyi]; \ + } \ + while (0) +# endif +# endif +#endif /* !YYCOPY_NEEDED */ + +/* 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 +/* YYNSTATES -- Number of states. */ +#define YYNSTATES 138 + +#define YYUNDEFTOK 2 +#define YYMAXUTOK 271 + + +/* YYTRANSLATE(TOKEN-NUM) -- Symbol number corresponding to TOKEN-NUM + as returned by yylex, with out-of-bounds checking. */ +#define YYTRANSLATE(YYX) \ + (0 <= (YYX) && (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK) + +/* YYTRANSLATE[TOKEN-NUM] -- Symbol number corresponding to TOKEN-NUM + as returned by yylex. */ +static const yytype_int8 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 + /* YYRLINE[YYN] -- Source line where rule number YYN was defined. */ +static const yytype_int16 yyrline[] = +{ + 0, 108, 108, 110, 110, 112, 112, 114, 115, 116, + 119, 119, 121, 121, 123, 124, 125, 128, 129, 135, + 135, 140, 140, 142, 152, 154, 156, 156, 158, 162, + 166, 171, 175, 177, 178, 179, 180, 181, 184, 185, + 188, 190, 194, 197, 198, 201, 203, 207, 210, 226, + 228, 229, 230, 231, 232, 235, 236, 239, 241, 244, + 244, 250, 251, 254, 256, 260, 260 +}; +#endif + +#if YYDEBUG || YYERROR_VERBOSE || 1 +/* 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", YY_NULLPTR +}; +#endif + +# ifdef YYPRINT +/* YYTOKNUM[NUM] -- (External) token number corresponding to the + (internal) symbol number NUM (which must be that of a token). */ +static const yytype_int16 yytoknum[] = +{ + 0, 256, 257, 258, 259, 260, 261, 262, 263, 264, + 265, 266, 267, 268, 269, 270, 271 +}; +# endif + +#define YYPACT_NINF (-114) + +#define yypact_value_is_default(Yyn) \ + ((Yyn) == YYPACT_NINF) + +#define YYTABLE_NINF (-22) + +#define yytable_value_is_error(Yyn) \ + 0 + + /* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing + STATE-NUM. */ +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 +}; + + /* YYDEFACT[STATE-NUM] -- Default reduction number in state STATE-NUM. + Performed when YYTABLE does not specify something else to do. Zero + means the default is an error. */ +static const yytype_int8 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 +}; + + /* 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 +}; + + /* 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 +}; + + /* YYTABLE[YYPACT[STATE-NUM]] -- What to do in state STATE-NUM. If + positive, shift that token. If negative, reduce the rule whose + number is the opposite. If YYTABLE_NINF, syntax error. */ +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_int8 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 +}; + + /* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ +static const yytype_int8 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 on the right hand side of rule YYN. */ +static const yytype_int8 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 +}; + + +#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 + + +#define YYRECOVERING() (!!yyerrstatus) + +#define YYBACKUP(Token, Value) \ + do \ + if (yychar == YYEMPTY) \ + { \ + yychar = (Token); \ + yylval = (Value); \ + YYPOPSTACK (yylen); \ + yystate = *yyssp; \ + goto yybackup; \ + } \ + else \ + { \ + yyerror (&yylloc, context, YY_("syntax error: cannot back up")); \ + YYERROR; \ + } \ + while (0) + +/* Error token number */ +#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). */ + +#ifndef YYLLOC_DEFAULT +# define YYLLOC_DEFAULT(Current, Rhs, N) \ + do \ + if (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 (0) +#endif + +#define YYRHSLOC(Rhs, K) ((Rhs)[K]) + + +/* Enable debugging if requested. */ +#if YYDEBUG + +# ifndef YYFPRINTF +# include /* INFRINGES ON USER NAME SPACE */ +# define YYFPRINTF fprintf +# endif + +# define YYDPRINTF(Args) \ +do { \ + if (yydebug) \ + YYFPRINTF Args; \ +} while (0) + + +/* 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 + +/* Print *YYLOCP on YYO. Private, do not rely on its existence. */ + +YY_ATTRIBUTE_UNUSED +static int +yy_location_print_ (FILE *yyo, YYLTYPE const * const yylocp) +{ + int res = 0; + int end_col = 0 != yylocp->last_column ? yylocp->last_column - 1 : 0; + if (0 <= yylocp->first_line) + { + res += YYFPRINTF (yyo, "%d", yylocp->first_line); + if (0 <= yylocp->first_column) + res += YYFPRINTF (yyo, ".%d", yylocp->first_column); + } + if (0 <= yylocp->last_line) + { + if (yylocp->first_line < yylocp->last_line) + { + res += YYFPRINTF (yyo, "-%d", yylocp->last_line); + if (0 <= end_col) + res += YYFPRINTF (yyo, ".%d", end_col); + } + else if (0 <= end_col && yylocp->first_column < end_col) + res += YYFPRINTF (yyo, "-%d", end_col); + } + return res; + } + +# define YY_LOCATION_PRINT(File, Loc) \ + yy_location_print_ (File, &(Loc)) + +# else +# define YY_LOCATION_PRINT(File, Loc) ((void) 0) +# endif +#endif + + +# 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 (0) + + +/*-----------------------------------. +| Print this symbol's value on YYO. | +`-----------------------------------*/ + +static void +yy_symbol_value_print (FILE *yyo, int yytype, YYSTYPE const * const yyvaluep, YYLTYPE const * const yylocationp, igraph_i_dl_parsedata_t* context) +{ + FILE *yyoutput = yyo; + YYUSE (yyoutput); + YYUSE (yylocationp); + YYUSE (context); + if (!yyvaluep) + return; +# ifdef YYPRINT + if (yytype < YYNTOKENS) + YYPRINT (yyo, yytoknum[yytype], *yyvaluep); +# endif + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + YYUSE (yytype); + YY_IGNORE_MAYBE_UNINITIALIZED_END +} + + +/*---------------------------. +| Print this symbol on YYO. | +`---------------------------*/ + +static void +yy_symbol_print (FILE *yyo, int yytype, YYSTYPE const * const yyvaluep, YYLTYPE const * const yylocationp, igraph_i_dl_parsedata_t* context) +{ + YYFPRINTF (yyo, "%s %s (", + yytype < YYNTOKENS ? "token" : "nterm", yytname[yytype]); + + YY_LOCATION_PRINT (yyo, *yylocationp); + YYFPRINTF (yyo, ": "); + yy_symbol_value_print (yyo, yytype, yyvaluep, yylocationp, context); + YYFPRINTF (yyo, ")"); +} + +/*------------------------------------------------------------------. +| yy_stack_print -- Print the state stack from its BOTTOM up to its | +| TOP (included). | +`------------------------------------------------------------------*/ + +static void +yy_stack_print (yy_state_t *yybottom, yy_state_t *yytop) +{ + YYFPRINTF (stderr, "Stack now"); + for (; yybottom <= yytop; yybottom++) + { + int yybot = *yybottom; + YYFPRINTF (stderr, " %d", yybot); + } + YYFPRINTF (stderr, "\n"); +} + +# define YY_STACK_PRINT(Bottom, Top) \ +do { \ + if (yydebug) \ + yy_stack_print ((Bottom), (Top)); \ +} while (0) + + +/*------------------------------------------------. +| Report that the YYRULE is going to be reduced. | +`------------------------------------------------*/ + +static void +yy_reduce_print (yy_state_t *yyssp, YYSTYPE *yyvsp, YYLTYPE *yylsp, int yyrule, igraph_i_dl_parsedata_t* context) +{ + int yylno = yyrline[yyrule]; + int yynrhs = yyr2[yyrule]; + int yyi; + YYFPRINTF (stderr, "Reducing stack by rule %d (line %d):\n", + yyrule - 1, yylno); + /* The symbols being reduced. */ + for (yyi = 0; yyi < yynrhs; yyi++) + { + YYFPRINTF (stderr, " $%d = ", yyi + 1); + yy_symbol_print (stderr, + yystos[+yyssp[yyi + 1 - yynrhs]], + &yyvsp[(yyi + 1) - (yynrhs)] + , &(yylsp[(yyi + 1) - (yynrhs)]) , context); + YYFPRINTF (stderr, "\n"); + } +} + +# define YY_REDUCE_PRINT(Rule) \ +do { \ + if (yydebug) \ + yy_reduce_print (yyssp, yyvsp, yylsp, Rule, context); \ +} while (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(S) (YY_CAST (YYPTRDIFF_T, strlen (S))) +# else +/* Return the length of YYSTR. */ +static YYPTRDIFF_T +yystrlen (const char *yystr) +{ + YYPTRDIFF_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. */ +static char * +yystpcpy (char *yydest, const char *yysrc) +{ + 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 YYPTRDIFF_T +yytnamerr (char *yyres, const char *yystr) +{ + if (*yystr == '"') + { + YYPTRDIFF_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; + else + goto append; + + append: + default: + if (yyres) + yyres[yyn] = *yyp; + yyn++; + break; + + case '"': + if (yyres) + yyres[yyn] = '\0'; + return yyn; + } + do_not_strip_quotes: ; + } + + if (yyres) + return yystpcpy (yyres, yystr) - yyres; + else + return yystrlen (yystr); +} +# endif + +/* Copy into *YYMSG, which is of size *YYMSG_ALLOC, an error message + about the unexpected token YYTOKEN for the state stack whose top is + YYSSP. + + Return 0 if *YYMSG was successfully written. Return 1 if *YYMSG is + not large enough to hold the message. In that case, also set + *YYMSG_ALLOC to the required number of bytes. Return 2 if the + required number of bytes is too large to store. */ +static int +yysyntax_error (YYPTRDIFF_T *yymsg_alloc, char **yymsg, + yy_state_t *yyssp, int yytoken) +{ + enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 }; + /* Internationalized format string. */ + const char *yyformat = YY_NULLPTR; + /* Arguments of yyformat: reported tokens (one for the "unexpected", + one per "expected"). */ + char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM]; + /* Actual size of YYARG. */ + int yycount = 0; + /* Cumulated lengths of YYARG. */ + YYPTRDIFF_T yysize = 0; + + /* There are many possibilities here to consider: + - If this state is a consistent state with a default action, then + the only way this function was invoked is if the default action + is an error action. In that case, don't check for expected + tokens because there are none. + - The only way there can be no lookahead present (in yychar) is if + this state is a consistent state with a default action. Thus, + detecting the absence of a lookahead is sufficient to determine + that there is no unexpected or expected token to report. In that + case, just report a simple "syntax error". + - Don't assume there isn't a lookahead just because this state is a + consistent state with a default action. There might have been a + previous inconsistent state, consistent state with a non-default + action, or user semantic action that manipulated yychar. + - Of course, the expected token list depends on states to have + correct lookahead information, and it depends on the parser not + to perform extra reductions after fetching a lookahead from the + scanner and before detecting a syntax error. Thus, state merging + (from LALR or IELR) and default reductions corrupt the expected + token list. However, the list is correct for canonical LR with + one exception: it will still contain any token that will not be + accepted due to an error action in a later state. + */ + if (yytoken != YYEMPTY) + { + int yyn = yypact[+*yyssp]; + YYPTRDIFF_T yysize0 = yytnamerr (YY_NULLPTR, yytname[yytoken]); + yysize = yysize0; + yyarg[yycount++] = yytname[yytoken]; + if (!yypact_value_is_default (yyn)) + { + /* Start YYX at -YYN if negative to avoid negative indexes in + YYCHECK. In other words, skip the first -YYN actions for + this state because they are default actions. */ + 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 yyx; + + for (yyx = yyxbegin; yyx < yyxend; ++yyx) + if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR + && !yytable_value_is_error (yytable[yyx + yyn])) + { + if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM) + { + yycount = 1; + yysize = yysize0; + break; + } + yyarg[yycount++] = yytname[yyx]; + { + YYPTRDIFF_T yysize1 + = yysize + yytnamerr (YY_NULLPTR, yytname[yyx]); + if (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM) + yysize = yysize1; + else + return 2; + } + } + } + } + + switch (yycount) + { +# define YYCASE_(N, S) \ + case N: \ + yyformat = S; \ + break + default: /* Avoid compiler warnings. */ + YYCASE_(0, YY_("syntax error")); + YYCASE_(1, YY_("syntax error, unexpected %s")); + YYCASE_(2, YY_("syntax error, unexpected %s, expecting %s")); + YYCASE_(3, YY_("syntax error, unexpected %s, expecting %s or %s")); + YYCASE_(4, YY_("syntax error, unexpected %s, expecting %s or %s or %s")); + YYCASE_(5, YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s")); +# undef YYCASE_ + } + + { + /* Don't count the "%s"s in the final size, but reserve room for + the terminator. */ + YYPTRDIFF_T yysize1 = yysize + (yystrlen (yyformat) - 2 * yycount) + 1; + if (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM) + yysize = yysize1; + else + return 2; + } + + if (*yymsg_alloc < yysize) + { + *yymsg_alloc = 2 * yysize; + if (! (yysize <= *yymsg_alloc + && *yymsg_alloc <= YYSTACK_ALLOC_MAXIMUM)) + *yymsg_alloc = YYSTACK_ALLOC_MAXIMUM; + return 1; + } + + /* 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 = *yymsg; + int yyi = 0; + while ((*yyp = *yyformat) != '\0') + if (*yyp == '%' && yyformat[1] == 's' && yyi < yycount) + { + yyp += yytnamerr (yyp, yyarg[yyi++]); + yyformat += 2; + } + else + { + ++yyp; + ++yyformat; + } + } + return 0; +} +#endif /* YYERROR_VERBOSE */ + +/*-----------------------------------------------. +| Release the memory associated to this symbol. | +`-----------------------------------------------*/ + +static void +yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep, YYLTYPE *yylocationp, igraph_i_dl_parsedata_t* context) +{ + YYUSE (yyvaluep); + YYUSE (yylocationp); + YYUSE (context); + if (!yymsg) + yymsg = "Deleting"; + YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp); + + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + YYUSE (yytype); + YY_IGNORE_MAYBE_UNINITIALIZED_END +} + + + + +/*----------. +| yyparse. | +`----------*/ + +int +yyparse (igraph_i_dl_parsedata_t* context) +{ +/* The lookahead symbol. */ +int yychar; + + +/* The semantic value of the lookahead symbol. */ +/* Default value used for initialization, for pacifying older GCCs + or non-GCC compilers. */ +YY_INITIAL_VALUE (static YYSTYPE yyval_default;) +YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); + +/* Location data for the lookahead symbol. */ +static YYLTYPE yyloc_default +# if defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL + = { 1, 1, 1, 1 } +# endif +; +YYLTYPE yylloc = yyloc_default; + + /* Number of syntax errors so far. */ + int yynerrs; + + yy_state_fast_t yystate; + /* Number of tokens to shift before error messages enabled. */ + int yyerrstatus; + + /* The stacks and their tools: + 'yyss': related to states. + 'yyvs': related to semantic values. + 'yyls': related to locations. + + Refer to the stacks through separate pointers, to allow yyoverflow + to reallocate them elsewhere. */ + + /* The state stack. */ + yy_state_t yyssa[YYINITDEPTH]; + yy_state_t *yyss; + yy_state_t *yyssp; + + /* The semantic value stack. */ + YYSTYPE yyvsa[YYINITDEPTH]; + YYSTYPE *yyvs; + YYSTYPE *yyvsp; + + /* The location stack. */ + YYLTYPE yylsa[YYINITDEPTH]; + YYLTYPE *yyls; + YYLTYPE *yylsp; + + /* The locations where the error started and ended. */ + YYLTYPE yyerror_range[3]; + + YYPTRDIFF_T yystacksize; + + int yyn; + int yyresult; + /* Lookahead token as an internal (translated) token number. */ + int yytoken = 0; + /* The variables used to return semantic value and location from the + action routines. */ + YYSTYPE yyval; + YYLTYPE yyloc; + +#if YYERROR_VERBOSE + /* Buffer for error messages, and its allocated size. */ + char yymsgbuf[128]; + char *yymsg = yymsgbuf; + YYPTRDIFF_T yymsg_alloc = sizeof yymsgbuf; +#endif + +#define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N), yylsp -= (N)) + + /* The number of symbols on the RHS of the reduced rule. + Keep to zero when no symbol should be popped. */ + int yylen = 0; + + yyssp = yyss = yyssa; + yyvsp = yyvs = yyvsa; + yylsp = yyls = yylsa; + yystacksize = YYINITDEPTH; + + YYDPRINTF ((stderr, "Starting parse\n")); + + yystate = 0; + yyerrstatus = 0; + yynerrs = 0; + yychar = YYEMPTY; /* Cause a token to be read. */ + yylsp[0] = yylloc; + 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 -- set current state (the top of the stack) to yystate. | +`--------------------------------------------------------------------*/ +yysetstate: + YYDPRINTF ((stderr, "Entering state %d\n", yystate)); + YY_ASSERT (0 <= yystate && yystate < YYNSTATES); + YY_IGNORE_USELESS_CAST_BEGIN + *yyssp = YY_CAST (yy_state_t, yystate); + YY_IGNORE_USELESS_CAST_END + + if (yyss + yystacksize - 1 <= yyssp) +#if !defined yyoverflow && !defined YYSTACK_RELOCATE + goto yyexhaustedlab; +#else + { + /* Get the current used size of the three stacks, in elements. */ + YYPTRDIFF_T yysize = yyssp - yyss + 1; + +# if defined 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. */ + yy_state_t *yyss1 = yyss; + YYSTYPE *yyvs1 = yyvs; + 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 * YYSIZEOF (*yyssp), + &yyvs1, yysize * YYSIZEOF (*yyvsp), + &yyls1, yysize * YYSIZEOF (*yylsp), + &yystacksize); + yyss = yyss1; + yyvs = yyvs1; + yyls = yyls1; + } +# else /* defined YYSTACK_RELOCATE */ + /* Extend the stack our own way. */ + if (YYMAXDEPTH <= yystacksize) + goto yyexhaustedlab; + yystacksize *= 2; + if (YYMAXDEPTH < yystacksize) + yystacksize = YYMAXDEPTH; + + { + yy_state_t *yyss1 = yyss; + union yyalloc *yyptr = + YY_CAST (union yyalloc *, + YYSTACK_ALLOC (YY_CAST (YYSIZE_T, YYSTACK_BYTES (yystacksize)))); + if (! yyptr) + goto yyexhaustedlab; + YYSTACK_RELOCATE (yyss_alloc, yyss); + YYSTACK_RELOCATE (yyvs_alloc, yyvs); + YYSTACK_RELOCATE (yyls_alloc, yyls); +# undef YYSTACK_RELOCATE + if (yyss1 != yyssa) + YYSTACK_FREE (yyss1); + } +# endif + + yyssp = yyss + yysize - 1; + yyvsp = yyvs + yysize - 1; + yylsp = yyls + yysize - 1; + + YY_IGNORE_USELESS_CAST_BEGIN + YYDPRINTF ((stderr, "Stack size increased to %ld\n", + YY_CAST (long, yystacksize))); + YY_IGNORE_USELESS_CAST_END + + if (yyss + yystacksize - 1 <= yyssp) + YYABORT; + } +#endif /* !defined yyoverflow && !defined YYSTACK_RELOCATE */ + + if (yystate == YYFINAL) + YYACCEPT; + + goto yybackup; + + +/*-----------. +| yybackup. | +`-----------*/ +yybackup: + /* Do appropriate processing given the current state. Read a + lookahead token if we need one and don't already have one. */ + + /* First try to decide what to do without reference to lookahead token. */ + yyn = yypact[yystate]; + if (yypact_value_is_default (yyn)) + goto yydefault; + + /* Not known => get a lookahead token if don't already have one. */ + + /* YYCHAR is either YYEMPTY or YYEOF or a valid lookahead symbol. */ + if (yychar == YYEMPTY) + { + YYDPRINTF ((stderr, "Reading a token: ")); + yychar = yylex (&yylval, &yylloc, scanner); + } + + 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 (yytable_value_is_error (yyn)) + goto yyerrlab; + yyn = -yyn; + goto yyreduce; + } + + /* Count tokens shifted since error; after three, turn off error + status. */ + if (yyerrstatus) + yyerrstatus--; + + /* Shift the lookahead token. */ + YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc); + yystate = yyn; + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + *++yyvsp = yylval; + YY_IGNORE_MAYBE_UNINITIALIZED_END + *++yylsp = yylloc; + + /* Discard the shifted token. */ + yychar = YYEMPTY; + 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); + yyerror_range[1] = yyloc; + YY_REDUCE_PRINT (yyn); + switch (yyn) + { + case 2: +#line 108 "src/core/io/dl-parser.y" + { context->n=(yyvsp[-4].integer); } +#line 1578 "yy.tab.c" + break; + + case 7: +#line 114 "src/core/io/dl-parser.y" + { context->type=IGRAPH_DL_MATRIX; } +#line 1584 "yy.tab.c" + break; + + case 8: +#line 115 "src/core/io/dl-parser.y" + { context->type=IGRAPH_DL_EDGELIST1; } +#line 1590 "yy.tab.c" + break; + + case 9: +#line 116 "src/core/io/dl-parser.y" + { context->type=IGRAPH_DL_NODELIST1; } +#line 1596 "yy.tab.c" + break; + + case 10: +#line 119 "src/core/io/dl-parser.y" + {} +#line 1602 "yy.tab.c" + break; + + case 11: +#line 119 "src/core/io/dl-parser.y" + {} +#line 1608 "yy.tab.c" + break; + + case 14: +#line 123 "src/core/io/dl-parser.y" + { } +#line 1614 "yy.tab.c" + break; + + case 15: +#line 124 "src/core/io/dl-parser.y" + { } +#line 1620 "yy.tab.c" + break; + + case 16: +#line 125 "src/core/io/dl-parser.y" + { } +#line 1626 "yy.tab.c" + break; + + case 17: +#line 128 "src/core/io/dl-parser.y" + {} +#line 1632 "yy.tab.c" + break; + + case 18: +#line 129 "src/core/io/dl-parser.y" + { + igraph_i_dl_add_str(igraph_dl_yyget_text(scanner), + igraph_dl_yyget_leng(scanner), + context); } +#line 1641 "yy.tab.c" + break; + + case 19: +#line 135 "src/core/io/dl-parser.y" + {} +#line 1647 "yy.tab.c" + break; + + case 20: +#line 135 "src/core/io/dl-parser.y" + { + context->from += 1; + context->to = 0; + } +#line 1656 "yy.tab.c" + break; + + case 22: +#line 140 "src/core/io/dl-parser.y" + { } +#line 1662 "yy.tab.c" + break; + + case 23: +#line 142 "src/core/io/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; +} +#line 1676 "yy.tab.c" + break; + + case 24: +#line 152 "src/core/io/dl-parser.y" + {} +#line 1682 "yy.tab.c" + break; + + case 25: +#line 154 "src/core/io/dl-parser.y" + {} +#line 1688 "yy.tab.c" + break; + + case 28: +#line 158 "src/core/io/dl-parser.y" + { igraph_i_dl_add_str(igraph_dl_yyget_text(scanner), + igraph_dl_yyget_leng(scanner), + context); } +#line 1696 "yy.tab.c" + break; + + case 29: +#line 162 "src/core/io/dl-parser.y" + { + context->from += 1; + context->to = 0; + } +#line 1705 "yy.tab.c" + break; + + case 30: +#line 166 "src/core/io/dl-parser.y" + { + context->from += 1; + context->to = 0; + } +#line 1714 "yy.tab.c" + break; + + case 31: +#line 171 "src/core/io/dl-parser.y" + { } +#line 1720 "yy.tab.c" + break; + + case 32: +#line 175 "src/core/io/dl-parser.y" + {} +#line 1726 "yy.tab.c" + break; + + case 33: +#line 177 "src/core/io/dl-parser.y" + {} +#line 1732 "yy.tab.c" + break; + + case 34: +#line 178 "src/core/io/dl-parser.y" + {} +#line 1738 "yy.tab.c" + break; + + case 35: +#line 179 "src/core/io/dl-parser.y" + {} +#line 1744 "yy.tab.c" + break; + + case 36: +#line 180 "src/core/io/dl-parser.y" + {} +#line 1750 "yy.tab.c" + break; + + case 37: +#line 181 "src/core/io/dl-parser.y" + {} +#line 1756 "yy.tab.c" + break; + + case 38: +#line 184 "src/core/io/dl-parser.y" + {} +#line 1762 "yy.tab.c" + break; + + case 39: +#line 185 "src/core/io/dl-parser.y" + {} +#line 1768 "yy.tab.c" + break; + + case 40: +#line 188 "src/core/io/dl-parser.y" + { + igraph_i_dl_add_edge_w((yyvsp[-3].integer)-1, (yyvsp[-2].integer)-1, (yyvsp[-1].real), context); } +#line 1775 "yy.tab.c" + break; + + case 41: +#line 190 "src/core/io/dl-parser.y" + { + igraph_i_dl_add_edge((yyvsp[-2].integer)-1, (yyvsp[-1].integer)-1, context); +} +#line 1783 "yy.tab.c" + break; + + case 42: +#line 194 "src/core/io/dl-parser.y" + { (yyval.integer)=igraph_pajek_get_number(igraph_dl_yyget_text(scanner), + igraph_dl_yyget_leng(scanner)); } +#line 1790 "yy.tab.c" + break; + + case 43: +#line 197 "src/core/io/dl-parser.y" + {} +#line 1796 "yy.tab.c" + break; + + case 44: +#line 198 "src/core/io/dl-parser.y" + {} +#line 1802 "yy.tab.c" + break; + + case 45: +#line 201 "src/core/io/dl-parser.y" + { + igraph_i_dl_add_edge_w((yyvsp[-3].integer), (yyvsp[-2].integer), (yyvsp[-1].real), context); } +#line 1809 "yy.tab.c" + break; + + case 46: +#line 203 "src/core/io/dl-parser.y" + { + igraph_i_dl_add_edge((yyvsp[-2].integer), (yyvsp[-1].integer), context); + } +#line 1817 "yy.tab.c" + break; + + case 47: +#line 207 "src/core/io/dl-parser.y" + { (yyval.real)=igraph_pajek_get_number(igraph_dl_yyget_text(scanner), + igraph_dl_yyget_leng(scanner)); } +#line 1824 "yy.tab.c" + break; + + case 48: +#line 210 "src/core/io/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; itrie, + 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)); + } +#line 1842 "yy.tab.c" + break; + + case 49: +#line 226 "src/core/io/dl-parser.y" + {} +#line 1848 "yy.tab.c" + break; + + case 50: +#line 228 "src/core/io/dl-parser.y" + {} +#line 1854 "yy.tab.c" + break; + + case 51: +#line 229 "src/core/io/dl-parser.y" + {} +#line 1860 "yy.tab.c" + break; + + case 52: +#line 230 "src/core/io/dl-parser.y" + {} +#line 1866 "yy.tab.c" + break; + + case 53: +#line 231 "src/core/io/dl-parser.y" + {} +#line 1872 "yy.tab.c" + break; + + case 54: +#line 232 "src/core/io/dl-parser.y" + {} +#line 1878 "yy.tab.c" + break; + + case 55: +#line 235 "src/core/io/dl-parser.y" + {} +#line 1884 "yy.tab.c" + break; + + case 56: +#line 236 "src/core/io/dl-parser.y" + {} +#line 1890 "yy.tab.c" + break; + + case 57: +#line 239 "src/core/io/dl-parser.y" + {} +#line 1896 "yy.tab.c" + break; + + case 58: +#line 241 "src/core/io/dl-parser.y" + { context->from=igraph_pajek_get_number(igraph_dl_yyget_text(scanner), + igraph_dl_yyget_leng(scanner)); } +#line 1903 "yy.tab.c" + break; + + case 59: +#line 244 "src/core/io/dl-parser.y" + {} +#line 1909 "yy.tab.c" + break; + + case 60: +#line 244 "src/core/io/dl-parser.y" + { + IGRAPH_CHECK(igraph_vector_push_back(&context->edges, + context->from-1)); + IGRAPH_CHECK(igraph_vector_push_back(&context->edges, (yyvsp[0].integer)-1)); + } +#line 1919 "yy.tab.c" + break; + + case 61: +#line 250 "src/core/io/dl-parser.y" + {} +#line 1925 "yy.tab.c" + break; + + case 62: +#line 251 "src/core/io/dl-parser.y" + {} +#line 1931 "yy.tab.c" + break; + + case 63: +#line 254 "src/core/io/dl-parser.y" + { } +#line 1937 "yy.tab.c" + break; + + case 64: +#line 256 "src/core/io/dl-parser.y" + { + context->from=(yyvsp[0].integer); + } +#line 1945 "yy.tab.c" + break; + + case 66: +#line 260 "src/core/io/dl-parser.y" + { + IGRAPH_CHECK(igraph_vector_push_back(&context->edges, + context->from)); + IGRAPH_CHECK(igraph_vector_push_back(&context->edges, (yyvsp[0].integer))); + } +#line 1955 "yy.tab.c" + break; + + +#line 1959 "yy.tab.c" + + default: break; + } + /* User semantic actions sometimes alter yychar, and that requires + that yytoken be updated with the new translation. We take the + approach of translating immediately before every use of yytoken. + One alternative is translating here after every semantic action, + but that translation would be missed if the semantic action invokes + YYABORT, YYACCEPT, or YYERROR immediately after altering yychar or + if it invokes YYBACKUP. In the case of YYABORT or YYACCEPT, an + incorrect destructor might then be invoked immediately. In the + case of YYERROR or YYBACKUP, subsequent parser actions might lead + to an incorrect destructor call or verbose syntax error message + before the lookahead is translated. */ + 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. */ + { + const int yylhs = yyr1[yyn] - YYNTOKENS; + const int yyi = yypgoto[yylhs] + *yyssp; + yystate = (0 <= yyi && yyi <= YYLAST && yycheck[yyi] == *yyssp + ? yytable[yyi] + : yydefgoto[yylhs]); + } + + goto yynewstate; + + +/*--------------------------------------. +| yyerrlab -- here on detecting error. | +`--------------------------------------*/ +yyerrlab: + /* Make sure we have latest lookahead translation. See comments at + user semantic actions for why this is necessary. */ + yytoken = yychar == YYEMPTY ? YYEMPTY : YYTRANSLATE (yychar); + + /* If not already recovering from an error, report this error. */ + if (!yyerrstatus) + { + ++yynerrs; +#if ! YYERROR_VERBOSE + yyerror (&yylloc, context, YY_("syntax error")); +#else +# define YYSYNTAX_ERROR yysyntax_error (&yymsg_alloc, &yymsg, \ + yyssp, yytoken) + { + char const *yymsgp = YY_("syntax error"); + int yysyntax_error_status; + yysyntax_error_status = YYSYNTAX_ERROR; + if (yysyntax_error_status == 0) + yymsgp = yymsg; + else if (yysyntax_error_status == 1) + { + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); + yymsg = YY_CAST (char *, YYSTACK_ALLOC (YY_CAST (YYSIZE_T, yymsg_alloc))); + if (!yymsg) + { + yymsg = yymsgbuf; + yymsg_alloc = sizeof yymsgbuf; + yysyntax_error_status = 2; + } + else + { + yysyntax_error_status = YYSYNTAX_ERROR; + yymsgp = yymsg; + } + } + yyerror (&yylloc, context, yymsgp); + if (yysyntax_error_status == 2) + goto yyexhaustedlab; + } +# undef YYSYNTAX_ERROR +#endif + } + + yyerror_range[1] = yylloc; + + if (yyerrstatus == 3) + { + /* If just tried and failed to reuse lookahead 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 lookahead token after shifting the error + token. */ + goto yyerrlab1; + + +/*---------------------------------------------------. +| yyerrorlab -- error raised explicitly by YYERROR. | +`---------------------------------------------------*/ +yyerrorlab: + /* Pacify compilers when the user code never invokes YYERROR and the + label yyerrorlab therefore never appears in user code. */ + if (0) + YYERROR; + + /* Do not reclaim the symbols of the rule whose 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 (!yypact_value_is_default (yyn)) + { + 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[1] = *yylsp; + yydestruct ("Error: popping", + yystos[yystate], yyvsp, yylsp, context); + YYPOPSTACK (1); + yystate = *yyssp; + YY_STACK_PRINT (yyss, yyssp); + } + + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + *++yyvsp = yylval; + YY_IGNORE_MAYBE_UNINITIALIZED_END + + yyerror_range[2] = yylloc; + /* Using YYLLOC is tempting, but would change the location of + the lookahead. YYLOC is available though. */ + YYLLOC_DEFAULT (yyloc, yyerror_range, 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; + + +#if !defined yyoverflow || YYERROR_VERBOSE +/*-------------------------------------------------. +| yyexhaustedlab -- memory exhaustion comes here. | +`-------------------------------------------------*/ +yyexhaustedlab: + yyerror (&yylloc, context, YY_("memory exhausted")); + yyresult = 2; + /* Fall through. */ +#endif + + +/*-----------------------------------------------------. +| yyreturn -- parsing is finished, return the result. | +`-----------------------------------------------------*/ +yyreturn: + if (yychar != YYEMPTY) + { + /* Make sure we have latest lookahead translation. See comments at + user semantic actions for why this is necessary. */ + yytoken = YYTRANSLATE (yychar); + yydestruct ("Cleanup: discarding lookahead", + yytoken, &yylval, &yylloc, context); + } + /* Do not reclaim the symbols of the rule whose 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 + return yyresult; +} +#line 266 "src/core/io/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 (; nweights)[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/src/rigraph/core/io/dl-parser.h b/src/rigraph/core/io/dl-parser.h new file mode 100644 index 0000000..675958f --- /dev/null +++ b/src/rigraph/core/io/dl-parser.h @@ -0,0 +1,104 @@ +/* A Bison parser, made by GNU Bison 3.5.1. */ + +/* Bison interface for Yacc-like parsers in C + + Copyright (C) 1984, 1989-1990, 2000-2015, 2018-2020 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 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 . */ + +/* 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. */ + +/* Undocumented macros, especially those whose name start with YY_, + are private implementation details. Do not rely on them. */ + +#ifndef YY_IGRAPH_DL_YY_YY_TAB_H_INCLUDED +# define YY_IGRAPH_DL_YY_YY_TAB_H_INCLUDED +/* Debug traces. */ +#ifndef YYDEBUG +# define YYDEBUG 0 +#endif +#if YYDEBUG +extern int igraph_dl_yydebug; +#endif + +/* Token type. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + 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 + +/* Value type. */ +#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED +union YYSTYPE +{ +#line 83 "src/core/io/dl-parser.y" + + long int integer; + igraph_real_t real; + +#line 79 "yy.tab.h" + +}; +typedef union YYSTYPE YYSTYPE; +# define YYSTYPE_IS_TRIVIAL 1 +# define YYSTYPE_IS_DECLARED 1 +#endif + +/* Location type. */ +#if ! defined YYLTYPE && ! defined YYLTYPE_IS_DECLARED +typedef struct YYLTYPE YYLTYPE; +struct YYLTYPE +{ + int first_line; + int first_column; + int last_line; + int last_column; +}; +# define YYLTYPE_IS_DECLARED 1 +# define YYLTYPE_IS_TRIVIAL 1 +#endif + + + +int igraph_dl_yyparse (igraph_i_dl_parsedata_t* context); + +#endif /* !YY_IGRAPH_DL_YY_YY_TAB_H_INCLUDED */ diff --git a/src/rigraph/core/io/dl-parser.y b/src/rigraph/core/io/dl-parser.y new file mode 100644 index 0000000..557ff9a --- /dev/null +++ b/src/rigraph/core/io/dl-parser.y @@ -0,0 +1,306 @@ +/* + IGraph library. + Copyright (C) 2009-2012 Gabor Csardi + 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 + +*/ + +%{ + +/* + IGraph library. + Copyright (C) 2009-2012 Gabor Csardi + 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 "core/math.h" +#include "internal/hacks.h" +#include "io/dl-header.h" +#include "io/parsers/dl-parser.h" +#include "io/parsers/dl-lexer.h" + +#include + +int igraph_dl_yyerror(YYLTYPE* locp, igraph_i_dl_parsedata_t* context, + const char *s); +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 + +%} + +%pure-parser +/* bison: do not remove the equals sign; macOS XCode ships with bison 2.3, which + * needs the equals sign */ +%name-prefix="igraph_dl_yy" +%defines +%locations +%error-verbose +%parse-param { igraph_i_dl_parsedata_t* context } +%lex-param { void* scanner } + +%union { + long int integer; + igraph_real_t real; +}; + +%type integer elabel; +%type weight; + +%token NUM +%token NEWLINE +%token DL +%token NEQ +%token DATA +%token LABELS +%token LABELSEMBEDDED +%token FORMATFULLMATRIX +%token FORMATEDGELIST1 +%token FORMATNODELIST1 +%token DIGIT +%token LABEL +%token EOFF +%token ERROR + +%% + +input: DL NEQ integer NEWLINE rest trail eof { context->n=$3; }; + +trail: | trail newline; + +eof: | EOFF; + +rest: formfullmatrix { context->type=IGRAPH_DL_MATRIX; } + | edgelist1 { context->type=IGRAPH_DL_EDGELIST1; } + | nodelist1 { context->type=IGRAPH_DL_NODELIST1; } +; + +formfullmatrix: FORMATFULLMATRIX newline fullmatrix {} | fullmatrix {} ; + +newline: | NEWLINE ; + +fullmatrix: DATA newline fullmatrixdata { } + | LABELS newline labels newline DATA newline fullmatrixdata { } + | LABELSEMBEDDED newline DATA newline labeledfullmatrixdata { } +; + +labels: {} /* nothing, empty matrix */ + | labels newline LABEL { + igraph_i_dl_add_str(igraph_dl_yyget_text(scanner), + igraph_dl_yyget_leng(scanner), + context); } +; + +fullmatrixdata: {} | fullmatrixdata zerooneseq NEWLINE { + context->from += 1; + context->to = 0; + } ; + +zerooneseq: | zerooneseq zeroone { } ; + +zeroone: DIGIT { + 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; +} ; + +labeledfullmatrixdata: reallabeledfullmatrixdata {} ; + +reallabeledfullmatrixdata: labelseq NEWLINE labeledmatrixlines {} ; + +labelseq: | labelseq newline label ; + +label: LABEL { igraph_i_dl_add_str(igraph_dl_yyget_text(scanner), + igraph_dl_yyget_leng(scanner), + context); }; + +labeledmatrixlines: labeledmatrixline { + context->from += 1; + context->to = 0; + } + | labeledmatrixlines labeledmatrixline { + context->from += 1; + context->to = 0; + }; + +labeledmatrixline: LABEL zerooneseq NEWLINE { } ; + +/*-----------------------------------------------------------*/ + +edgelist1: FORMATEDGELIST1 newline edgelist1rest {} ; + +edgelist1rest: DATA newline edgelist1data {} + | LABELS newline labels newline DATA newline edgelist1data {} + | LABELSEMBEDDED newline DATA newline labelededgelist1data {} + | LABELS newline labels newline LABELSEMBEDDED newline DATA newline labelededgelist1data {} + | LABELSEMBEDDED newline LABELS newline labels newline DATA newline labelededgelist1data {} +; + +edgelist1data: {} /* nothing, empty graph */ + | edgelist1data edgelist1dataline {} +; + +edgelist1dataline: integer integer weight NEWLINE { + igraph_i_dl_add_edge_w($1-1, $2-1, $3, context); } + | integer integer NEWLINE { + igraph_i_dl_add_edge($1-1, $2-1, context); +} ; + +integer: NUM { $$=igraph_pajek_get_number(igraph_dl_yyget_text(scanner), + igraph_dl_yyget_leng(scanner)); }; + +labelededgelist1data: {} /* nothing, empty graph */ + | labelededgelist1data labelededgelist1dataline {} +; + +labelededgelist1dataline: elabel elabel weight NEWLINE { + igraph_i_dl_add_edge_w($1, $2, $3, context); } + | elabel elabel NEWLINE { + igraph_i_dl_add_edge($1, $2, context); + }; + +weight: NUM { $$=igraph_pajek_get_number(igraph_dl_yyget_text(scanner), + igraph_dl_yyget_leng(scanner)); }; + +elabel: LABEL { + /* 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; itrie, + 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), &$$); + }; + +/*-----------------------------------------------------------*/ + +nodelist1: FORMATNODELIST1 newline nodelist1rest {} ; + +nodelist1rest: DATA nodelist1data {} + | LABELS newline labels newline DATA newline nodelist1data {} + | LABELSEMBEDDED newline DATA newline labelednodelist1data {} + | LABELS newline labels newline LABELSEMBEDDED newline DATA newline labelednodelist1data {} + | LABELSEMBEDDED newline LABELS newline labels newline DATA newline labelednodelist1data {} +; + +nodelist1data: {} /* nothing, empty graph */ + | nodelist1data nodelist1dataline {} +; + +nodelist1dataline: from tolist NEWLINE {} ; + +from: NUM { context->from=igraph_pajek_get_number(igraph_dl_yyget_text(scanner), + igraph_dl_yyget_leng(scanner)); } ; + +tolist: {} | tolist integer { + IGRAPH_CHECK(igraph_vector_push_back(&context->edges, + context->from-1)); + IGRAPH_CHECK(igraph_vector_push_back(&context->edges, $2-1)); + } ; + +labelednodelist1data: {} /* nothing, empty graph */ + | labelednodelist1data labelednodelist1dataline {} +; + +labelednodelist1dataline: fromelabel labeltolist NEWLINE { } ; + +fromelabel: elabel { + context->from=$1; + }; + +labeltolist: | labeltolist elabel { + IGRAPH_CHECK(igraph_vector_push_back(&context->edges, + context->from)); + IGRAPH_CHECK(igraph_vector_push_back(&context->edges, $2)); + } ; + +%% + +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 (; nweights)[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/src/rigraph/core/io/dl.c b/src/rigraph/core/io/dl.c new file mode 100644 index 0000000..26e1347 --- /dev/null +++ b/src/rigraph/core/io/dl.c @@ -0,0 +1,175 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2005-2020 The igraph development 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., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_foreign.h" + +#include "igraph_attributes.h" +#include "igraph_interface.h" + +#include "io/dl-header.h" + +int igraph_dl_yylex_init_extra (igraph_i_dl_parsedata_t* user_defined, + void* scanner); +void 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.) + * + * 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; +} diff --git a/src/rigraph/core/io/dot.c b/src/rigraph/core/io/dot.c new file mode 100644 index 0000000..b3ef3bc --- /dev/null +++ b/src/rigraph/core/io/dot.c @@ -0,0 +1,319 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2005-2020 The igraph development 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., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_foreign.h" + +#include "igraph_attributes.h" +#include "igraph_error.h" +#include "igraph_interface.h" +#include "igraph_memory.h" +#include "igraph_version.h" + +#include "graph/attributes.h" +#include "internal/hacks.h" /* strcasecmp */ + +#include +#include + +#define CHECK(cmd) do { ret=cmd; if (ret<0) IGRAPH_ERROR("Write DOT format failed.", IGRAPH_EFILE); } while (0) + +static 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 format failed.", IGRAPH_ENOMEM); + } + } else { + *result = IGRAPH_CALLOC(newlen + 3, char); + if (!*result) { + IGRAPH_ERROR("Writing DOT format failed.", IGRAPH_ENOMEM); + } + (*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 IGRAPH_SUCCESS; +} + +/** + * \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 + * + * 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(>ypes, 0); + IGRAPH_VECTOR_INIT_FINALLY(&vtypes, 0); + IGRAPH_VECTOR_INIT_FINALLY(&etypes, 0); + IGRAPH_CHECK(igraph_i_attribute_get_info(graph, + &gnames, >ypes, + &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", + IGRAPH_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(>ypes) > 0) { + CHECK(fprintf(outstream, " graph [\n")); + for (i = 0; i < igraph_vector_size(>ypes); i++) { + char *name, *newname; + igraph_strvector_get(&gnames, i, &name); + IGRAPH_CHECK(igraph_i_dot_escape(name, &newname)); + IGRAPH_FINALLY(igraph_free, 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); + IGRAPH_FINALLY_CLEAN(1); + } + 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)); + IGRAPH_FINALLY(igraph_free, 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); + IGRAPH_FINALLY_CLEAN(1); + } + 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)); + IGRAPH_FINALLY(igraph_free, 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)); + } + } 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(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"); + } + IGRAPH_FREE(newname); + IGRAPH_FINALLY_CLEAN(1); + } + 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(>ypes); + igraph_strvector_destroy(&enames); + igraph_strvector_destroy(&vnames); + igraph_strvector_destroy(&gnames); + IGRAPH_FINALLY_CLEAN(9); + + return IGRAPH_SUCCESS; +} + +#undef CHECK diff --git a/src/rigraph/core/io/edgelist.c b/src/rigraph/core/io/edgelist.c new file mode 100644 index 0000000..4eef988 --- /dev/null +++ b/src/rigraph/core/io/edgelist.c @@ -0,0 +1,159 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2005-2020 The igraph development 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., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_foreign.h" + +#include "igraph_constructors.h" +#include "igraph_interface.h" +#include "igraph_iterators.h" + +#include "core/interruption.h" + +#include + +/** + * \section about_loadsave + * + * These functions can write a graph to a file, or read a graph + * from a file. + * + * 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. + */ + +/** + * \ingroup loadsave + * \function igraph_read_graph_edgelist + * \brief Reads an edge list from a file and creates a graph. + * + * + * This format is simply a series of an even number of non-negative integers separated by + * whitespace. The integers represent vertex IDs. Placing each edge (i.e. pair of integers) + * on a separate line is not required, but it is 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; +} + +/** + * \ingroup loadsave + * \function igraph_write_graph_edgelist + * \brief Writes the edge list of a graph to a file. + * + * + * 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; +} diff --git a/src/rigraph/core/io/gml-header.h b/src/rigraph/core/io/gml-header.h new file mode 100644 index 0000000..618db87 --- /dev/null +++ b/src/rigraph/core/io/gml-header.h @@ -0,0 +1,44 @@ +/* + IGraph library. + Copyright (C) 2011-2012 Gabor Csardi + 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_error.h" + +#include "io/gml-tree.h" + +typedef struct { + void *scanner; + int eof; + int depth; + char errmsg[300]; + igraph_gml_tree_t *tree; +} igraph_i_gml_parsedata_t; + +/** + * Initializes a GML parser context. + */ +int igraph_i_gml_parsedata_init(igraph_i_gml_parsedata_t* context); + +/** + * Destroys a GML parser context, freeing all memory currently used by the + * context. + */ +void igraph_i_gml_parsedata_destroy(igraph_i_gml_parsedata_t* context); diff --git a/src/rigraph/core/io/gml-lexer.c b/src/rigraph/core/io/gml-lexer.c new file mode 100644 index 0000000..a5ea7fc --- /dev/null +++ b/src/rigraph/core/io/gml-lexer.c @@ -0,0 +1,2319 @@ +#line 2 "src/core/io/gml-lexer.c" + +#line 4 "src/core/io/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 6 +#define YY_FLEX_SUBMINOR_VERSION 4 +#if YY_FLEX_SUBMINOR_VERSION > 0 +#define FLEX_BETA +#endif + +#ifdef yy_create_buffer +#define igraph_gml_yy_create_buffer_ALREADY_DEFINED +#else +#define yy_create_buffer igraph_gml_yy_create_buffer +#endif + +#ifdef yy_delete_buffer +#define igraph_gml_yy_delete_buffer_ALREADY_DEFINED +#else +#define yy_delete_buffer igraph_gml_yy_delete_buffer +#endif + +#ifdef yy_scan_buffer +#define igraph_gml_yy_scan_buffer_ALREADY_DEFINED +#else +#define yy_scan_buffer igraph_gml_yy_scan_buffer +#endif + +#ifdef yy_scan_string +#define igraph_gml_yy_scan_string_ALREADY_DEFINED +#else +#define yy_scan_string igraph_gml_yy_scan_string +#endif + +#ifdef yy_scan_bytes +#define igraph_gml_yy_scan_bytes_ALREADY_DEFINED +#else +#define yy_scan_bytes igraph_gml_yy_scan_bytes +#endif + +#ifdef yy_init_buffer +#define igraph_gml_yy_init_buffer_ALREADY_DEFINED +#else +#define yy_init_buffer igraph_gml_yy_init_buffer +#endif + +#ifdef yy_flush_buffer +#define igraph_gml_yy_flush_buffer_ALREADY_DEFINED +#else +#define yy_flush_buffer igraph_gml_yy_flush_buffer +#endif + +#ifdef yy_load_buffer_state +#define igraph_gml_yy_load_buffer_state_ALREADY_DEFINED +#else +#define yy_load_buffer_state igraph_gml_yy_load_buffer_state +#endif + +#ifdef yy_switch_to_buffer +#define igraph_gml_yy_switch_to_buffer_ALREADY_DEFINED +#else +#define yy_switch_to_buffer igraph_gml_yy_switch_to_buffer +#endif + +#ifdef yypush_buffer_state +#define igraph_gml_yypush_buffer_state_ALREADY_DEFINED +#else +#define yypush_buffer_state igraph_gml_yypush_buffer_state +#endif + +#ifdef yypop_buffer_state +#define igraph_gml_yypop_buffer_state_ALREADY_DEFINED +#else +#define yypop_buffer_state igraph_gml_yypop_buffer_state +#endif + +#ifdef yyensure_buffer_stack +#define igraph_gml_yyensure_buffer_stack_ALREADY_DEFINED +#else +#define yyensure_buffer_stack igraph_gml_yyensure_buffer_stack +#endif + +#ifdef yylex +#define igraph_gml_yylex_ALREADY_DEFINED +#else +#define yylex igraph_gml_yylex +#endif + +#ifdef yyrestart +#define igraph_gml_yyrestart_ALREADY_DEFINED +#else +#define yyrestart igraph_gml_yyrestart +#endif + +#ifdef yylex_init +#define igraph_gml_yylex_init_ALREADY_DEFINED +#else +#define yylex_init igraph_gml_yylex_init +#endif + +#ifdef yylex_init_extra +#define igraph_gml_yylex_init_extra_ALREADY_DEFINED +#else +#define yylex_init_extra igraph_gml_yylex_init_extra +#endif + +#ifdef yylex_destroy +#define igraph_gml_yylex_destroy_ALREADY_DEFINED +#else +#define yylex_destroy igraph_gml_yylex_destroy +#endif + +#ifdef yyget_debug +#define igraph_gml_yyget_debug_ALREADY_DEFINED +#else +#define yyget_debug igraph_gml_yyget_debug +#endif + +#ifdef yyset_debug +#define igraph_gml_yyset_debug_ALREADY_DEFINED +#else +#define yyset_debug igraph_gml_yyset_debug +#endif + +#ifdef yyget_extra +#define igraph_gml_yyget_extra_ALREADY_DEFINED +#else +#define yyget_extra igraph_gml_yyget_extra +#endif + +#ifdef yyset_extra +#define igraph_gml_yyset_extra_ALREADY_DEFINED +#else +#define yyset_extra igraph_gml_yyset_extra +#endif + +#ifdef yyget_in +#define igraph_gml_yyget_in_ALREADY_DEFINED +#else +#define yyget_in igraph_gml_yyget_in +#endif + +#ifdef yyset_in +#define igraph_gml_yyset_in_ALREADY_DEFINED +#else +#define yyset_in igraph_gml_yyset_in +#endif + +#ifdef yyget_out +#define igraph_gml_yyget_out_ALREADY_DEFINED +#else +#define yyget_out igraph_gml_yyget_out +#endif + +#ifdef yyset_out +#define igraph_gml_yyset_out_ALREADY_DEFINED +#else +#define yyset_out igraph_gml_yyset_out +#endif + +#ifdef yyget_leng +#define igraph_gml_yyget_leng_ALREADY_DEFINED +#else +#define yyget_leng igraph_gml_yyget_leng +#endif + +#ifdef yyget_text +#define igraph_gml_yyget_text_ALREADY_DEFINED +#else +#define yyget_text igraph_gml_yyget_text +#endif + +#ifdef yyget_lineno +#define igraph_gml_yyget_lineno_ALREADY_DEFINED +#else +#define yyget_lineno igraph_gml_yyget_lineno +#endif + +#ifdef yyset_lineno +#define igraph_gml_yyset_lineno_ALREADY_DEFINED +#else +#define yyset_lineno igraph_gml_yyset_lineno +#endif + +#ifdef yyget_column +#define igraph_gml_yyget_column_ALREADY_DEFINED +#else +#define yyget_column igraph_gml_yyget_column +#endif + +#ifdef yyset_column +#define igraph_gml_yyset_column_ALREADY_DEFINED +#else +#define yyset_column igraph_gml_yyset_column +#endif + +#ifdef yywrap +#define igraph_gml_yywrap_ALREADY_DEFINED +#else +#define yywrap igraph_gml_yywrap +#endif + +#ifdef yyget_lval +#define igraph_gml_yyget_lval_ALREADY_DEFINED +#else +#define yyget_lval igraph_gml_yyget_lval +#endif + +#ifdef yyset_lval +#define igraph_gml_yyset_lval_ALREADY_DEFINED +#else +#define yyset_lval igraph_gml_yyset_lval +#endif + +#ifdef yyget_lloc +#define igraph_gml_yyget_lloc_ALREADY_DEFINED +#else +#define yyget_lloc igraph_gml_yyget_lloc +#endif + +#ifdef yyset_lloc +#define igraph_gml_yyset_lloc_ALREADY_DEFINED +#else +#define yyset_lloc igraph_gml_yyset_lloc +#endif + +#ifdef yyalloc +#define igraph_gml_yyalloc_ALREADY_DEFINED +#else +#define yyalloc igraph_gml_yyalloc +#endif + +#ifdef yyrealloc +#define igraph_gml_yyrealloc_ALREADY_DEFINED +#else +#define yyrealloc igraph_gml_yyrealloc +#endif + +#ifdef yyfree +#define igraph_gml_yyfree_ALREADY_DEFINED +#else +#define yyfree igraph_gml_yyfree +#endif + +/* First, we deal with platform-specific or compiler-specific issues. */ + +/* begin standard C headers. */ +#include +#include +#include +#include + +/* end standard C headers. */ + +/* flex integer type definitions */ + +#ifndef FLEXINT_H +#define FLEXINT_H + +/* C99 systems have . 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 +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; +#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; + +/* 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 + +#ifndef SIZE_MAX +#define SIZE_MAX (~(size_t)0) +#endif + +#endif /* ! C99 */ + +#endif /* ! FLEXINT_H */ + +/* begin standard C++ headers. */ + +/* TODO: this is always defined, so inline it */ +#define yyconst const + +#if defined(__GNUC__) && __GNUC__ >= 3 +#define yynoreturn __attribute__((__noreturn__)) +#else +#define yynoreturn +#endif + +/* Returned upon end-of-file. */ +#define YY_NULL 0 + +/* Promotes a possibly negative, possibly signed char to an + * integer in range [0..255] for use as an array index. + */ +#define YY_SC_TO_UI(c) ((YY_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 yyrestart( yyin , yyscanner ) +#define YY_END_OF_BUFFER_CHAR 0 + +/* Size of default input buffer. */ +#ifndef YY_BUF_SIZE +#ifdef __ia64__ +/* On IA-64, the buffer size is 16k, not 8k. + * Moreover, YY_BUF_SIZE is 2*YY_READ_BUF_SIZE in the general case. + * Ditto for the __ia64__ case accordingly. + */ +#define YY_BUF_SIZE 32768 +#else +#define YY_BUF_SIZE 16384 +#endif /* __ia64__ */ +#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) + #define YY_LINENO_REWIND_TO(ptr) + +/* 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. + */ + int yy_buf_size; + + /* Number of characters read into yy_ch_buf, not including EOB + * characters. + */ + int 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 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 yyrestart ( FILE *input_file , yyscan_t yyscanner ); +void yy_switch_to_buffer ( YY_BUFFER_STATE new_buffer , yyscan_t yyscanner ); +YY_BUFFER_STATE yy_create_buffer ( FILE *file, int size , yyscan_t yyscanner ); +void yy_delete_buffer ( YY_BUFFER_STATE b , yyscan_t yyscanner ); +void yy_flush_buffer ( YY_BUFFER_STATE b , yyscan_t yyscanner ); +void yypush_buffer_state ( YY_BUFFER_STATE new_buffer , yyscan_t yyscanner ); +void yypop_buffer_state ( yyscan_t yyscanner ); + +static void yyensure_buffer_stack ( yyscan_t yyscanner ); +static void yy_load_buffer_state ( yyscan_t yyscanner ); +static void yy_init_buffer ( YY_BUFFER_STATE b, FILE *file , yyscan_t yyscanner ); +#define YY_FLUSH_BUFFER yy_flush_buffer( YY_CURRENT_BUFFER , yyscanner) + +YY_BUFFER_STATE yy_scan_buffer ( char *base, yy_size_t size , yyscan_t yyscanner ); +YY_BUFFER_STATE yy_scan_string ( const char *yy_str , yyscan_t yyscanner ); +YY_BUFFER_STATE yy_scan_bytes ( const char *bytes, int len , yyscan_t yyscanner ); + +void *yyalloc ( yy_size_t , yyscan_t yyscanner ); +void *yyrealloc ( void *, yy_size_t , yyscan_t yyscanner ); +void yyfree ( void * , yyscan_t yyscanner ); + +#define yy_new_buffer yy_create_buffer +#define yy_set_interactive(is_interactive) \ + { \ + if ( ! YY_CURRENT_BUFFER ){ \ + yyensure_buffer_stack (yyscanner); \ + YY_CURRENT_BUFFER_LVALUE = \ + 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 ){\ + yyensure_buffer_stack (yyscanner); \ + YY_CURRENT_BUFFER_LVALUE = \ + 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(yyscanner) (/*CONSTCOND*/1) +#define YY_SKIP_YYWRAP +typedef flex_uint8_t 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 __attribute__((unused)) yy_fatal_error ( const 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 = (int) (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 const 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 const YY_CHAR 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 const YY_CHAR yy_meta[16] = + { 0, + 1, 1, 1, 2, 1, 1, 1, 1, 1, 3, + 3, 3, 1, 1, 4 + } ; + +static const flex_int16_t yy_base[32] = + { 0, + 0, 12, 44, 45, 45, 39, 39, 36, 30, 10, + 0, 45, 45, 36, 35, 45, 32, 45, 0, 26, + 16, 0, 32, 45, 15, 22, 11, 45, 27, 14, + 30 + } ; + +static const 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 const flex_int16_t yy_nxt[61] = + { 0, + 4, 5, 6, 7, 8, 4, 4, 9, 4, 10, + 11, 11, 12, 13, 4, 14, 22, 15, 20, 19, + 27, 21, 26, 26, 25, 27, 21, 17, 17, 17, + 23, 27, 23, 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, 28 + } ; + +static const flex_int16_t yy_chk[61] = + { 0, + 1, 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, 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, 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/core/io/gml-lexer.l" +/* + IGraph library. + Copyright (C) 2007-2012 Gabor Csardi + 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/core/io/gml-lexer.l" + +/* + IGraph library. + Copyright (C) 2007-2012 Gabor Csardi + 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 + +#include "io/gml-header.h" +#include "io/gml-parser.h" + +#define YY_EXTRA_TYPE igraph_i_gml_parsedata_t* +#define YY_USER_ACTION yylloc->first_line = yylineno; +#define YY_FATAL_ERROR(msg) IGRAPH_FATAL("Error in GML parser: " # msg) +#ifdef USING_R +#define fprintf(file, msg, ...) (1) +#ifdef stdout +# undef stdout +#endif +#define stdout 0 +#endif +#line 736 "src/core/io/gml-lexer.c" +#define YY_NO_INPUT 1 +#line 738 "src/core/io/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 +#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; + int yy_n_chars; + int 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 yylex_init (yyscan_t* scanner); + +int 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 yylex_destroy ( yyscan_t yyscanner ); + +int yyget_debug ( yyscan_t yyscanner ); + +void yyset_debug ( int debug_flag , yyscan_t yyscanner ); + +YY_EXTRA_TYPE yyget_extra ( yyscan_t yyscanner ); + +void yyset_extra ( YY_EXTRA_TYPE user_defined , yyscan_t yyscanner ); + +FILE *yyget_in ( yyscan_t yyscanner ); + +void yyset_in ( FILE * _in_str , yyscan_t yyscanner ); + +FILE *yyget_out ( yyscan_t yyscanner ); + +void yyset_out ( FILE * _out_str , yyscan_t yyscanner ); + + yy_size_t yyget_leng ( yyscan_t yyscanner ); + +char *yyget_text ( yyscan_t yyscanner ); + +int yyget_lineno ( yyscan_t yyscanner ); + +void yyset_lineno ( int _line_number , yyscan_t yyscanner ); + +int yyget_column ( yyscan_t yyscanner ); + +void yyset_column ( int _column_no , yyscan_t yyscanner ); + +YYSTYPE * yyget_lval ( yyscan_t yyscanner ); + +void yyset_lval ( YYSTYPE * yylval_param , yyscan_t yyscanner ); + + YYLTYPE *yyget_lloc ( yyscan_t yyscanner ); + + void 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 yywrap ( yyscan_t yyscanner ); +#else +extern int yywrap ( yyscan_t yyscanner ); +#endif +#endif + +#ifndef YY_NO_UNPUT + +#endif + +#ifndef yytext_ptr +static void yy_flex_strncpy ( char *, const char *, int , yyscan_t yyscanner); +#endif + +#ifdef YY_NEED_STRLEN +static int yy_flex_strlen ( const 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 +#ifdef __ia64__ +/* On IA-64, the buffer size is 16k, not 8k */ +#define YY_READ_BUF_SIZE 16384 +#else +#define YY_READ_BUF_SIZE 8192 +#endif /* __ia64__ */ +#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 do { if (fwrite( yytext, (size_t) yyleng, 1, yyout )) {} } while (0) +#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 = '*'; \ + int 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 = (int) fread(buf, 1, (yy_size_t) 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 yylex \ + (YYSTYPE * yylval_param, YYLTYPE * yylloc_param , yyscan_t yyscanner); + +#define YY_DECL int 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 /*LINTED*/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 +{ + yy_state_type yy_current_state; + char *yy_cp, *yy_bp; + int yy_act; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + 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 ) { + yyensure_buffer_stack (yyscanner); + YY_CURRENT_BUFFER_LVALUE = + yy_create_buffer( yyin, YY_BUF_SIZE , yyscanner); + } + + yy_load_buffer_state( yyscanner ); + } + + { +#line 77 "src/core/io/gml-lexer.l" + + +#line 1026 "src/core/io/gml-lexer.c" + + while ( /*CONSTCOND*/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 + { + 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[yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c]; + ++yy_cp; + } + while ( yy_base[yy_current_state] != 45 ); + +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 79 "src/core/io/gml-lexer.l" +{ /* comments ignored */ } + YY_BREAK +case 2: +/* rule 2 can match eol */ +YY_RULE_SETUP +#line 81 "src/core/io/gml-lexer.l" +{ return STRING; } + YY_BREAK +case 3: +YY_RULE_SETUP +#line 82 "src/core/io/gml-lexer.l" +{ return NUM; } + YY_BREAK +case 4: +YY_RULE_SETUP +#line 83 "src/core/io/gml-lexer.l" +{ return KEYWORD; } + YY_BREAK +case 5: +YY_RULE_SETUP +#line 84 "src/core/io/gml-lexer.l" +{ + yyextra->depth++; + if (yyextra->depth >= 32) { + return ERROR; + } else { + return LISTOPEN; + } + } + YY_BREAK +case 6: +YY_RULE_SETUP +#line 92 "src/core/io/gml-lexer.l" +{ + yyextra->depth--; + if (yyextra->depth < 0) { + return ERROR; + } else { + return LISTCLOSE; + } + } + YY_BREAK +case 7: +/* rule 7 can match eol */ +YY_RULE_SETUP +#line 100 "src/core/io/gml-lexer.l" +{ } + YY_BREAK +case 8: +/* rule 8 can match eol */ +YY_RULE_SETUP +#line 101 "src/core/io/gml-lexer.l" +{ /* other whitespace ignored */ } + YY_BREAK +case YY_STATE_EOF(INITIAL): +#line 103 "src/core/io/gml-lexer.l" +{ + if (yyextra->eof) { + yyterminate(); + } else { + yyextra->eof=1; + return EOFF; + } + } + YY_BREAK +case 9: +YY_RULE_SETUP +#line 111 "src/core/io/gml-lexer.l" +{ return ERROR; } + YY_BREAK +case 10: +YY_RULE_SETUP +#line 112 "src/core/io/gml-lexer.l" +YY_FATAL_ERROR( "flex scanner jammed" ); + YY_BREAK +#line 1163 "src/core/io/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 + * 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 ( 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 user's declarations */ +} /* end of 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; + char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf; + char *source = yyg->yytext_ptr; + 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 + { + int 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_LVALUE; + + int yy_c_buf_p_offset = + (int) (yyg->yy_c_buf_p - b->yy_ch_buf); + + if ( b->yy_is_our_buffer ) + { + int 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. */ + yyrealloc( (void *) b->yy_ch_buf, + (yy_size_t) (b->yy_buf_size + 2) , yyscanner ); + } + else + /* Can't grow it, we don't own it. */ + b->yy_ch_buf = NULL; + + 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; + 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 ((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. */ + int new_size = yyg->yy_n_chars + number_to_move + (yyg->yy_n_chars >> 1); + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) yyrealloc( + (void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf, (yy_size_t) new_size , yyscanner ); + if ( ! YY_CURRENT_BUFFER_LVALUE->yy_ch_buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_get_next_buffer()" ); + /* "- 2" to take care of EOB's */ + YY_CURRENT_BUFFER_LVALUE->yy_buf_size = (int) (new_size - 2); + } + + 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) +{ + yy_state_type yy_current_state; + 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 ) + { + YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 15); + 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[yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + 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) +{ + int yy_is_jam; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* This var may be unused depending upon options. */ + char *yy_cp = yyg->yy_c_buf_p; + + YY_CHAR yy_c = 15; + 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[yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c]; + yy_is_jam = (yy_current_state == 28); + + (void)yyg; + return yy_is_jam ? 0 : yy_current_state; +} + +#ifndef YY_NO_UNPUT + +#endif + +#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 */ + int offset = (int) (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. */ + yyrestart( yyin , yyscanner); + + /*FALLTHROUGH*/ + + case EOB_ACT_END_OF_FILE: + { + if ( 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 yyrestart (FILE * input_file , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + if ( ! YY_CURRENT_BUFFER ){ + yyensure_buffer_stack (yyscanner); + YY_CURRENT_BUFFER_LVALUE = + yy_create_buffer( yyin, YY_BUF_SIZE , yyscanner); + } + + yy_init_buffer( YY_CURRENT_BUFFER, input_file , yyscanner); + yy_load_buffer_state( yyscanner ); +} + +/** Switch to a different input buffer. + * @param new_buffer The new input buffer. + * @param yyscanner The scanner object. + */ + void 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 + * yypop_buffer_state(); + * yypush_buffer_state(new_buffer); + */ + 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; + yy_load_buffer_state( yyscanner ); + + /* We don't actually know whether we did this switch during + * EOF (yywrap()) processing, but the only time this flag + * is looked at is after yywrap() is called, so it's safe + * to go ahead and always set it. + */ + yyg->yy_did_buffer_switch_on_eof = 1; +} + +static void 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 yy_create_buffer (FILE * file, int size , yyscan_t yyscanner) +{ + YY_BUFFER_STATE b; + + b = (YY_BUFFER_STATE) yyalloc( sizeof( struct yy_buffer_state ) , yyscanner ); + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in 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 *) yyalloc( (yy_size_t) (b->yy_buf_size + 2) , yyscanner ); + if ( ! b->yy_ch_buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); + + b->yy_is_our_buffer = 1; + + yy_init_buffer( b, file , yyscanner); + + return b; +} + +/** Destroy the buffer. + * @param b a buffer created with yy_create_buffer() + * @param yyscanner The scanner object. + */ + void 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 ) + yyfree( (void *) b->yy_ch_buf , yyscanner ); + + yyfree( (void *) b , yyscanner ); +} + +/* Initializes or reinitializes a buffer. + * This function is sometimes called more than once on the same buffer, + * such as during a yyrestart() or at EOF. + */ + static void yy_init_buffer (YY_BUFFER_STATE b, FILE * file , yyscan_t yyscanner) + +{ + int oerrno = errno; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + yy_flush_buffer( b , yyscanner); + + b->yy_input_file = file; + b->yy_fill_buffer = 1; + + /* If b is the current buffer, then yy_init_buffer was _probably_ + * called from 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 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 ) + 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 yypush_buffer_state (YY_BUFFER_STATE new_buffer , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + if (new_buffer == NULL) + return; + + yyensure_buffer_stack(yyscanner); + + /* This block is copied from 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 yy_switch_to_buffer. */ + 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 yypop_buffer_state (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + if (!YY_CURRENT_BUFFER) + return; + + 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) { + 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 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; /* After all that talk, this was set to 1 anyways... */ + yyg->yy_buffer_stack = (struct yy_buffer_state**)yyalloc + (num_to_alloc * sizeof(struct yy_buffer_state*) + , yyscanner); + if ( ! yyg->yy_buffer_stack ) + YY_FATAL_ERROR( "out of dynamic memory in 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. */ + yy_size_t grow_size = 8 /* arbitrary grow size */; + + num_to_alloc = yyg->yy_buffer_stack_max + grow_size; + yyg->yy_buffer_stack = (struct yy_buffer_state**)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 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 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 NULL; + + b = (YY_BUFFER_STATE) yyalloc( sizeof( struct yy_buffer_state ) , yyscanner ); + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in yy_scan_buffer()" ); + + b->yy_buf_size = (int) (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 = NULL; + 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; + + yy_switch_to_buffer( b , yyscanner ); + + return b; +} + +/** Setup the input buffer state to scan a string. The next call to 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 + * yy_scan_bytes() instead. + */ +YY_BUFFER_STATE yy_scan_string (const char * yystr , yyscan_t yyscanner) +{ + + return yy_scan_bytes( yystr, (int) strlen(yystr) , yyscanner); +} + +/** Setup the input buffer state to scan the given bytes. The next call to yylex() will + * scan from a @e copy of @a bytes. + * @param yybytes the byte buffer to scan + * @param _yybytes_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 yy_scan_bytes (const char * yybytes, int _yybytes_len , yyscan_t yyscanner) +{ + YY_BUFFER_STATE b; + char *buf; + yy_size_t n; + int i; + + /* Get memory for full buffer, including space for trailing EOB's. */ + n = (yy_size_t) (_yybytes_len + 2); + buf = (char *) yyalloc( n , yyscanner ); + if ( ! buf ) + YY_FATAL_ERROR( "out of dynamic memory in 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 = yy_scan_buffer( buf, n , yyscanner); + if ( ! b ) + YY_FATAL_ERROR( "bad buffer in 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 __attribute__((unused)) yy_fatal_error (const char* msg , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + (void)yyg; + fprintf( stderr, "%s\n", msg ); +} + +/* 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 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 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 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 *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 *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 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 *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 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 line number + * @param yyscanner The scanner object. + */ +void 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( "yyset_lineno called with no buffer" ); + + yylineno = _line_number; +} + +/** Set the current column. + * @param _column_no column number + * @param yyscanner The scanner object. + */ +void 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( "yyset_column called with no buffer" ); + + 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 yy_switch_to_buffer + */ +void yyset_in (FILE * _in_str , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yyin = _in_str ; +} + +void yyset_out (FILE * _out_str , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yyout = _out_str ; +} + +int yyget_debug (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yy_flex_debug; +} + +void 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 * yyget_lval (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yylval; +} + +void yyset_lval (YYSTYPE * yylval_param , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yylval = yylval_param; +} + +YYLTYPE *yyget_lloc (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yylloc; +} + +void yyset_lloc (YYLTYPE * yylloc_param , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yylloc = yylloc_param; +} + +/* User-visible API */ + +/* 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 yylex_init(yyscan_t* ptr_yy_globals) +{ + if (ptr_yy_globals == NULL){ + errno = EINVAL; + return 1; + } + + *ptr_yy_globals = (yyscan_t) 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 ); +} + +/* yylex_init_extra has the same functionality as 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 yyalloc in + * the yyextra field. + */ +int yylex_init_extra( YY_EXTRA_TYPE yy_user_defined, yyscan_t* ptr_yy_globals ) +{ + struct yyguts_t dummy_yyguts; + + yyset_extra (yy_user_defined, &dummy_yyguts); + + if (ptr_yy_globals == NULL){ + errno = EINVAL; + return 1; + } + + *ptr_yy_globals = (yyscan_t) 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)); + + 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 yylex_destroy(), so don't allocate here. + */ + + yyg->yy_buffer_stack = NULL; + yyg->yy_buffer_stack_top = 0; + yyg->yy_buffer_stack_max = 0; + yyg->yy_c_buf_p = NULL; + 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 = NULL; + yyout = NULL; +#endif + + /* For future reference: Set errno on error, since we are called by + * yylex_init() + */ + return 0; +} + +/* yylex_destroy is for both reentrant and non-reentrant scanners. */ +int yylex_destroy (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + /* Pop the buffer stack, destroying each element. */ + while(YY_CURRENT_BUFFER){ + yy_delete_buffer( YY_CURRENT_BUFFER , yyscanner ); + YY_CURRENT_BUFFER_LVALUE = NULL; + yypop_buffer_state(yyscanner); + } + + /* Destroy the stack itself. */ + yyfree(yyg->yy_buffer_stack , yyscanner); + yyg->yy_buffer_stack = NULL; + + /* Destroy the start condition stack. */ + 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 + * yylex() is called, initialization will occur. */ + yy_init_globals( yyscanner); + + /* Destroy the main struct (reentrant only). */ + yyfree ( yyscanner , yyscanner ); + yyscanner = NULL; + return 0; +} + +/* + * Internal utility routines. + */ + +#ifndef yytext_ptr +static void yy_flex_strncpy (char* s1, const char * s2, int n , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + (void)yyg; + + int i; + for ( i = 0; i < n; ++i ) + s1[i] = s2[i]; +} +#endif + +#ifdef YY_NEED_STRLEN +static int yy_flex_strlen (const char * s , yyscan_t yyscanner) +{ + int n; + for ( n = 0; s[n]; ++n ) + ; + + return n; +} +#endif + +void *yyalloc (yy_size_t size , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + (void)yyg; + return malloc(size); +} + +void *yyrealloc (void * ptr, yy_size_t size , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + (void)yyg; + + /* 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 realloc(ptr, size); +} + +void yyfree (void * ptr , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + (void)yyg; + free( (char *) ptr ); /* see yyrealloc() for (char *) cast */ +} + +#define YYTABLES_NAME "yytables" + +#line 112 "src/core/io/gml-lexer.l" + + diff --git a/src/rigraph/core/io/gml-lexer.h b/src/rigraph/core/io/gml-lexer.h new file mode 100644 index 0000000..0d2c1e4 --- /dev/null +++ b/src/rigraph/core/io/gml-lexer.h @@ -0,0 +1,736 @@ +#ifndef igraph_gml_yyHEADER_H +#define igraph_gml_yyHEADER_H 1 +#define igraph_gml_yyIN_HEADER 1 + +#line 6 "src/core/io/gml-lexer.h" + +#line 8 "src/core/io/gml-lexer.h" + +#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 6 +#define YY_FLEX_SUBMINOR_VERSION 4 +#if YY_FLEX_SUBMINOR_VERSION > 0 +#define FLEX_BETA +#endif + +#ifdef yy_create_buffer +#define igraph_gml_yy_create_buffer_ALREADY_DEFINED +#else +#define yy_create_buffer igraph_gml_yy_create_buffer +#endif + +#ifdef yy_delete_buffer +#define igraph_gml_yy_delete_buffer_ALREADY_DEFINED +#else +#define yy_delete_buffer igraph_gml_yy_delete_buffer +#endif + +#ifdef yy_scan_buffer +#define igraph_gml_yy_scan_buffer_ALREADY_DEFINED +#else +#define yy_scan_buffer igraph_gml_yy_scan_buffer +#endif + +#ifdef yy_scan_string +#define igraph_gml_yy_scan_string_ALREADY_DEFINED +#else +#define yy_scan_string igraph_gml_yy_scan_string +#endif + +#ifdef yy_scan_bytes +#define igraph_gml_yy_scan_bytes_ALREADY_DEFINED +#else +#define yy_scan_bytes igraph_gml_yy_scan_bytes +#endif + +#ifdef yy_init_buffer +#define igraph_gml_yy_init_buffer_ALREADY_DEFINED +#else +#define yy_init_buffer igraph_gml_yy_init_buffer +#endif + +#ifdef yy_flush_buffer +#define igraph_gml_yy_flush_buffer_ALREADY_DEFINED +#else +#define yy_flush_buffer igraph_gml_yy_flush_buffer +#endif + +#ifdef yy_load_buffer_state +#define igraph_gml_yy_load_buffer_state_ALREADY_DEFINED +#else +#define yy_load_buffer_state igraph_gml_yy_load_buffer_state +#endif + +#ifdef yy_switch_to_buffer +#define igraph_gml_yy_switch_to_buffer_ALREADY_DEFINED +#else +#define yy_switch_to_buffer igraph_gml_yy_switch_to_buffer +#endif + +#ifdef yypush_buffer_state +#define igraph_gml_yypush_buffer_state_ALREADY_DEFINED +#else +#define yypush_buffer_state igraph_gml_yypush_buffer_state +#endif + +#ifdef yypop_buffer_state +#define igraph_gml_yypop_buffer_state_ALREADY_DEFINED +#else +#define yypop_buffer_state igraph_gml_yypop_buffer_state +#endif + +#ifdef yyensure_buffer_stack +#define igraph_gml_yyensure_buffer_stack_ALREADY_DEFINED +#else +#define yyensure_buffer_stack igraph_gml_yyensure_buffer_stack +#endif + +#ifdef yylex +#define igraph_gml_yylex_ALREADY_DEFINED +#else +#define yylex igraph_gml_yylex +#endif + +#ifdef yyrestart +#define igraph_gml_yyrestart_ALREADY_DEFINED +#else +#define yyrestart igraph_gml_yyrestart +#endif + +#ifdef yylex_init +#define igraph_gml_yylex_init_ALREADY_DEFINED +#else +#define yylex_init igraph_gml_yylex_init +#endif + +#ifdef yylex_init_extra +#define igraph_gml_yylex_init_extra_ALREADY_DEFINED +#else +#define yylex_init_extra igraph_gml_yylex_init_extra +#endif + +#ifdef yylex_destroy +#define igraph_gml_yylex_destroy_ALREADY_DEFINED +#else +#define yylex_destroy igraph_gml_yylex_destroy +#endif + +#ifdef yyget_debug +#define igraph_gml_yyget_debug_ALREADY_DEFINED +#else +#define yyget_debug igraph_gml_yyget_debug +#endif + +#ifdef yyset_debug +#define igraph_gml_yyset_debug_ALREADY_DEFINED +#else +#define yyset_debug igraph_gml_yyset_debug +#endif + +#ifdef yyget_extra +#define igraph_gml_yyget_extra_ALREADY_DEFINED +#else +#define yyget_extra igraph_gml_yyget_extra +#endif + +#ifdef yyset_extra +#define igraph_gml_yyset_extra_ALREADY_DEFINED +#else +#define yyset_extra igraph_gml_yyset_extra +#endif + +#ifdef yyget_in +#define igraph_gml_yyget_in_ALREADY_DEFINED +#else +#define yyget_in igraph_gml_yyget_in +#endif + +#ifdef yyset_in +#define igraph_gml_yyset_in_ALREADY_DEFINED +#else +#define yyset_in igraph_gml_yyset_in +#endif + +#ifdef yyget_out +#define igraph_gml_yyget_out_ALREADY_DEFINED +#else +#define yyget_out igraph_gml_yyget_out +#endif + +#ifdef yyset_out +#define igraph_gml_yyset_out_ALREADY_DEFINED +#else +#define yyset_out igraph_gml_yyset_out +#endif + +#ifdef yyget_leng +#define igraph_gml_yyget_leng_ALREADY_DEFINED +#else +#define yyget_leng igraph_gml_yyget_leng +#endif + +#ifdef yyget_text +#define igraph_gml_yyget_text_ALREADY_DEFINED +#else +#define yyget_text igraph_gml_yyget_text +#endif + +#ifdef yyget_lineno +#define igraph_gml_yyget_lineno_ALREADY_DEFINED +#else +#define yyget_lineno igraph_gml_yyget_lineno +#endif + +#ifdef yyset_lineno +#define igraph_gml_yyset_lineno_ALREADY_DEFINED +#else +#define yyset_lineno igraph_gml_yyset_lineno +#endif + +#ifdef yyget_column +#define igraph_gml_yyget_column_ALREADY_DEFINED +#else +#define yyget_column igraph_gml_yyget_column +#endif + +#ifdef yyset_column +#define igraph_gml_yyset_column_ALREADY_DEFINED +#else +#define yyset_column igraph_gml_yyset_column +#endif + +#ifdef yywrap +#define igraph_gml_yywrap_ALREADY_DEFINED +#else +#define yywrap igraph_gml_yywrap +#endif + +#ifdef yyget_lval +#define igraph_gml_yyget_lval_ALREADY_DEFINED +#else +#define yyget_lval igraph_gml_yyget_lval +#endif + +#ifdef yyset_lval +#define igraph_gml_yyset_lval_ALREADY_DEFINED +#else +#define yyset_lval igraph_gml_yyset_lval +#endif + +#ifdef yyget_lloc +#define igraph_gml_yyget_lloc_ALREADY_DEFINED +#else +#define yyget_lloc igraph_gml_yyget_lloc +#endif + +#ifdef yyset_lloc +#define igraph_gml_yyset_lloc_ALREADY_DEFINED +#else +#define yyset_lloc igraph_gml_yyset_lloc +#endif + +#ifdef yyalloc +#define igraph_gml_yyalloc_ALREADY_DEFINED +#else +#define yyalloc igraph_gml_yyalloc +#endif + +#ifdef yyrealloc +#define igraph_gml_yyrealloc_ALREADY_DEFINED +#else +#define yyrealloc igraph_gml_yyrealloc +#endif + +#ifdef yyfree +#define igraph_gml_yyfree_ALREADY_DEFINED +#else +#define yyfree igraph_gml_yyfree +#endif + +/* First, we deal with platform-specific or compiler-specific issues. */ + +/* begin standard C headers. */ +#include +#include +#include +#include + +/* end standard C headers. */ + +/* flex integer type definitions */ + +#ifndef FLEXINT_H +#define FLEXINT_H + +/* C99 systems have . 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 +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; +#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; + +/* 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 + +#ifndef SIZE_MAX +#define SIZE_MAX (~(size_t)0) +#endif + +#endif /* ! C99 */ + +#endif /* ! FLEXINT_H */ + +/* begin standard C++ headers. */ + +/* TODO: this is always defined, so inline it */ +#define yyconst const + +#if defined(__GNUC__) && __GNUC__ >= 3 +#define yynoreturn __attribute__((__noreturn__)) +#else +#define yynoreturn +#endif + +/* 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 + +/* Size of default input buffer. */ +#ifndef YY_BUF_SIZE +#ifdef __ia64__ +/* On IA-64, the buffer size is 16k, not 8k. + * Moreover, YY_BUF_SIZE is 2*YY_READ_BUF_SIZE in the general case. + * Ditto for the __ia64__ case accordingly. + */ +#define YY_BUF_SIZE 32768 +#else +#define YY_BUF_SIZE 16384 +#endif /* __ia64__ */ +#endif + +#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 + +#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. + */ + int yy_buf_size; + + /* Number of characters read into yy_ch_buf, not including EOB + * characters. + */ + int 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; + + }; +#endif /* !YY_STRUCT_YY_BUFFER_STATE */ + +void yyrestart ( FILE *input_file , yyscan_t yyscanner ); +void yy_switch_to_buffer ( YY_BUFFER_STATE new_buffer , yyscan_t yyscanner ); +YY_BUFFER_STATE yy_create_buffer ( FILE *file, int size , yyscan_t yyscanner ); +void yy_delete_buffer ( YY_BUFFER_STATE b , yyscan_t yyscanner ); +void yy_flush_buffer ( YY_BUFFER_STATE b , yyscan_t yyscanner ); +void yypush_buffer_state ( YY_BUFFER_STATE new_buffer , yyscan_t yyscanner ); +void yypop_buffer_state ( yyscan_t yyscanner ); + +YY_BUFFER_STATE yy_scan_buffer ( char *base, yy_size_t size , yyscan_t yyscanner ); +YY_BUFFER_STATE yy_scan_string ( const char *yy_str , yyscan_t yyscanner ); +YY_BUFFER_STATE yy_scan_bytes ( const char *bytes, int len , yyscan_t yyscanner ); + +void *yyalloc ( yy_size_t , yyscan_t yyscanner ); +void *yyrealloc ( void *, yy_size_t , yyscan_t yyscanner ); +void yyfree ( void * , yyscan_t yyscanner ); + +/* Begin user sect3 */ + +#define igraph_gml_yywrap(yyscanner) (/*CONSTCOND*/1) +#define YY_SKIP_YYWRAP + +#define yytext_ptr yytext_r + +#ifdef YY_HEADER_EXPORT_START_CONDITIONS +#define INITIAL 0 + +#endif + +#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 +#endif + +#ifndef YY_EXTRA_TYPE +#define YY_EXTRA_TYPE void * +#endif + +int yylex_init (yyscan_t* scanner); + +int 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 yylex_destroy ( yyscan_t yyscanner ); + +int yyget_debug ( yyscan_t yyscanner ); + +void yyset_debug ( int debug_flag , yyscan_t yyscanner ); + +YY_EXTRA_TYPE yyget_extra ( yyscan_t yyscanner ); + +void yyset_extra ( YY_EXTRA_TYPE user_defined , yyscan_t yyscanner ); + +FILE *yyget_in ( yyscan_t yyscanner ); + +void yyset_in ( FILE * _in_str , yyscan_t yyscanner ); + +FILE *yyget_out ( yyscan_t yyscanner ); + +void yyset_out ( FILE * _out_str , yyscan_t yyscanner ); + + int yyget_leng ( yyscan_t yyscanner ); + +char *yyget_text ( yyscan_t yyscanner ); + +int yyget_lineno ( yyscan_t yyscanner ); + +void yyset_lineno ( int _line_number , yyscan_t yyscanner ); + +int yyget_column ( yyscan_t yyscanner ); + +void yyset_column ( int _column_no , yyscan_t yyscanner ); + +YYSTYPE * yyget_lval ( yyscan_t yyscanner ); + +void yyset_lval ( YYSTYPE * yylval_param , yyscan_t yyscanner ); + + YYLTYPE *yyget_lloc ( yyscan_t yyscanner ); + + void 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 yywrap ( yyscan_t yyscanner ); +#else +extern int yywrap ( yyscan_t yyscanner ); +#endif +#endif + +#ifndef yytext_ptr +static void yy_flex_strncpy ( char *, const char *, int , yyscan_t yyscanner); +#endif + +#ifdef YY_NEED_STRLEN +static int yy_flex_strlen ( const char * , yyscan_t yyscanner); +#endif + +#ifndef YY_NO_INPUT + +#endif + +/* Amount of stuff to slurp up with each read. */ +#ifndef YY_READ_BUF_SIZE +#ifdef __ia64__ +/* On IA-64, the buffer size is 16k, not 8k */ +#define YY_READ_BUF_SIZE 16384 +#else +#define YY_READ_BUF_SIZE 8192 +#endif /* __ia64__ */ +#endif + +/* Number of entries by which start-condition stack grows. */ +#ifndef YY_START_STACK_INCR +#define YY_START_STACK_INCR 25 +#endif + +/* 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 yylex \ + (YYSTYPE * yylval_param, YYLTYPE * yylloc_param , yyscan_t yyscanner); + +#define YY_DECL int yylex \ + (YYSTYPE * yylval_param, YYLTYPE * yylloc_param , yyscan_t yyscanner) +#endif /* !YY_DECL */ + +/* yy_get_previous_state - get the state just before the EOB char was reached */ + +#undef YY_NEW_FILE +#undef YY_FLUSH_BUFFER +#undef yy_set_bol +#undef yy_new_buffer +#undef yy_set_interactive +#undef YY_DO_BEFORE_ACTION + +#ifdef YY_DECL_IS_OURS +#undef YY_DECL_IS_OURS +#undef YY_DECL +#endif + +#ifndef igraph_gml_yy_create_buffer_ALREADY_DEFINED +#undef yy_create_buffer +#endif +#ifndef igraph_gml_yy_delete_buffer_ALREADY_DEFINED +#undef yy_delete_buffer +#endif +#ifndef igraph_gml_yy_scan_buffer_ALREADY_DEFINED +#undef yy_scan_buffer +#endif +#ifndef igraph_gml_yy_scan_string_ALREADY_DEFINED +#undef yy_scan_string +#endif +#ifndef igraph_gml_yy_scan_bytes_ALREADY_DEFINED +#undef yy_scan_bytes +#endif +#ifndef igraph_gml_yy_init_buffer_ALREADY_DEFINED +#undef yy_init_buffer +#endif +#ifndef igraph_gml_yy_flush_buffer_ALREADY_DEFINED +#undef yy_flush_buffer +#endif +#ifndef igraph_gml_yy_load_buffer_state_ALREADY_DEFINED +#undef yy_load_buffer_state +#endif +#ifndef igraph_gml_yy_switch_to_buffer_ALREADY_DEFINED +#undef yy_switch_to_buffer +#endif +#ifndef igraph_gml_yypush_buffer_state_ALREADY_DEFINED +#undef yypush_buffer_state +#endif +#ifndef igraph_gml_yypop_buffer_state_ALREADY_DEFINED +#undef yypop_buffer_state +#endif +#ifndef igraph_gml_yyensure_buffer_stack_ALREADY_DEFINED +#undef yyensure_buffer_stack +#endif +#ifndef igraph_gml_yylex_ALREADY_DEFINED +#undef yylex +#endif +#ifndef igraph_gml_yyrestart_ALREADY_DEFINED +#undef yyrestart +#endif +#ifndef igraph_gml_yylex_init_ALREADY_DEFINED +#undef yylex_init +#endif +#ifndef igraph_gml_yylex_init_extra_ALREADY_DEFINED +#undef yylex_init_extra +#endif +#ifndef igraph_gml_yylex_destroy_ALREADY_DEFINED +#undef yylex_destroy +#endif +#ifndef igraph_gml_yyget_debug_ALREADY_DEFINED +#undef yyget_debug +#endif +#ifndef igraph_gml_yyset_debug_ALREADY_DEFINED +#undef yyset_debug +#endif +#ifndef igraph_gml_yyget_extra_ALREADY_DEFINED +#undef yyget_extra +#endif +#ifndef igraph_gml_yyset_extra_ALREADY_DEFINED +#undef yyset_extra +#endif +#ifndef igraph_gml_yyget_in_ALREADY_DEFINED +#undef yyget_in +#endif +#ifndef igraph_gml_yyset_in_ALREADY_DEFINED +#undef yyset_in +#endif +#ifndef igraph_gml_yyget_out_ALREADY_DEFINED +#undef yyget_out +#endif +#ifndef igraph_gml_yyset_out_ALREADY_DEFINED +#undef yyset_out +#endif +#ifndef igraph_gml_yyget_leng_ALREADY_DEFINED +#undef yyget_leng +#endif +#ifndef igraph_gml_yyget_text_ALREADY_DEFINED +#undef yyget_text +#endif +#ifndef igraph_gml_yyget_lineno_ALREADY_DEFINED +#undef yyget_lineno +#endif +#ifndef igraph_gml_yyset_lineno_ALREADY_DEFINED +#undef yyset_lineno +#endif +#ifndef igraph_gml_yyget_column_ALREADY_DEFINED +#undef yyget_column +#endif +#ifndef igraph_gml_yyset_column_ALREADY_DEFINED +#undef yyset_column +#endif +#ifndef igraph_gml_yywrap_ALREADY_DEFINED +#undef yywrap +#endif +#ifndef igraph_gml_yyget_lval_ALREADY_DEFINED +#undef yyget_lval +#endif +#ifndef igraph_gml_yyset_lval_ALREADY_DEFINED +#undef yyset_lval +#endif +#ifndef igraph_gml_yyget_lloc_ALREADY_DEFINED +#undef yyget_lloc +#endif +#ifndef igraph_gml_yyset_lloc_ALREADY_DEFINED +#undef yyset_lloc +#endif +#ifndef igraph_gml_yyalloc_ALREADY_DEFINED +#undef yyalloc +#endif +#ifndef igraph_gml_yyrealloc_ALREADY_DEFINED +#undef yyrealloc +#endif +#ifndef igraph_gml_yyfree_ALREADY_DEFINED +#undef yyfree +#endif +#ifndef igraph_gml_yytext_ALREADY_DEFINED +#undef yytext +#endif +#ifndef igraph_gml_yyleng_ALREADY_DEFINED +#undef yyleng +#endif +#ifndef igraph_gml_yyin_ALREADY_DEFINED +#undef yyin +#endif +#ifndef igraph_gml_yyout_ALREADY_DEFINED +#undef yyout +#endif +#ifndef igraph_gml_yy_flex_debug_ALREADY_DEFINED +#undef yy_flex_debug +#endif +#ifndef igraph_gml_yylineno_ALREADY_DEFINED +#undef yylineno +#endif +#ifndef igraph_gml_yytables_fload_ALREADY_DEFINED +#undef yytables_fload +#endif +#ifndef igraph_gml_yytables_destroy_ALREADY_DEFINED +#undef yytables_destroy +#endif +#ifndef igraph_gml_yyTABLES_NAME_ALREADY_DEFINED +#undef yyTABLES_NAME +#endif + +#line 112 "src/core/io/gml-lexer.l" + + +#line 735 "src/core/io/gml-lexer.h" +#undef igraph_gml_yyIN_HEADER +#endif /* igraph_gml_yyHEADER_H */ diff --git a/src/rigraph/core/io/gml-lexer.l b/src/rigraph/core/io/gml-lexer.l new file mode 100644 index 0000000..0acd8fa --- /dev/null +++ b/src/rigraph/core/io/gml-lexer.l @@ -0,0 +1,112 @@ +/* + IGraph library. + Copyright (C) 2007-2012 Gabor Csardi + 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 + +*/ + +%{ + +/* + IGraph library. + Copyright (C) 2007-2012 Gabor Csardi + 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 + +#include "io/gml-header.h" +#include "io/parsers/gml-parser.h" + +#define YY_EXTRA_TYPE igraph_i_gml_parsedata_t* +#define YY_USER_ACTION yylloc->first_line = yylineno; +#define YY_FATAL_ERROR(msg) IGRAPH_FATAL("Error in GML parser: " # msg) +#ifdef USING_R +#define fprintf(file, msg, ...) (1) +#ifdef stdout +# undef stdout +#endif +#define stdout 0 +#endif +%} + +%option noyywrap +%option prefix="igraph_gml_yy" +%option nounput +%option noinput +%option nodefault +%option reentrant +%option bison-bridge +%option bison-locations + +digit [0-9] +whitespace [ \r\n\t] + +%% + +^#[^\n\r]*[\n]|[\r] { /* comments ignored */ } + +\"[^\x00\"]*\" { return STRING; } +\-?{digit}+(\.{digit}+)?([eE](\+|\-)?{digit}+)? { return NUM; } +[a-zA-Z_][a-zA-Z_0-9]* { return KEYWORD; } +\[ { + yyextra->depth++; + if (yyextra->depth >= 32) { + return ERROR; + } else { + return LISTOPEN; + } + } +\] { + yyextra->depth--; + if (yyextra->depth < 0) { + return ERROR; + } else { + return LISTCLOSE; + } + } +\n\r|\r\n|\r|\n { } +{whitespace} { /* other whitespace ignored */ } + +<> { + if (yyextra->eof) { + yyterminate(); + } else { + yyextra->eof=1; + return EOFF; + } + } +. { return ERROR; } +%% diff --git a/src/rigraph/core/io/gml-parser.c b/src/rigraph/core/io/gml-parser.c new file mode 100644 index 0000000..3d0b526 --- /dev/null +++ b/src/rigraph/core/io/gml-parser.c @@ -0,0 +1,1987 @@ +/* A Bison parser, made by GNU Bison 3.5.1. */ + +/* Bison implementation for Yacc-like parsers in C + + Copyright (C) 1984, 1989-1990, 2000-2015, 2018-2020 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 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 . */ + +/* 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. */ + +/* Undocumented macros, especially those whose name start with YY_, + are private implementation details. Do not rely on them. */ + +/* Identify Bison output. */ +#define YYBISON 1 + +/* Bison version. */ +#define YYBISON_VERSION "3.5.1" + +/* Skeleton name. */ +#define YYSKELETON_NAME "yacc.c" + +/* Pure parsers. */ +#define YYPURE 1 + +/* Push parsers. */ +#define YYPUSH 0 + +/* Pull parsers. */ +#define YYPULL 1 + + +/* Substitute the variable and function names. */ +#define yyparse igraph_gml_yyparse +#define yylex igraph_gml_yylex +#define yyerror igraph_gml_yyerror +#define yydebug igraph_gml_yydebug +#define yynerrs igraph_gml_yynerrs + +/* First part of user prologue. */ +#line 23 "src/core/io/gml-parser.y" + + +/* + IGraph library. + Copyright (C) 2009-2012 Gabor Csardi + 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 +#include +#include + +#include "igraph_error.h" +#include "igraph_memory.h" +#include "config.h" + +#include "core/math.h" +#include "io/gml-header.h" +#include "io/gml-tree.h" +#include "io/gml-parser.h" +#include "io/gml-lexer.h" +#include "internal/hacks.h" /* strcasecmp */ + +int igraph_gml_yyerror(YYLTYPE* locp, igraph_i_gml_parsedata_t *context, + const char *s); +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)*/ + + +#line 133 "yy.tab.c" + +# ifndef YY_CAST +# ifdef __cplusplus +# define YY_CAST(Type, Val) static_cast (Val) +# define YY_REINTERPRET_CAST(Type, Val) reinterpret_cast (Val) +# else +# define YY_CAST(Type, Val) ((Type) (Val)) +# define YY_REINTERPRET_CAST(Type, Val) ((Type) (Val)) +# endif +# endif +# ifndef YY_NULLPTR +# if defined __cplusplus +# if 201103L <= __cplusplus +# define YY_NULLPTR nullptr +# else +# define YY_NULLPTR 0 +# endif +# else +# define YY_NULLPTR ((void*)0) +# endif +# endif + +/* Enabling verbose error messages. */ +#ifdef YYERROR_VERBOSE +# undef YYERROR_VERBOSE +# define YYERROR_VERBOSE 1 +#else +# define YYERROR_VERBOSE 1 +#endif + +/* Use api.header.include to #include this header + instead of duplicating it here. */ +#ifndef YY_IGRAPH_GML_YY_YY_TAB_H_INCLUDED +# define YY_IGRAPH_GML_YY_YY_TAB_H_INCLUDED +/* Debug traces. */ +#ifndef YYDEBUG +# define YYDEBUG 0 +#endif +#if YYDEBUG +extern int igraph_gml_yydebug; +#endif + +/* Token type. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + enum yytokentype + { + STRING = 258, + NUM = 259, + KEYWORD = 260, + LISTOPEN = 261, + LISTCLOSE = 262, + EOFF = 263, + ERROR = 264 + }; +#endif + +/* Value type. */ +#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED +union YYSTYPE +{ +#line 91 "src/core/io/gml-parser.y" + + struct { + char *s; + int len; + } str; + void *tree; + double real; + +#line 204 "yy.tab.c" + +}; +typedef union YYSTYPE YYSTYPE; +# define YYSTYPE_IS_TRIVIAL 1 +# define YYSTYPE_IS_DECLARED 1 +#endif + +/* Location type. */ +#if ! defined YYLTYPE && ! defined YYLTYPE_IS_DECLARED +typedef struct YYLTYPE YYLTYPE; +struct YYLTYPE +{ + int first_line; + int first_column; + int last_line; + int last_column; +}; +# define YYLTYPE_IS_DECLARED 1 +# define YYLTYPE_IS_TRIVIAL 1 +#endif + + + +int igraph_gml_yyparse (igraph_i_gml_parsedata_t* context); + +#endif /* !YY_IGRAPH_GML_YY_YY_TAB_H_INCLUDED */ + + + +#ifdef short +# undef short +#endif + +/* On compilers that do not define __PTRDIFF_MAX__ etc., make sure + and (if available) are included + so that the code can choose integer types of a good width. */ + +#ifndef __PTRDIFF_MAX__ +# include /* INFRINGES ON USER NAME SPACE */ +# if defined __STDC_VERSION__ && 199901 <= __STDC_VERSION__ +# include /* INFRINGES ON USER NAME SPACE */ +# define YY_STDINT_H +# endif +#endif + +/* Narrow types that promote to a signed type and that can represent a + signed or unsigned integer of at least N bits. In tables they can + save space and decrease cache pressure. Promoting to a signed type + helps avoid bugs in integer arithmetic. */ + +#ifdef __INT_LEAST8_MAX__ +typedef __INT_LEAST8_TYPE__ yytype_int8; +#elif defined YY_STDINT_H +typedef int_least8_t yytype_int8; +#else +typedef signed char yytype_int8; +#endif + +#ifdef __INT_LEAST16_MAX__ +typedef __INT_LEAST16_TYPE__ yytype_int16; +#elif defined YY_STDINT_H +typedef int_least16_t yytype_int16; +#else +typedef short yytype_int16; +#endif + +#if defined __UINT_LEAST8_MAX__ && __UINT_LEAST8_MAX__ <= __INT_MAX__ +typedef __UINT_LEAST8_TYPE__ yytype_uint8; +#elif (!defined __UINT_LEAST8_MAX__ && defined YY_STDINT_H \ + && UINT_LEAST8_MAX <= INT_MAX) +typedef uint_least8_t yytype_uint8; +#elif !defined __UINT_LEAST8_MAX__ && UCHAR_MAX <= INT_MAX +typedef unsigned char yytype_uint8; +#else +typedef short yytype_uint8; +#endif + +#if defined __UINT_LEAST16_MAX__ && __UINT_LEAST16_MAX__ <= __INT_MAX__ +typedef __UINT_LEAST16_TYPE__ yytype_uint16; +#elif (!defined __UINT_LEAST16_MAX__ && defined YY_STDINT_H \ + && UINT_LEAST16_MAX <= INT_MAX) +typedef uint_least16_t yytype_uint16; +#elif !defined __UINT_LEAST16_MAX__ && USHRT_MAX <= INT_MAX +typedef unsigned short yytype_uint16; +#else +typedef int yytype_uint16; +#endif + +#ifndef YYPTRDIFF_T +# if defined __PTRDIFF_TYPE__ && defined __PTRDIFF_MAX__ +# define YYPTRDIFF_T __PTRDIFF_TYPE__ +# define YYPTRDIFF_MAXIMUM __PTRDIFF_MAX__ +# elif defined PTRDIFF_MAX +# ifndef ptrdiff_t +# include /* INFRINGES ON USER NAME SPACE */ +# endif +# define YYPTRDIFF_T ptrdiff_t +# define YYPTRDIFF_MAXIMUM PTRDIFF_MAX +# else +# define YYPTRDIFF_T long +# define YYPTRDIFF_MAXIMUM LONG_MAX +# endif +#endif + +#ifndef YYSIZE_T +# ifdef __SIZE_TYPE__ +# define YYSIZE_T __SIZE_TYPE__ +# elif defined size_t +# define YYSIZE_T size_t +# elif defined __STDC_VERSION__ && 199901 <= __STDC_VERSION__ +# include /* INFRINGES ON USER NAME SPACE */ +# define YYSIZE_T size_t +# else +# define YYSIZE_T unsigned +# endif +#endif + +#define YYSIZE_MAXIMUM \ + YY_CAST (YYPTRDIFF_T, \ + (YYPTRDIFF_MAXIMUM < YY_CAST (YYSIZE_T, -1) \ + ? YYPTRDIFF_MAXIMUM \ + : YY_CAST (YYSIZE_T, -1))) + +#define YYSIZEOF(X) YY_CAST (YYPTRDIFF_T, sizeof (X)) + +/* Stored state numbers (used for stacks). */ +typedef yytype_int8 yy_state_t; + +/* State numbers in computations. */ +typedef int yy_state_fast_t; + +#ifndef YY_ +# if defined YYENABLE_NLS && YYENABLE_NLS +# if ENABLE_NLS +# include /* INFRINGES ON USER NAME SPACE */ +# define YY_(Msgid) dgettext ("bison-runtime", Msgid) +# endif +# endif +# ifndef YY_ +# define YY_(Msgid) Msgid +# endif +#endif + +#ifndef YY_ATTRIBUTE_PURE +# if defined __GNUC__ && 2 < __GNUC__ + (96 <= __GNUC_MINOR__) +# define YY_ATTRIBUTE_PURE __attribute__ ((__pure__)) +# else +# define YY_ATTRIBUTE_PURE +# endif +#endif + +#ifndef YY_ATTRIBUTE_UNUSED +# if defined __GNUC__ && 2 < __GNUC__ + (7 <= __GNUC_MINOR__) +# define YY_ATTRIBUTE_UNUSED __attribute__ ((__unused__)) +# else +# define YY_ATTRIBUTE_UNUSED +# 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 + +#if defined __GNUC__ && ! defined __ICC && 407 <= __GNUC__ * 100 + __GNUC_MINOR__ +/* Suppress an incorrect diagnostic about yylval being uninitialized. */ +# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN \ + _Pragma ("GCC diagnostic push") \ + _Pragma ("GCC diagnostic ignored \"-Wuninitialized\"") \ + _Pragma ("GCC diagnostic ignored \"-Wmaybe-uninitialized\"") +# define YY_IGNORE_MAYBE_UNINITIALIZED_END \ + _Pragma ("GCC diagnostic pop") +#else +# define YY_INITIAL_VALUE(Value) Value +#endif +#ifndef YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN +# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN +# define YY_IGNORE_MAYBE_UNINITIALIZED_END +#endif +#ifndef YY_INITIAL_VALUE +# define YY_INITIAL_VALUE(Value) /* Nothing. */ +#endif + +#if defined __cplusplus && defined __GNUC__ && ! defined __ICC && 6 <= __GNUC__ +# define YY_IGNORE_USELESS_CAST_BEGIN \ + _Pragma ("GCC diagnostic push") \ + _Pragma ("GCC diagnostic ignored \"-Wuseless-cast\"") +# define YY_IGNORE_USELESS_CAST_END \ + _Pragma ("GCC diagnostic pop") +#endif +#ifndef YY_IGNORE_USELESS_CAST_BEGIN +# define YY_IGNORE_USELESS_CAST_BEGIN +# define YY_IGNORE_USELESS_CAST_END +#endif + + +#define YY_ASSERT(E) ((void) (0 && (E))) + +#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 /* INFRINGES ON USER NAME SPACE */ +# elif defined _AIX +# define YYSTACK_ALLOC __alloca +# elif defined _MSC_VER +# include /* INFRINGES ON USER NAME SPACE */ +# define alloca _alloca +# else +# define YYSTACK_ALLOC alloca +# if ! defined _ALLOCA_H && ! defined EXIT_SUCCESS +# include /* INFRINGES ON USER NAME SPACE */ + /* Use EXIT_SUCCESS as a witness for stdlib.h. */ +# ifndef EXIT_SUCCESS +# define EXIT_SUCCESS 0 +# endif +# endif +# endif +# endif +# endif + +# ifdef YYSTACK_ALLOC + /* Pacify GCC's 'empty if-body' warning. */ +# define YYSTACK_FREE(Ptr) do { /* empty */; } while (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 EXIT_SUCCESS \ + && ! ((defined YYMALLOC || defined malloc) \ + && (defined YYFREE || defined free))) +# include /* INFRINGES ON USER NAME SPACE */ +# ifndef EXIT_SUCCESS +# define EXIT_SUCCESS 0 +# endif +# endif +# ifndef YYMALLOC +# define YYMALLOC malloc +# if ! defined malloc && ! defined EXIT_SUCCESS +void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# ifndef YYFREE +# define YYFREE free +# if ! defined free && ! defined EXIT_SUCCESS +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 +{ + yy_state_t yyss_alloc; + YYSTYPE yyvs_alloc; + YYLTYPE yyls_alloc; +}; + +/* The size of the maximum gap between one aligned stack and the next. */ +# define YYSTACK_GAP_MAXIMUM (YYSIZEOF (union yyalloc) - 1) + +/* The size of an array large to enough to hold all stacks, each with + N elements. */ +# define YYSTACK_BYTES(N) \ + ((N) * (YYSIZEOF (yy_state_t) + YYSIZEOF (YYSTYPE) \ + + YYSIZEOF (YYLTYPE)) \ + + 2 * YYSTACK_GAP_MAXIMUM) + +# define YYCOPY_NEEDED 1 + +/* 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_alloc, Stack) \ + do \ + { \ + YYPTRDIFF_T yynewbytes; \ + YYCOPY (&yyptr->Stack_alloc, Stack, yysize); \ + Stack = &yyptr->Stack_alloc; \ + yynewbytes = yystacksize * YYSIZEOF (*Stack) + YYSTACK_GAP_MAXIMUM; \ + yyptr += yynewbytes / YYSIZEOF (*yyptr); \ + } \ + while (0) + +#endif + +#if defined YYCOPY_NEEDED && YYCOPY_NEEDED +/* Copy COUNT objects from SRC to DST. The source and destination do + not overlap. */ +# ifndef YYCOPY +# if defined __GNUC__ && 1 < __GNUC__ +# define YYCOPY(Dst, Src, Count) \ + __builtin_memcpy (Dst, Src, YY_CAST (YYSIZE_T, (Count)) * sizeof (*(Src))) +# else +# define YYCOPY(Dst, Src, Count) \ + do \ + { \ + YYPTRDIFF_T yyi; \ + for (yyi = 0; yyi < (Count); yyi++) \ + (Dst)[yyi] = (Src)[yyi]; \ + } \ + while (0) +# endif +# endif +#endif /* !YYCOPY_NEEDED */ + +/* 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 +/* YYNSTATES -- Number of states. */ +#define YYNSTATES 17 + +#define YYUNDEFTOK 2 +#define YYMAXUTOK 264 + + +/* YYTRANSLATE(TOKEN-NUM) -- Symbol number corresponding to TOKEN-NUM + as returned by yylex, with out-of-bounds checking. */ +#define YYTRANSLATE(YYX) \ + (0 <= (YYX) && (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK) + +/* YYTRANSLATE[TOKEN-NUM] -- Symbol number corresponding to TOKEN-NUM + as returned by yylex. */ +static const yytype_int8 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 + /* YYRLINE[YYN] -- Source line where rule number YYN was defined. */ +static const yytype_uint8 yyrline[] = +{ + 0, 119, 119, 120, 123, 124, 126, 128, 130, 132, + 136, 139, 142 +}; +#endif + +#if YYDEBUG || YYERROR_VERBOSE || 1 +/* 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", YY_NULLPTR +}; +#endif + +# ifdef YYPRINT +/* YYTOKNUM[NUM] -- (External) token number corresponding to the + (internal) symbol number NUM (which must be that of a token). */ +static const yytype_int16 yytoknum[] = +{ + 0, 256, 257, 258, 259, 260, 261, 262, 263, 264 +}; +# endif + +#define YYPACT_NINF (-4) + +#define yypact_value_is_default(Yyn) \ + ((Yyn) == YYPACT_NINF) + +#define YYTABLE_NINF (-1) + +#define yytable_value_is_error(Yyn) \ + 0 + + /* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing + STATE-NUM. */ +static const yytype_int8 yypact[] = +{ + 1, -4, 10, 0, -4, -2, -4, -4, -4, -4, + -4, 1, -4, -4, -4, 2, -4 +}; + + /* YYDEFACT[STATE-NUM] -- Default reduction number in state STATE-NUM. + Performed when YYTABLE does not specify something else to do. Zero + means the default is an error. */ +static const yytype_int8 yydefact[] = +{ + 0, 10, 0, 2, 4, 0, 1, 3, 5, 12, + 11, 0, 9, 6, 7, 0, 8 +}; + + /* YYPGOTO[NTERM-NUM]. */ +static const yytype_int8 yypgoto[] = +{ + -4, -4, 3, -3, 6, -4, -4 +}; + + /* YYDEFGOTO[NTERM-NUM]. */ +static const yytype_int8 yydefgoto[] = +{ + -1, 2, 3, 4, 5, 13, 14 +}; + + /* YYTABLE[YYPACT[STATE-NUM]] -- What to do in state STATE-NUM. If + positive, shift that token. If negative, reduce the rule whose + number is the opposite. If YYTABLE_NINF, syntax error. */ +static const yytype_int8 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_int8 yystos[] = +{ + 0, 5, 11, 12, 13, 14, 0, 8, 13, 3, + 4, 6, 14, 15, 16, 12, 7 +}; + + /* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ +static const yytype_int8 yyr1[] = +{ + 0, 10, 11, 11, 12, 12, 13, 13, 13, 13, + 14, 15, 16 +}; + + /* YYR2[YYN] -- Number of symbols on the right hand side of rule YYN. */ +static const yytype_int8 yyr2[] = +{ + 0, 2, 1, 2, 1, 2, 2, 2, 4, 2, + 1, 1, 1 +}; + + +#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 + + +#define YYRECOVERING() (!!yyerrstatus) + +#define YYBACKUP(Token, Value) \ + do \ + if (yychar == YYEMPTY) \ + { \ + yychar = (Token); \ + yylval = (Value); \ + YYPOPSTACK (yylen); \ + yystate = *yyssp; \ + goto yybackup; \ + } \ + else \ + { \ + yyerror (&yylloc, context, YY_("syntax error: cannot back up")); \ + YYERROR; \ + } \ + while (0) + +/* Error token number */ +#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). */ + +#ifndef YYLLOC_DEFAULT +# define YYLLOC_DEFAULT(Current, Rhs, N) \ + do \ + if (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 (0) +#endif + +#define YYRHSLOC(Rhs, K) ((Rhs)[K]) + + +/* Enable debugging if requested. */ +#if YYDEBUG + +# ifndef YYFPRINTF +# include /* INFRINGES ON USER NAME SPACE */ +# define YYFPRINTF fprintf +# endif + +# define YYDPRINTF(Args) \ +do { \ + if (yydebug) \ + YYFPRINTF Args; \ +} while (0) + + +/* 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 + +/* Print *YYLOCP on YYO. Private, do not rely on its existence. */ + +YY_ATTRIBUTE_UNUSED +static int +yy_location_print_ (FILE *yyo, YYLTYPE const * const yylocp) +{ + int res = 0; + int end_col = 0 != yylocp->last_column ? yylocp->last_column - 1 : 0; + if (0 <= yylocp->first_line) + { + res += YYFPRINTF (yyo, "%d", yylocp->first_line); + if (0 <= yylocp->first_column) + res += YYFPRINTF (yyo, ".%d", yylocp->first_column); + } + if (0 <= yylocp->last_line) + { + if (yylocp->first_line < yylocp->last_line) + { + res += YYFPRINTF (yyo, "-%d", yylocp->last_line); + if (0 <= end_col) + res += YYFPRINTF (yyo, ".%d", end_col); + } + else if (0 <= end_col && yylocp->first_column < end_col) + res += YYFPRINTF (yyo, "-%d", end_col); + } + return res; + } + +# define YY_LOCATION_PRINT(File, Loc) \ + yy_location_print_ (File, &(Loc)) + +# else +# define YY_LOCATION_PRINT(File, Loc) ((void) 0) +# endif +#endif + + +# 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 (0) + + +/*-----------------------------------. +| Print this symbol's value on YYO. | +`-----------------------------------*/ + +static void +yy_symbol_value_print (FILE *yyo, int yytype, YYSTYPE const * const yyvaluep, YYLTYPE const * const yylocationp, igraph_i_gml_parsedata_t* context) +{ + FILE *yyoutput = yyo; + YYUSE (yyoutput); + YYUSE (yylocationp); + YYUSE (context); + if (!yyvaluep) + return; +# ifdef YYPRINT + if (yytype < YYNTOKENS) + YYPRINT (yyo, yytoknum[yytype], *yyvaluep); +# endif + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + YYUSE (yytype); + YY_IGNORE_MAYBE_UNINITIALIZED_END +} + + +/*---------------------------. +| Print this symbol on YYO. | +`---------------------------*/ + +static void +yy_symbol_print (FILE *yyo, int yytype, YYSTYPE const * const yyvaluep, YYLTYPE const * const yylocationp, igraph_i_gml_parsedata_t* context) +{ + YYFPRINTF (yyo, "%s %s (", + yytype < YYNTOKENS ? "token" : "nterm", yytname[yytype]); + + YY_LOCATION_PRINT (yyo, *yylocationp); + YYFPRINTF (yyo, ": "); + yy_symbol_value_print (yyo, yytype, yyvaluep, yylocationp, context); + YYFPRINTF (yyo, ")"); +} + +/*------------------------------------------------------------------. +| yy_stack_print -- Print the state stack from its BOTTOM up to its | +| TOP (included). | +`------------------------------------------------------------------*/ + +static void +yy_stack_print (yy_state_t *yybottom, yy_state_t *yytop) +{ + YYFPRINTF (stderr, "Stack now"); + for (; yybottom <= yytop; yybottom++) + { + int yybot = *yybottom; + YYFPRINTF (stderr, " %d", yybot); + } + YYFPRINTF (stderr, "\n"); +} + +# define YY_STACK_PRINT(Bottom, Top) \ +do { \ + if (yydebug) \ + yy_stack_print ((Bottom), (Top)); \ +} while (0) + + +/*------------------------------------------------. +| Report that the YYRULE is going to be reduced. | +`------------------------------------------------*/ + +static void +yy_reduce_print (yy_state_t *yyssp, YYSTYPE *yyvsp, YYLTYPE *yylsp, int yyrule, igraph_i_gml_parsedata_t* context) +{ + int yylno = yyrline[yyrule]; + int yynrhs = yyr2[yyrule]; + int yyi; + YYFPRINTF (stderr, "Reducing stack by rule %d (line %d):\n", + yyrule - 1, yylno); + /* The symbols being reduced. */ + for (yyi = 0; yyi < yynrhs; yyi++) + { + YYFPRINTF (stderr, " $%d = ", yyi + 1); + yy_symbol_print (stderr, + yystos[+yyssp[yyi + 1 - yynrhs]], + &yyvsp[(yyi + 1) - (yynrhs)] + , &(yylsp[(yyi + 1) - (yynrhs)]) , context); + YYFPRINTF (stderr, "\n"); + } +} + +# define YY_REDUCE_PRINT(Rule) \ +do { \ + if (yydebug) \ + yy_reduce_print (yyssp, yyvsp, yylsp, Rule, context); \ +} while (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(S) (YY_CAST (YYPTRDIFF_T, strlen (S))) +# else +/* Return the length of YYSTR. */ +static YYPTRDIFF_T +yystrlen (const char *yystr) +{ + YYPTRDIFF_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. */ +static char * +yystpcpy (char *yydest, const char *yysrc) +{ + 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 YYPTRDIFF_T +yytnamerr (char *yyres, const char *yystr) +{ + if (*yystr == '"') + { + YYPTRDIFF_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; + else + goto append; + + append: + default: + if (yyres) + yyres[yyn] = *yyp; + yyn++; + break; + + case '"': + if (yyres) + yyres[yyn] = '\0'; + return yyn; + } + do_not_strip_quotes: ; + } + + if (yyres) + return yystpcpy (yyres, yystr) - yyres; + else + return yystrlen (yystr); +} +# endif + +/* Copy into *YYMSG, which is of size *YYMSG_ALLOC, an error message + about the unexpected token YYTOKEN for the state stack whose top is + YYSSP. + + Return 0 if *YYMSG was successfully written. Return 1 if *YYMSG is + not large enough to hold the message. In that case, also set + *YYMSG_ALLOC to the required number of bytes. Return 2 if the + required number of bytes is too large to store. */ +static int +yysyntax_error (YYPTRDIFF_T *yymsg_alloc, char **yymsg, + yy_state_t *yyssp, int yytoken) +{ + enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 }; + /* Internationalized format string. */ + const char *yyformat = YY_NULLPTR; + /* Arguments of yyformat: reported tokens (one for the "unexpected", + one per "expected"). */ + char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM]; + /* Actual size of YYARG. */ + int yycount = 0; + /* Cumulated lengths of YYARG. */ + YYPTRDIFF_T yysize = 0; + + /* There are many possibilities here to consider: + - If this state is a consistent state with a default action, then + the only way this function was invoked is if the default action + is an error action. In that case, don't check for expected + tokens because there are none. + - The only way there can be no lookahead present (in yychar) is if + this state is a consistent state with a default action. Thus, + detecting the absence of a lookahead is sufficient to determine + that there is no unexpected or expected token to report. In that + case, just report a simple "syntax error". + - Don't assume there isn't a lookahead just because this state is a + consistent state with a default action. There might have been a + previous inconsistent state, consistent state with a non-default + action, or user semantic action that manipulated yychar. + - Of course, the expected token list depends on states to have + correct lookahead information, and it depends on the parser not + to perform extra reductions after fetching a lookahead from the + scanner and before detecting a syntax error. Thus, state merging + (from LALR or IELR) and default reductions corrupt the expected + token list. However, the list is correct for canonical LR with + one exception: it will still contain any token that will not be + accepted due to an error action in a later state. + */ + if (yytoken != YYEMPTY) + { + int yyn = yypact[+*yyssp]; + YYPTRDIFF_T yysize0 = yytnamerr (YY_NULLPTR, yytname[yytoken]); + yysize = yysize0; + yyarg[yycount++] = yytname[yytoken]; + if (!yypact_value_is_default (yyn)) + { + /* Start YYX at -YYN if negative to avoid negative indexes in + YYCHECK. In other words, skip the first -YYN actions for + this state because they are default actions. */ + 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 yyx; + + for (yyx = yyxbegin; yyx < yyxend; ++yyx) + if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR + && !yytable_value_is_error (yytable[yyx + yyn])) + { + if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM) + { + yycount = 1; + yysize = yysize0; + break; + } + yyarg[yycount++] = yytname[yyx]; + { + YYPTRDIFF_T yysize1 + = yysize + yytnamerr (YY_NULLPTR, yytname[yyx]); + if (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM) + yysize = yysize1; + else + return 2; + } + } + } + } + + switch (yycount) + { +# define YYCASE_(N, S) \ + case N: \ + yyformat = S; \ + break + default: /* Avoid compiler warnings. */ + YYCASE_(0, YY_("syntax error")); + YYCASE_(1, YY_("syntax error, unexpected %s")); + YYCASE_(2, YY_("syntax error, unexpected %s, expecting %s")); + YYCASE_(3, YY_("syntax error, unexpected %s, expecting %s or %s")); + YYCASE_(4, YY_("syntax error, unexpected %s, expecting %s or %s or %s")); + YYCASE_(5, YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s")); +# undef YYCASE_ + } + + { + /* Don't count the "%s"s in the final size, but reserve room for + the terminator. */ + YYPTRDIFF_T yysize1 = yysize + (yystrlen (yyformat) - 2 * yycount) + 1; + if (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM) + yysize = yysize1; + else + return 2; + } + + if (*yymsg_alloc < yysize) + { + *yymsg_alloc = 2 * yysize; + if (! (yysize <= *yymsg_alloc + && *yymsg_alloc <= YYSTACK_ALLOC_MAXIMUM)) + *yymsg_alloc = YYSTACK_ALLOC_MAXIMUM; + return 1; + } + + /* 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 = *yymsg; + int yyi = 0; + while ((*yyp = *yyformat) != '\0') + if (*yyp == '%' && yyformat[1] == 's' && yyi < yycount) + { + yyp += yytnamerr (yyp, yyarg[yyi++]); + yyformat += 2; + } + else + { + ++yyp; + ++yyformat; + } + } + return 0; +} +#endif /* YYERROR_VERBOSE */ + +/*-----------------------------------------------. +| Release the memory associated to this symbol. | +`-----------------------------------------------*/ + +static void +yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep, YYLTYPE *yylocationp, igraph_i_gml_parsedata_t* context) +{ + YYUSE (yyvaluep); + YYUSE (yylocationp); + YYUSE (context); + if (!yymsg) + yymsg = "Deleting"; + YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp); + + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + switch (yytype) + { + case 5: /* KEYWORD */ +#line 114 "src/core/io/gml-parser.y" + { IGRAPH_FREE(((*yyvaluep).str).s); } +#line 1203 "yy.tab.c" + break; + + case 12: /* list */ +#line 115 "src/core/io/gml-parser.y" + { igraph_gml_tree_destroy(((*yyvaluep).tree)); } +#line 1209 "yy.tab.c" + break; + + case 13: /* keyvalue */ +#line 115 "src/core/io/gml-parser.y" + { igraph_gml_tree_destroy(((*yyvaluep).tree)); } +#line 1215 "yy.tab.c" + break; + + case 14: /* key */ +#line 114 "src/core/io/gml-parser.y" + { IGRAPH_FREE(((*yyvaluep).str).s); } +#line 1221 "yy.tab.c" + break; + + case 16: /* string */ +#line 114 "src/core/io/gml-parser.y" + { IGRAPH_FREE(((*yyvaluep).str).s); } +#line 1227 "yy.tab.c" + break; + + default: + break; + } + YY_IGNORE_MAYBE_UNINITIALIZED_END +} + + + + +/*----------. +| yyparse. | +`----------*/ + +int +yyparse (igraph_i_gml_parsedata_t* context) +{ +/* The lookahead symbol. */ +int yychar; + + +/* The semantic value of the lookahead symbol. */ +/* Default value used for initialization, for pacifying older GCCs + or non-GCC compilers. */ +YY_INITIAL_VALUE (static YYSTYPE yyval_default;) +YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); + +/* Location data for the lookahead symbol. */ +static YYLTYPE yyloc_default +# if defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL + = { 1, 1, 1, 1 } +# endif +; +YYLTYPE yylloc = yyloc_default; + + /* Number of syntax errors so far. */ + int yynerrs; + + yy_state_fast_t yystate; + /* Number of tokens to shift before error messages enabled. */ + int yyerrstatus; + + /* The stacks and their tools: + 'yyss': related to states. + 'yyvs': related to semantic values. + 'yyls': related to locations. + + Refer to the stacks through separate pointers, to allow yyoverflow + to reallocate them elsewhere. */ + + /* The state stack. */ + yy_state_t yyssa[YYINITDEPTH]; + yy_state_t *yyss; + yy_state_t *yyssp; + + /* The semantic value stack. */ + YYSTYPE yyvsa[YYINITDEPTH]; + YYSTYPE *yyvs; + YYSTYPE *yyvsp; + + /* The location stack. */ + YYLTYPE yylsa[YYINITDEPTH]; + YYLTYPE *yyls; + YYLTYPE *yylsp; + + /* The locations where the error started and ended. */ + YYLTYPE yyerror_range[3]; + + YYPTRDIFF_T yystacksize; + + int yyn; + int yyresult; + /* Lookahead token as an internal (translated) token number. */ + int yytoken = 0; + /* The variables used to return semantic value and location from the + action routines. */ + YYSTYPE yyval; + YYLTYPE yyloc; + +#if YYERROR_VERBOSE + /* Buffer for error messages, and its allocated size. */ + char yymsgbuf[128]; + char *yymsg = yymsgbuf; + YYPTRDIFF_T yymsg_alloc = sizeof yymsgbuf; +#endif + +#define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N), yylsp -= (N)) + + /* The number of symbols on the RHS of the reduced rule. + Keep to zero when no symbol should be popped. */ + int yylen = 0; + + yyssp = yyss = yyssa; + yyvsp = yyvs = yyvsa; + yylsp = yyls = yylsa; + yystacksize = YYINITDEPTH; + + YYDPRINTF ((stderr, "Starting parse\n")); + + yystate = 0; + yyerrstatus = 0; + yynerrs = 0; + yychar = YYEMPTY; /* Cause a token to be read. */ + yylsp[0] = yylloc; + 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 -- set current state (the top of the stack) to yystate. | +`--------------------------------------------------------------------*/ +yysetstate: + YYDPRINTF ((stderr, "Entering state %d\n", yystate)); + YY_ASSERT (0 <= yystate && yystate < YYNSTATES); + YY_IGNORE_USELESS_CAST_BEGIN + *yyssp = YY_CAST (yy_state_t, yystate); + YY_IGNORE_USELESS_CAST_END + + if (yyss + yystacksize - 1 <= yyssp) +#if !defined yyoverflow && !defined YYSTACK_RELOCATE + goto yyexhaustedlab; +#else + { + /* Get the current used size of the three stacks, in elements. */ + YYPTRDIFF_T yysize = yyssp - yyss + 1; + +# if defined 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. */ + yy_state_t *yyss1 = yyss; + YYSTYPE *yyvs1 = yyvs; + 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 * YYSIZEOF (*yyssp), + &yyvs1, yysize * YYSIZEOF (*yyvsp), + &yyls1, yysize * YYSIZEOF (*yylsp), + &yystacksize); + yyss = yyss1; + yyvs = yyvs1; + yyls = yyls1; + } +# else /* defined YYSTACK_RELOCATE */ + /* Extend the stack our own way. */ + if (YYMAXDEPTH <= yystacksize) + goto yyexhaustedlab; + yystacksize *= 2; + if (YYMAXDEPTH < yystacksize) + yystacksize = YYMAXDEPTH; + + { + yy_state_t *yyss1 = yyss; + union yyalloc *yyptr = + YY_CAST (union yyalloc *, + YYSTACK_ALLOC (YY_CAST (YYSIZE_T, YYSTACK_BYTES (yystacksize)))); + if (! yyptr) + goto yyexhaustedlab; + YYSTACK_RELOCATE (yyss_alloc, yyss); + YYSTACK_RELOCATE (yyvs_alloc, yyvs); + YYSTACK_RELOCATE (yyls_alloc, yyls); +# undef YYSTACK_RELOCATE + if (yyss1 != yyssa) + YYSTACK_FREE (yyss1); + } +# endif + + yyssp = yyss + yysize - 1; + yyvsp = yyvs + yysize - 1; + yylsp = yyls + yysize - 1; + + YY_IGNORE_USELESS_CAST_BEGIN + YYDPRINTF ((stderr, "Stack size increased to %ld\n", + YY_CAST (long, yystacksize))); + YY_IGNORE_USELESS_CAST_END + + if (yyss + yystacksize - 1 <= yyssp) + YYABORT; + } +#endif /* !defined yyoverflow && !defined YYSTACK_RELOCATE */ + + if (yystate == YYFINAL) + YYACCEPT; + + goto yybackup; + + +/*-----------. +| yybackup. | +`-----------*/ +yybackup: + /* Do appropriate processing given the current state. Read a + lookahead token if we need one and don't already have one. */ + + /* First try to decide what to do without reference to lookahead token. */ + yyn = yypact[yystate]; + if (yypact_value_is_default (yyn)) + goto yydefault; + + /* Not known => get a lookahead token if don't already have one. */ + + /* YYCHAR is either YYEMPTY or YYEOF or a valid lookahead symbol. */ + if (yychar == YYEMPTY) + { + YYDPRINTF ((stderr, "Reading a token: ")); + yychar = yylex (&yylval, &yylloc, scanner); + } + + 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 (yytable_value_is_error (yyn)) + goto yyerrlab; + yyn = -yyn; + goto yyreduce; + } + + /* Count tokens shifted since error; after three, turn off error + status. */ + if (yyerrstatus) + yyerrstatus--; + + /* Shift the lookahead token. */ + YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc); + yystate = yyn; + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + *++yyvsp = yylval; + YY_IGNORE_MAYBE_UNINITIALIZED_END + *++yylsp = yylloc; + + /* Discard the shifted token. */ + yychar = YYEMPTY; + 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); + yyerror_range[1] = yyloc; + YY_REDUCE_PRINT (yyn); + switch (yyn) + { + case 2: +#line 119 "src/core/io/gml-parser.y" + { context->tree=(yyvsp[0].tree); } +#line 1529 "yy.tab.c" + break; + + case 3: +#line 120 "src/core/io/gml-parser.y" + { context->tree=(yyvsp[-1].tree); } +#line 1535 "yy.tab.c" + break; + + case 4: +#line 123 "src/core/io/gml-parser.y" + { (yyval.tree)=(yyvsp[0].tree); } +#line 1541 "yy.tab.c" + break; + + case 5: +#line 124 "src/core/io/gml-parser.y" + { (yyval.tree)=igraph_i_gml_merge((yyvsp[-1].tree), (yyvsp[0].tree)); } +#line 1547 "yy.tab.c" + break; + + case 6: +#line 127 "src/core/io/gml-parser.y" + { (yyval.tree)=igraph_i_gml_make_numeric((yyvsp[-1].str).s, (yyvsp[-1].str).len, (yyvsp[0].real)); } +#line 1553 "yy.tab.c" + break; + + case 7: +#line 129 "src/core/io/gml-parser.y" + { (yyval.tree)=igraph_i_gml_make_string((yyvsp[-1].str).s, (yyvsp[-1].str).len, (yyvsp[0].str).s, (yyvsp[0].str).len); } +#line 1559 "yy.tab.c" + break; + + case 8: +#line 131 "src/core/io/gml-parser.y" + { (yyval.tree)=igraph_i_gml_make_list((yyvsp[-3].str).s, (yyvsp[-3].str).len, (yyvsp[-1].tree)); } +#line 1565 "yy.tab.c" + break; + + case 9: +#line 133 "src/core/io/gml-parser.y" + { (yyval.tree)=igraph_i_gml_make_numeric2((yyvsp[-1].str).s, (yyvsp[-1].str).len, (yyvsp[0].str).s, (yyvsp[0].str).len); } +#line 1571 "yy.tab.c" + break; + + case 10: +#line 136 "src/core/io/gml-parser.y" + { igraph_i_gml_get_keyword(igraph_gml_yyget_text(scanner), + igraph_gml_yyget_leng(scanner), + &(yyval.str)); USE((yyvsp[0].str)); } +#line 1579 "yy.tab.c" + break; + + case 11: +#line 139 "src/core/io/gml-parser.y" + { (yyval.real)=igraph_i_gml_get_real(igraph_gml_yyget_text(scanner), + igraph_gml_yyget_leng(scanner)); } +#line 1586 "yy.tab.c" + break; + + case 12: +#line 142 "src/core/io/gml-parser.y" + { igraph_i_gml_get_string(igraph_gml_yyget_text(scanner), + igraph_gml_yyget_leng(scanner), + &(yyval.str)); } +#line 1594 "yy.tab.c" + break; + + +#line 1598 "yy.tab.c" + + default: break; + } + /* User semantic actions sometimes alter yychar, and that requires + that yytoken be updated with the new translation. We take the + approach of translating immediately before every use of yytoken. + One alternative is translating here after every semantic action, + but that translation would be missed if the semantic action invokes + YYABORT, YYACCEPT, or YYERROR immediately after altering yychar or + if it invokes YYBACKUP. In the case of YYABORT or YYACCEPT, an + incorrect destructor might then be invoked immediately. In the + case of YYERROR or YYBACKUP, subsequent parser actions might lead + to an incorrect destructor call or verbose syntax error message + before the lookahead is translated. */ + 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. */ + { + const int yylhs = yyr1[yyn] - YYNTOKENS; + const int yyi = yypgoto[yylhs] + *yyssp; + yystate = (0 <= yyi && yyi <= YYLAST && yycheck[yyi] == *yyssp + ? yytable[yyi] + : yydefgoto[yylhs]); + } + + goto yynewstate; + + +/*--------------------------------------. +| yyerrlab -- here on detecting error. | +`--------------------------------------*/ +yyerrlab: + /* Make sure we have latest lookahead translation. See comments at + user semantic actions for why this is necessary. */ + yytoken = yychar == YYEMPTY ? YYEMPTY : YYTRANSLATE (yychar); + + /* If not already recovering from an error, report this error. */ + if (!yyerrstatus) + { + ++yynerrs; +#if ! YYERROR_VERBOSE + yyerror (&yylloc, context, YY_("syntax error")); +#else +# define YYSYNTAX_ERROR yysyntax_error (&yymsg_alloc, &yymsg, \ + yyssp, yytoken) + { + char const *yymsgp = YY_("syntax error"); + int yysyntax_error_status; + yysyntax_error_status = YYSYNTAX_ERROR; + if (yysyntax_error_status == 0) + yymsgp = yymsg; + else if (yysyntax_error_status == 1) + { + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); + yymsg = YY_CAST (char *, YYSTACK_ALLOC (YY_CAST (YYSIZE_T, yymsg_alloc))); + if (!yymsg) + { + yymsg = yymsgbuf; + yymsg_alloc = sizeof yymsgbuf; + yysyntax_error_status = 2; + } + else + { + yysyntax_error_status = YYSYNTAX_ERROR; + yymsgp = yymsg; + } + } + yyerror (&yylloc, context, yymsgp); + if (yysyntax_error_status == 2) + goto yyexhaustedlab; + } +# undef YYSYNTAX_ERROR +#endif + } + + yyerror_range[1] = yylloc; + + if (yyerrstatus == 3) + { + /* If just tried and failed to reuse lookahead 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 lookahead token after shifting the error + token. */ + goto yyerrlab1; + + +/*---------------------------------------------------. +| yyerrorlab -- error raised explicitly by YYERROR. | +`---------------------------------------------------*/ +yyerrorlab: + /* Pacify compilers when the user code never invokes YYERROR and the + label yyerrorlab therefore never appears in user code. */ + if (0) + YYERROR; + + /* Do not reclaim the symbols of the rule whose 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 (!yypact_value_is_default (yyn)) + { + 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[1] = *yylsp; + yydestruct ("Error: popping", + yystos[yystate], yyvsp, yylsp, context); + YYPOPSTACK (1); + yystate = *yyssp; + YY_STACK_PRINT (yyss, yyssp); + } + + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + *++yyvsp = yylval; + YY_IGNORE_MAYBE_UNINITIALIZED_END + + yyerror_range[2] = yylloc; + /* Using YYLLOC is tempting, but would change the location of + the lookahead. YYLOC is available though. */ + YYLLOC_DEFAULT (yyloc, yyerror_range, 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; + + +#if !defined yyoverflow || YYERROR_VERBOSE +/*-------------------------------------------------. +| yyexhaustedlab -- memory exhaustion comes here. | +`-------------------------------------------------*/ +yyexhaustedlab: + yyerror (&yylloc, context, YY_("memory exhausted")); + yyresult = 2; + /* Fall through. */ +#endif + + +/*-----------------------------------------------------. +| yyreturn -- parsing is finished, return the result. | +`-----------------------------------------------------*/ +yyreturn: + if (yychar != YYEMPTY) + { + /* Make sure we have latest lookahead translation. See comments at + user semantic actions for why this is necessary. */ + yytoken = YYTRANSLATE (yychar); + yydestruct ("Cleanup: discarding lookahead", + yytoken, &yylval, &yylloc, context); + } + /* Do not reclaim the symbols of the rule whose 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 + return yyresult; +} +#line 146 "src/core/io/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", IGRAPH_FILE_BASENAME, __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", IGRAPH_FILE_BASENAME, __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", IGRAPH_FILE_BASENAME, __LINE__, IGRAPH_ENOMEM); + return 0; + } + + if (floor(value)==value) { + if (igraph_gml_tree_init_integer(t, s, len, value)) { + free(t); + return 0; + } + } else { + if (igraph_gml_tree_init_real(t, s, len, value)) { + free(t); + return 0; + } + } + + 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]; + + if (!t) { + igraph_error("Cannot build GML tree", IGRAPH_FILE_BASENAME, __LINE__, IGRAPH_ENOMEM); + return 0; + } + + v[vlen]='\0'; + + /* if v == "inf" or v == "nan", the newly created tree node will take ownership + * of s. If the creation fails, we need to free s and v as well in order not + * to leak memory */ + if (strcasecmp(v, "inf")) { + if (igraph_gml_tree_init_real(t, s, len, IGRAPH_INFINITY)) { + free(t); + t = 0; + } + } else if (strcasecmp(v, "nan")) { + if (igraph_gml_tree_init_real(t, s, len, IGRAPH_NAN)) { + free(t); + t = 0; + } + } else { + igraph_error("Parse error", IGRAPH_FILE_BASENAME, __LINE__, IGRAPH_PARSEERROR); + free(t); + t = 0; + } + + v[vlen]=tmp; + free(v); + + if (t == 0) { + /* no new tree node was created so s has no owner any more */ + free(s); + } + + 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", IGRAPH_FILE_BASENAME, __LINE__, IGRAPH_ENOMEM); + return 0; + } + + /* if igraph_gml_tree_init_string succeeds, the newly created tree node takes + * ownership of 'value'. If it fails, we need to free 'value' ourselves in order + * not to leak memory */ + if (igraph_gml_tree_init_string(t, s, len, value, valuelen)) { + free(t); + free(value); + t = 0; + } + + 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", IGRAPH_FILE_BASENAME, __LINE__, IGRAPH_ENOMEM); + return 0; + } + + if (igraph_gml_tree_init_tree(t, s, len, list)) { + free(t); + return 0; + } + + 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/src/rigraph/core/io/gml-parser.h b/src/rigraph/core/io/gml-parser.h new file mode 100644 index 0000000..8434a6b --- /dev/null +++ b/src/rigraph/core/io/gml-parser.h @@ -0,0 +1,101 @@ +/* A Bison parser, made by GNU Bison 3.5.1. */ + +/* Bison interface for Yacc-like parsers in C + + Copyright (C) 1984, 1989-1990, 2000-2015, 2018-2020 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 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 . */ + +/* 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. */ + +/* Undocumented macros, especially those whose name start with YY_, + are private implementation details. Do not rely on them. */ + +#ifndef YY_IGRAPH_GML_YY_YY_TAB_H_INCLUDED +# define YY_IGRAPH_GML_YY_YY_TAB_H_INCLUDED +/* Debug traces. */ +#ifndef YYDEBUG +# define YYDEBUG 0 +#endif +#if YYDEBUG +extern int igraph_gml_yydebug; +#endif + +/* Token type. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + enum yytokentype + { + STRING = 258, + NUM = 259, + KEYWORD = 260, + LISTOPEN = 261, + LISTCLOSE = 262, + EOFF = 263, + ERROR = 264 + }; +#endif + +/* Value type. */ +#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED +union YYSTYPE +{ +#line 91 "src/core/io/gml-parser.y" + + struct { + char *s; + int len; + } str; + void *tree; + double real; + +#line 76 "yy.tab.h" + +}; +typedef union YYSTYPE YYSTYPE; +# define YYSTYPE_IS_TRIVIAL 1 +# define YYSTYPE_IS_DECLARED 1 +#endif + +/* Location type. */ +#if ! defined YYLTYPE && ! defined YYLTYPE_IS_DECLARED +typedef struct YYLTYPE YYLTYPE; +struct YYLTYPE +{ + int first_line; + int first_column; + int last_line; + int last_column; +}; +# define YYLTYPE_IS_DECLARED 1 +# define YYLTYPE_IS_TRIVIAL 1 +#endif + + + +int igraph_gml_yyparse (igraph_i_gml_parsedata_t* context); + +#endif /* !YY_IGRAPH_GML_YY_YY_TAB_H_INCLUDED */ diff --git a/src/rigraph/core/io/gml-parser.y b/src/rigraph/core/io/gml-parser.y new file mode 100644 index 0000000..241b39f --- /dev/null +++ b/src/rigraph/core/io/gml-parser.y @@ -0,0 +1,297 @@ +/* + IGraph library. + Copyright (C) 2009-2012 Gabor Csardi + 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 + +*/ + +%{ + +/* + IGraph library. + Copyright (C) 2009-2012 Gabor Csardi + 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 +#include +#include + +#include "igraph_error.h" +#include "igraph_memory.h" +#include "config.h" + +#include "core/math.h" +#include "io/gml-header.h" +#include "io/gml-tree.h" +#include "io/parsers/gml-parser.h" +#include "io/parsers/gml-lexer.h" +#include "internal/hacks.h" /* strcasecmp */ + +int igraph_gml_yyerror(YYLTYPE* locp, igraph_i_gml_parsedata_t *context, + const char *s); +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)*/ + +%} + +%pure-parser +/* bison: do not remove the equals sign; macOS XCode ships with bison 2.3, which + * needs the equals sign */ +%name-prefix="igraph_gml_yy" +%defines +%locations +%error-verbose +%parse-param { igraph_i_gml_parsedata_t* context } +%lex-param { void *scanner } + +%union { + struct { + char *s; + int len; + } str; + void *tree; + double real; +} + +%type list; +%type keyvalue; +%type key; +%type num; +%type string; + +%token STRING +%token NUM +%token KEYWORD +%token LISTOPEN +%token LISTCLOSE +%token EOFF +%token ERROR + +%destructor { IGRAPH_FREE($$.s); } string key KEYWORD; +%destructor { igraph_gml_tree_destroy($$); } list keyvalue; + +%% + +input: list { context->tree=$1; } + | list EOFF { context->tree=$1; } +; + +list: keyvalue { $$=$1; } + | list keyvalue { $$=igraph_i_gml_merge($1, $2); }; + +keyvalue: key num + { $$=igraph_i_gml_make_numeric($1.s, $1.len, $2); } + | key string + { $$=igraph_i_gml_make_string($1.s, $1.len, $2.s, $2.len); } + | key LISTOPEN list LISTCLOSE + { $$=igraph_i_gml_make_list($1.s, $1.len, $3); } + | key key + { $$=igraph_i_gml_make_numeric2($1.s, $1.len, $2.s, $2.len); } +; + +key: KEYWORD { igraph_i_gml_get_keyword(igraph_gml_yyget_text(scanner), + igraph_gml_yyget_leng(scanner), + &$$); USE($1); }; +num : NUM { $$=igraph_i_gml_get_real(igraph_gml_yyget_text(scanner), + igraph_gml_yyget_leng(scanner)); }; + +string: STRING { igraph_i_gml_get_string(igraph_gml_yyget_text(scanner), + igraph_gml_yyget_leng(scanner), + &$$); }; + +%% + +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", IGRAPH_FILE_BASENAME, __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", IGRAPH_FILE_BASENAME, __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", IGRAPH_FILE_BASENAME, __LINE__, IGRAPH_ENOMEM); + return 0; + } + + if (floor(value)==value) { + if (igraph_gml_tree_init_integer(t, s, len, value)) { + free(t); + return 0; + } + } else { + if (igraph_gml_tree_init_real(t, s, len, value)) { + free(t); + return 0; + } + } + + 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]; + + if (!t) { + igraph_error("Cannot build GML tree", IGRAPH_FILE_BASENAME, __LINE__, IGRAPH_ENOMEM); + return 0; + } + + v[vlen]='\0'; + + /* if v == "inf" or v == "nan", the newly created tree node will take ownership + * of s. If the creation fails, we need to free s and v as well in order not + * to leak memory */ + if (strcasecmp(v, "inf")) { + if (igraph_gml_tree_init_real(t, s, len, IGRAPH_INFINITY)) { + free(t); + t = 0; + } + } else if (strcasecmp(v, "nan")) { + if (igraph_gml_tree_init_real(t, s, len, IGRAPH_NAN)) { + free(t); + t = 0; + } + } else { + igraph_error("Parse error", IGRAPH_FILE_BASENAME, __LINE__, IGRAPH_PARSEERROR); + free(t); + t = 0; + } + + v[vlen]=tmp; + free(v); + + if (t == 0) { + /* no new tree node was created so s has no owner any more */ + free(s); + } + + 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", IGRAPH_FILE_BASENAME, __LINE__, IGRAPH_ENOMEM); + return 0; + } + + /* if igraph_gml_tree_init_string succeeds, the newly created tree node takes + * ownership of 'value'. If it fails, we need to free 'value' ourselves in order + * not to leak memory */ + if (igraph_gml_tree_init_string(t, s, len, value, valuelen)) { + free(t); + free(value); + t = 0; + } + + 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", IGRAPH_FILE_BASENAME, __LINE__, IGRAPH_ENOMEM); + return 0; + } + + if (igraph_gml_tree_init_tree(t, s, len, list)) { + free(t); + return 0; + } + + 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/src/rigraph/core/io/gml-tree.c b/src/rigraph/core/io/gml-tree.c new file mode 100644 index 0000000..8d0fe07 --- /dev/null +++ b/src/rigraph/core/io/gml-tree.c @@ -0,0 +1,260 @@ +/* + IGraph library. + Copyright (C) 2007-2012 Gabor Csardi + 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 "io/gml-tree.h" + +#include + +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/src/rigraph/core/io/gml-tree.h b/src/rigraph/core/io/gml-tree.h new file mode 100644 index 0000000..074721c --- /dev/null +++ b/src/rigraph/core/io/gml-tree.h @@ -0,0 +1,82 @@ +/* + IGraph library. + Copyright (C) 2007-2012 Gabor Csardi + 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 + +#include "igraph_decls.h" +#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/src/rigraph/core/io/gml.c b/src/rigraph/core/io/gml.c new file mode 100644 index 0000000..2b09eb3 --- /dev/null +++ b/src/rigraph/core/io/gml.c @@ -0,0 +1,794 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2005-2020 The igraph development 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., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_foreign.h" + +#include "igraph_attributes.h" +#include "igraph_interface.h" +#include "igraph_memory.h" +#include "igraph_version.h" + +#include "core/trie.h" +#include "graph/attributes.h" +#include "io/gml-header.h" + +#include +#include +#include + +int igraph_gml_yylex_init_extra (igraph_i_gml_parsedata_t* user_defined, + void* scanner); +void 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 ); + +static 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); + } +} + +static int igraph_i_gml_toreal(igraph_gml_tree_t *node, long int pos, igraph_real_t *result) { + + 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; + } + + *result = value; + return IGRAPH_SUCCESS; +} + +static const char *igraph_i_gml_tostring(igraph_gml_tree_t *node, long int pos) { + + int type = igraph_gml_tree_type(node, pos); + static 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; +} + +int igraph_i_gml_parsedata_init(igraph_i_gml_parsedata_t* context) { + context->eof = 0; + context->depth = 0; + context->scanner = 0; + context->tree = 0; + context->errmsg[0] = 0; + + return IGRAPH_SUCCESS; +} + +void igraph_i_gml_parsedata_destroy(igraph_i_gml_parsedata_t* context) { + if (context->tree != 0) { + igraph_gml_tree_destroy(context->tree); + context->tree = 0; + } + + if (context->scanner != 0) { + igraph_gml_yylex_destroy(context->scanner); + context->scanner = 0; + } +} + +/** + * \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. + * + * + * 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 node and edge 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 Version and the + * first graph attribute are completely ignored. + * \oli Graph attributes except for node and + * edge 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 inf (infinity) and nan + * (not a number) as a real number. This is case insensitive, so + * nan, NaN and NAN are equal. + * \endolist + * + * 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; + + IGRAPH_CHECK(igraph_i_gml_parsedata_init(&context)); + IGRAPH_FINALLY(igraph_i_gml_parsedata_destroy, &context); + + igraph_gml_yylex_init_extra(&context, &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_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 in GML file.", 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 in GML file.", 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 in GML file.", 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 was ignored in the GML file."); + } + } + + 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 was ignored in the GML file."); + } + } + + /* 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; + IGRAPH_CHECK(igraph_i_gml_toreal(edge, j, VECTOR(*v) + edgeid)); + } 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 in an edge in GML file.", 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; + IGRAPH_CHECK(igraph_i_gml_toreal(node, j, VECTOR(*v) + id)); + } 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_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_i_gml_parsedata_destroy(&context); + IGRAPH_FINALLY_CLEAN(3); + + return IGRAPH_SUCCESS; +} + +static 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 format 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 IGRAPH_SUCCESS; +} + +#define CHECK(cmd) do { ret=cmd; if (ret<0) IGRAPH_ERROR("Writing GML format 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. + * + * The graph, vertex and edges attributes are written to the + * file as well, if they are numeric or string. + * + * 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 igraph. + * Note that this might result identical names for two attributes, igraph + * does not check this. + * + * The id vertex attribute is treated specially. + * If the id argument is not 0 then it should be a numeric + * vector with the vertex ids and the id vertex attribute is + * ignored (if there is one). If id is 0 and there is a + * numeric id vertex attribute that is used instead. If ids + * are not specified in either way then the regular igraph vertex ids are used. + * + * Note that whichever way vertex ids are specified, their + * uniqueness is not checked. + * + * If the graph has edge attributes named source + * or target 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 NULL 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", + IGRAPH_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(>ypes, 0); + IGRAPH_VECTOR_INIT_FINALLY(&vtypes, 0); + IGRAPH_VECTOR_INIT_FINALLY(&etypes, 0); + IGRAPH_CHECK(igraph_i_attribute_get_info(graph, + &gnames, >ypes, + &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(>ypes); i++) { + char *name, *newname; + igraph_strvector_get(&gnames, i, &name); + IGRAPH_CHECK(igraph_i_gml_convert_to_key(name, &newname)); + IGRAPH_FINALLY(igraph_free, 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); + IGRAPH_FINALLY_CLEAN(1); + } + + /* 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)); + IGRAPH_FINALLY(igraph_free, 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); + IGRAPH_FINALLY_CLEAN(1); + } + 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)); + IGRAPH_FINALLY(igraph_free, 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); + IGRAPH_FINALLY_CLEAN(1); + } + 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(>ypes); + igraph_strvector_destroy(&enames); + igraph_strvector_destroy(&vnames); + igraph_strvector_destroy(&gnames); + IGRAPH_FINALLY_CLEAN(9); + + return IGRAPH_SUCCESS; +} + +#undef CHECK diff --git a/src/rigraph/core/io/graphdb.c b/src/rigraph/core/io/graphdb.c new file mode 100644 index 0000000..21e1d4b --- /dev/null +++ b/src/rigraph/core/io/graphdb.c @@ -0,0 +1,118 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2005-2020 The igraph development 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., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_foreign.h" + +#include "igraph_constructors.h" + +#include "core/trie.h" + +static 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: + * + * + * \blockquote + * 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. + * + * + * 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. \endblockquote + * + * + * 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; +} diff --git a/src/rigraph/core/io/graphml.c b/src/rigraph/core/io/graphml.c new file mode 100644 index 0000000..ba361cd --- /dev/null +++ b/src/rigraph/core/io/graphml.c @@ -0,0 +1,1861 @@ +/* -*- mode: C -*- */ +/* + IGraph R package. + Copyright (C) 2006-2012 Gabor Csardi + 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 "igraph_attributes.h" +#include "igraph_interface.h" +#include "igraph_memory.h" + +#include "core/math.h" +#include "core/trie.h" +#include "graph/attributes.h" +#include "internal/hacks.h" /* strcasecmp */ + +#include "config.h" + +#include +#include /* isnan */ +#include +#include /* va_start & co */ + +#define GRAPHML_NAMESPACE_URI "http://graphml.graphdrawing.org/xmlns" + +#if HAVE_LIBXML == 1 +#include +#include + +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_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; + igraph_bool_t edges_directed; + 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); +} + +static 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; +} + +static 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; +} + +static 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); + IGRAPH_FREE(rec->record.value); + } + if (rec->default_value.as_string != 0) { + IGRAPH_FREE(rec->default_value.as_string); + } + } 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); + } +} + +static int igraph_i_graphml_parser_state_init(struct igraph_i_graphml_parser_state* state, igraph_t* graph, int index) { + memset(state, 0, sizeof(struct igraph_i_graphml_parser_state)); + + state->g = graph; + state->index = index < 0 ? 0 : index; + state->successful = 1; + state->error_message = NULL; + + IGRAPH_CHECK(igraph_vector_int_init(&state->prev_state_stack, 0)); + IGRAPH_CHECK(igraph_vector_int_reserve(&state->prev_state_stack, 32)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &state->prev_state_stack); + + IGRAPH_CHECK(igraph_vector_ptr_init(&state->v_attrs, 0)); + IGRAPH_VECTOR_PTR_SET_ITEM_DESTRUCTOR(&state->v_attrs, + igraph_i_graphml_attribute_record_destroy); + IGRAPH_FINALLY(igraph_vector_ptr_destroy_all, &state->v_attrs); + + IGRAPH_CHECK(igraph_vector_ptr_init(&state->e_attrs, 0)); + IGRAPH_VECTOR_PTR_SET_ITEM_DESTRUCTOR(&state->e_attrs, + igraph_i_graphml_attribute_record_destroy); + IGRAPH_FINALLY(igraph_vector_ptr_destroy_all, &state->e_attrs); + + IGRAPH_CHECK(igraph_vector_ptr_init(&state->g_attrs, 0)); + IGRAPH_VECTOR_PTR_SET_ITEM_DESTRUCTOR(&state->g_attrs, + igraph_i_graphml_attribute_record_destroy); + IGRAPH_FINALLY(igraph_vector_ptr_destroy_all, &state->g_attrs); + + IGRAPH_CHECK(igraph_vector_init(&state->edgelist, 0)); + IGRAPH_FINALLY(igraph_vector_destroy, &state->edgelist); + + IGRAPH_CHECK(igraph_trie_init(&state->node_trie, 1)); + IGRAPH_FINALLY(igraph_trie_destroy, &state->node_trie); + + IGRAPH_CHECK(igraph_strvector_init(&state->edgeids, 0)); + IGRAPH_FINALLY(igraph_strvector_destroy, &state->edgeids); + + IGRAPH_CHECK(igraph_trie_init(&state->v_names, 0)); + IGRAPH_FINALLY(igraph_trie_destroy, &state->v_names); + + IGRAPH_CHECK(igraph_trie_init(&state->e_names, 0)); + IGRAPH_FINALLY(igraph_trie_destroy, &state->e_names); + + IGRAPH_CHECK(igraph_trie_init(&state->g_names, 0)); + + IGRAPH_FINALLY_CLEAN(9); + + return IGRAPH_SUCCESS; +} + +static void igraph_i_graphml_parser_state_destroy(struct igraph_i_graphml_parser_state* state) { + 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); + + igraph_vector_ptr_destroy_all(&state->v_attrs); + igraph_vector_ptr_destroy_all(&state->e_attrs); + igraph_vector_ptr_destroy_all(&state->g_attrs); + + if (state->data_key) { + free(state->data_key); + state->data_key = NULL; + } + if (state->data_char) { + free(state->data_char); + state->data_char = NULL; + } + + if (state->error_message) { + free(state->error_message); + state->error_message = NULL; + } +} + +static 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; + const size_t max_error_message_length = 4096; + + va_list ap; + + va_start(ap, msg); + + if (state->error_message == 0) { + /* ownership of state->error_message passed on immediately to + * state so the state destructor is responsible for freeing it */ + state->error_message = IGRAPH_CALLOC(max_error_message_length, char); + } + + state->successful = 0; + state->st = ERROR; + vsnprintf(state->error_message, max_error_message_length, msg, ap); + + va_end(ap); +} + +static 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; +} + +static 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++; + } +} + +static void igraph_i_graphml_sax_handler_start_document(void *state0) { + struct igraph_i_graphml_parser_state *state = + (struct igraph_i_graphml_parser_state*)state0; + + state->st = START; + state->successful = 1; + state->edges_directed = 0; + state->data_key = NULL; + state->data_char = NULL; + state->unknown_depth = 0; + state->ignore_namespaces = 0; +} + +static int igraph_i_graphml_parser_state_finish_parsing(struct igraph_i_graphml_parser_state *state) { + long i, l; + igraph_attribute_record_t idrec, eidrec; + const char *idstr = "id"; + igraph_bool_t already_has_vertex_id = 0, already_has_edge_id = 0; + igraph_vector_ptr_t vattr, eattr, gattr; + long int esize; + const void **tmp; + + IGRAPH_ASSERT(state->successful); + + /* check that we have found and parsed the graph the user is interested in */ + IGRAPH_ASSERT(state->index < 0); + + IGRAPH_CHECK(igraph_vector_ptr_init(&vattr, igraph_vector_ptr_size(&state->v_attrs) + 1)); + IGRAPH_FINALLY(igraph_vector_ptr_destroy, &vattr); + + esize = igraph_vector_ptr_size(&state->e_attrs); + if (igraph_strvector_size(&state->edgeids) != 0) { + esize++; + } + IGRAPH_CHECK(igraph_vector_ptr_init(&eattr, esize)); + IGRAPH_FINALLY(igraph_vector_ptr_destroy, &eattr); + + IGRAPH_CHECK(igraph_vector_ptr_init(&gattr, igraph_vector_ptr_size(&state->g_attrs))); + 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_CHECK(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_CHECK(igraph_strvector_resize(strvec, nodes)); + for (l = origsize; l < nodes; l++) { + IGRAPH_CHECK(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_CHECK(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_CHECK(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_CHECK(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_CHECK(igraph_strvector_resize(strvec, edges)); + for (l = origsize; l < edges; l++) { + IGRAPH_CHECK(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_CHECK(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_CHECK(igraph_strvector_resize(&state->edgeids, igraph_vector_size(&state->edgelist) / 2)); + for (; origsize < igraph_strvector_size(&state->edgeids); origsize++) { + IGRAPH_CHECK(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_CHECK(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_CHECK(igraph_strvector_resize(strvec, 1)); + for (l = origsize; l < 1; l++) { + IGRAPH_CHECK(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_CHECK(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_CHECK(igraph_empty_attrs(state->g, 0, state->edges_directed, &gattr)); + IGRAPH_CHECK(igraph_add_vertices(state->g, (igraph_integer_t) igraph_trie_size(&state->node_trie), &vattr)); + IGRAPH_CHECK(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); + + return IGRAPH_SUCCESS; +} + +#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)) + +static 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 = NULL; + igraph_vector_ptr_t *ptrvector = NULL; + 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); + IGRAPH_FINALLY(igraph_i_graphml_attribute_record_destroy, 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", IGRAPH_FILE_BASENAME, __LINE__); + skip = 1; + } else if (!xmlStrncmp(toXmlChar("hyperedge"), XML_ATTR_VALUE(it))) { + igraph_i_report_unhandled_attribute_target("hyperedge", IGRAPH_FILE_BASENAME, __LINE__); + skip = 1; + } else if (!xmlStrncmp(toXmlChar("port"), XML_ATTR_VALUE(it))) { + igraph_i_report_unhandled_attribute_target("port", IGRAPH_FILE_BASENAME, __LINE__); + skip = 1; + } else if (!xmlStrncmp(toXmlChar("endpoint"), XML_ATTR_VALUE(it))) { + igraph_i_report_unhandled_attribute_target("endpoint", IGRAPH_FILE_BASENAME, __LINE__); + skip = 1; + } else if (!xmlStrncmp(toXmlChar("all"), XML_ATTR_VALUE(it))) { + /* TODO: we should handle this */ + igraph_i_report_unhandled_attribute_target("all", IGRAPH_FILE_BASENAME, __LINE__); + skip = 1; + } else { + GRAPHML_PARSE_ERROR(state, + "Cannot parse GraphML file, unknown value in the 'for' attribute of a 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 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 because of a missing or unknown 'attr.type' attribute", IGRAPH_FILE_BASENAME, __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 tag"); + return 0; + } + + /* if the code above requested skipping the attribute, free everything and + * return */ + if (skip) { + igraph_i_graphml_attribute_record_destroy(rec); + igraph_free(rec); + IGRAPH_FINALLY_CLEAN(2); + 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(2); /* rec, destructor + igraph_free */ + + /* 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; +} + +static 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 = NULL; + state->data_type = type; + } else { + /* ignore */ + } + } +} + +static 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'; +} + +static 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 = NULL; + igraph_vector_ptr_t *ptrvector = NULL; + 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_WARNING("missing attribute key in a tag, ignoring attribute"); + 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 tag, ignoring attribute", + IGRAPH_FILE_BASENAME, __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); + } +} + +static 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_FATAL( + "state->current_attr_record was null where it should have been " + "non-null; please report as a bug." + ); + 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); + } +} + +static 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; + + IGRAPH_UNUSED(prefix); + IGRAPH_UNUSED(nb_namespaces); + IGRAPH_UNUSED(namespaces); + IGRAPH_UNUSED(nb_defaulted); + + 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 (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 and we are not skipping the current + * attribute, check for default tag */ + if (state->current_attr_record != NULL && 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 tag */ + igraph_i_graphml_handle_unknown_start_tag(state); + break; + + case UNKNOWN: + igraph_i_graphml_handle_unknown_start_tag(state); + break; + + default: + break; + } +} + +static 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 = NULL; + 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; + } +} + +static 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 = */ 0, + /* 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') + +static 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, "&"); d += 4; break; + case '<': + strcpy(d, "<"); d += 3; break; + case '>': + strcpy(d, ">"); d += 3; break; + case '"': + strcpy(d, """); d += 5; break; + case '\'': + strcpy(d, "'"); 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. + * + * + * 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, i.e. if you use igraph from R or Python. + * + * + * Graph attribute names are taken from the attr.name attributes of the + * \c key tags in the GraphML file. Since attr.name is not mandatory, + * igraph will fall back to the \c id attribute of the \c key tag if + * 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; + xmlDocPtr doc; + + struct igraph_i_graphml_parser_state state; + int res; + char buffer[4096]; + igraph_bool_t parsing_successful; + char* error_message; + + if (index < 0) { + IGRAPH_ERROR("Graph index must be non-negative", IGRAPH_EINVAL); + } + + xmlInitParser(); + IGRAPH_CHECK(igraph_i_graphml_parser_state_init(&state, graph, index)); + IGRAPH_FINALLY(igraph_i_graphml_parser_state_destroy, &state); + + /* Create a progressive parser context */ + res = (int) fread(buffer, 1, sizeof(buffer), instream); + if (res < sizeof(buffer) && !feof(instream)) { + IGRAPH_ERROR("IO error while reading GraphML data", IGRAPH_PARSEERROR); + } + 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 + )) { + xmlFreeParserCtxt(ctxt); + IGRAPH_ERROR("Cannot set options for the parser context", IGRAPH_EINVAL); + } + + /* Okay, parsing will start now. The parser might do things that eventually + * trigger the igraph error handler, but we want the parser state to + * survive whatever happens here. So, we need to pop off + * igraph_i_graphml_parser_state_destroy() from the stack and temporarily + * assume responsibility for calling it ourselves until we are back from the + * parser */ + IGRAPH_FINALLY_CLEAN(1); + + /* Do the parsing */ + while ((res = (int) fread(buffer, 1, sizeof(buffer), instream)) > 0) { + xmlParseChunk(ctxt, buffer, res, 0); + if (!state.successful) { + break; + } + } + xmlParseChunk(ctxt, buffer, res, 1); + + /* Free the context */ + doc = ctxt->myDoc; + xmlFreeParserCtxt(ctxt); + if (doc) { + /* In theory this should not be necessary, but it looks like certain malformed + * GraphML files leave a partially-parsed doc in memory */ + xmlFreeDoc(doc); + } + + /* Extract the error message from the parser state (if any), and make a + * copy so we can safely destroy the parser state before triggering the + * error */ + parsing_successful = state.successful; + error_message = parsing_successful || state.error_message == NULL ? NULL : strdup(state.error_message); + + /* Now that we have lifted error_message out of the parser state, we can + * put the destructor of the parser state back on the FINALLY stack */ + IGRAPH_FINALLY(igraph_i_graphml_parser_state_destroy, &state); + + /* ...and we can also put the error message pointer on the FINALLY stack */ + if (error_message != NULL) { + IGRAPH_FINALLY(free, error_message); + } + + /* Trigger the stored error if needed */ + if (!parsing_successful) { + if (error_message != NULL) { + IGRAPH_ERROR(error_message, IGRAPH_PARSEERROR); + } else { + IGRAPH_ERROR("Malformed GraphML file", IGRAPH_PARSEERROR); + } + } + + /* Did we actually manage to reach the graph to be parsed, given its index? + * If not, that's an error as well. */ + if (state.index >= 0) { + IGRAPH_ERROR("Graph index was too large", IGRAPH_EINVAL); + } + + /* Okay, everything seems good. We can now take the parser state and + * construct our graph from the data gathered during the parsing */ + IGRAPH_CHECK(igraph_i_graphml_parser_state_finish_parsing(&state)); + + igraph_i_graphml_parser_state_destroy(&state); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +#else + IGRAPH_UNUSED(graph); + IGRAPH_UNUSED(instream); + IGRAPH_UNUSED(index); + + 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 + * + * + * 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, "\n"); + if (ret < 0) { + IGRAPH_ERROR("Write failed", IGRAPH_EFILE); + } + ret = fprintf(outstream, "\n", GRAPHML_NAMESPACE_URI); + if (ret < 0) { + IGRAPH_ERROR("Write failed", IGRAPH_EFILE); + } + ret = fprintf(outstream, "\n"); + if (ret < 0) { + IGRAPH_ERROR("Write failed", IGRAPH_EFILE); + } + + /* dump the 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(>ypes, 0); + IGRAPH_VECTOR_INIT_FINALLY(&vtypes, 0); + IGRAPH_VECTOR_INIT_FINALLY(&etypes, 0); + igraph_i_attribute_get_info(graph, + &gnames, >ypes, + &vnames, &vtypes, + &enames, &etypes); + + /* graph attributes */ + for (i = 0; i < igraph_vector_size(>ypes); 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, " \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, " \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, " \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, " \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, " \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, " \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, " \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, " \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, " \n", eprefix, name_escaped, name_escaped); + if (ret < 0) { + IGRAPH_ERROR("Write failed", IGRAPH_EFILE); + } + } + IGRAPH_FREE(name_escaped); + } + + ret = fprintf(outstream, " \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(>ypes); 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, " ", 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, "\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, " ", 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, "\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, " %s\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, " \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, " ", 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, "\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, " ", 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, "\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, " %s\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, " \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, " \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, " ", 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, "\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, " ", 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, "\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, " %s\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, " \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, " \n"); + if (ret < 0) { + IGRAPH_ERROR("Write failed", IGRAPH_EFILE); + } + fprintf(outstream, "\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(>ypes); + 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/src/rigraph/core/io/leda.c b/src/rigraph/core/io/leda.c new file mode 100644 index 0000000..b33f286 --- /dev/null +++ b/src/rigraph/core/io/leda.c @@ -0,0 +1,258 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2005-2020 The igraph development 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., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_foreign.h" + +#include "igraph_attributes.h" +#include "igraph_interface.h" +#include "igraph_iterators.h" + +#include "graph/attributes.h" + +#include + +#define CHECK(cmd) do { ret=cmd; if (ret<0) IGRAPH_ERROR("Write failed", IGRAPH_EFILE); } while (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 + * + * + * 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/src/rigraph/core/io/lgl-header.h b/src/rigraph/core/io/lgl-header.h new file mode 100644 index 0000000..fdb8f58 --- /dev/null +++ b/src/rigraph/core/io/lgl-header.h @@ -0,0 +1,37 @@ +/* + IGraph library. + Copyright (C) 2011-2012 Gabor Csardi + 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_error.h" +#include "igraph_vector.h" + +#include "core/trie.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/src/rigraph/core/io/lgl-lexer.c b/src/rigraph/core/io/lgl-lexer.c new file mode 100644 index 0000000..cb35922 --- /dev/null +++ b/src/rigraph/core/io/lgl-lexer.c @@ -0,0 +1,2265 @@ +#line 2 "src/core/io/lgl-lexer.c" + +#line 4 "src/core/io/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 6 +#define YY_FLEX_SUBMINOR_VERSION 4 +#if YY_FLEX_SUBMINOR_VERSION > 0 +#define FLEX_BETA +#endif + +#ifdef yy_create_buffer +#define igraph_lgl_yy_create_buffer_ALREADY_DEFINED +#else +#define yy_create_buffer igraph_lgl_yy_create_buffer +#endif + +#ifdef yy_delete_buffer +#define igraph_lgl_yy_delete_buffer_ALREADY_DEFINED +#else +#define yy_delete_buffer igraph_lgl_yy_delete_buffer +#endif + +#ifdef yy_scan_buffer +#define igraph_lgl_yy_scan_buffer_ALREADY_DEFINED +#else +#define yy_scan_buffer igraph_lgl_yy_scan_buffer +#endif + +#ifdef yy_scan_string +#define igraph_lgl_yy_scan_string_ALREADY_DEFINED +#else +#define yy_scan_string igraph_lgl_yy_scan_string +#endif + +#ifdef yy_scan_bytes +#define igraph_lgl_yy_scan_bytes_ALREADY_DEFINED +#else +#define yy_scan_bytes igraph_lgl_yy_scan_bytes +#endif + +#ifdef yy_init_buffer +#define igraph_lgl_yy_init_buffer_ALREADY_DEFINED +#else +#define yy_init_buffer igraph_lgl_yy_init_buffer +#endif + +#ifdef yy_flush_buffer +#define igraph_lgl_yy_flush_buffer_ALREADY_DEFINED +#else +#define yy_flush_buffer igraph_lgl_yy_flush_buffer +#endif + +#ifdef yy_load_buffer_state +#define igraph_lgl_yy_load_buffer_state_ALREADY_DEFINED +#else +#define yy_load_buffer_state igraph_lgl_yy_load_buffer_state +#endif + +#ifdef yy_switch_to_buffer +#define igraph_lgl_yy_switch_to_buffer_ALREADY_DEFINED +#else +#define yy_switch_to_buffer igraph_lgl_yy_switch_to_buffer +#endif + +#ifdef yypush_buffer_state +#define igraph_lgl_yypush_buffer_state_ALREADY_DEFINED +#else +#define yypush_buffer_state igraph_lgl_yypush_buffer_state +#endif + +#ifdef yypop_buffer_state +#define igraph_lgl_yypop_buffer_state_ALREADY_DEFINED +#else +#define yypop_buffer_state igraph_lgl_yypop_buffer_state +#endif + +#ifdef yyensure_buffer_stack +#define igraph_lgl_yyensure_buffer_stack_ALREADY_DEFINED +#else +#define yyensure_buffer_stack igraph_lgl_yyensure_buffer_stack +#endif + +#ifdef yylex +#define igraph_lgl_yylex_ALREADY_DEFINED +#else +#define yylex igraph_lgl_yylex +#endif + +#ifdef yyrestart +#define igraph_lgl_yyrestart_ALREADY_DEFINED +#else +#define yyrestart igraph_lgl_yyrestart +#endif + +#ifdef yylex_init +#define igraph_lgl_yylex_init_ALREADY_DEFINED +#else +#define yylex_init igraph_lgl_yylex_init +#endif + +#ifdef yylex_init_extra +#define igraph_lgl_yylex_init_extra_ALREADY_DEFINED +#else +#define yylex_init_extra igraph_lgl_yylex_init_extra +#endif + +#ifdef yylex_destroy +#define igraph_lgl_yylex_destroy_ALREADY_DEFINED +#else +#define yylex_destroy igraph_lgl_yylex_destroy +#endif + +#ifdef yyget_debug +#define igraph_lgl_yyget_debug_ALREADY_DEFINED +#else +#define yyget_debug igraph_lgl_yyget_debug +#endif + +#ifdef yyset_debug +#define igraph_lgl_yyset_debug_ALREADY_DEFINED +#else +#define yyset_debug igraph_lgl_yyset_debug +#endif + +#ifdef yyget_extra +#define igraph_lgl_yyget_extra_ALREADY_DEFINED +#else +#define yyget_extra igraph_lgl_yyget_extra +#endif + +#ifdef yyset_extra +#define igraph_lgl_yyset_extra_ALREADY_DEFINED +#else +#define yyset_extra igraph_lgl_yyset_extra +#endif + +#ifdef yyget_in +#define igraph_lgl_yyget_in_ALREADY_DEFINED +#else +#define yyget_in igraph_lgl_yyget_in +#endif + +#ifdef yyset_in +#define igraph_lgl_yyset_in_ALREADY_DEFINED +#else +#define yyset_in igraph_lgl_yyset_in +#endif + +#ifdef yyget_out +#define igraph_lgl_yyget_out_ALREADY_DEFINED +#else +#define yyget_out igraph_lgl_yyget_out +#endif + +#ifdef yyset_out +#define igraph_lgl_yyset_out_ALREADY_DEFINED +#else +#define yyset_out igraph_lgl_yyset_out +#endif + +#ifdef yyget_leng +#define igraph_lgl_yyget_leng_ALREADY_DEFINED +#else +#define yyget_leng igraph_lgl_yyget_leng +#endif + +#ifdef yyget_text +#define igraph_lgl_yyget_text_ALREADY_DEFINED +#else +#define yyget_text igraph_lgl_yyget_text +#endif + +#ifdef yyget_lineno +#define igraph_lgl_yyget_lineno_ALREADY_DEFINED +#else +#define yyget_lineno igraph_lgl_yyget_lineno +#endif + +#ifdef yyset_lineno +#define igraph_lgl_yyset_lineno_ALREADY_DEFINED +#else +#define yyset_lineno igraph_lgl_yyset_lineno +#endif + +#ifdef yyget_column +#define igraph_lgl_yyget_column_ALREADY_DEFINED +#else +#define yyget_column igraph_lgl_yyget_column +#endif + +#ifdef yyset_column +#define igraph_lgl_yyset_column_ALREADY_DEFINED +#else +#define yyset_column igraph_lgl_yyset_column +#endif + +#ifdef yywrap +#define igraph_lgl_yywrap_ALREADY_DEFINED +#else +#define yywrap igraph_lgl_yywrap +#endif + +#ifdef yyget_lval +#define igraph_lgl_yyget_lval_ALREADY_DEFINED +#else +#define yyget_lval igraph_lgl_yyget_lval +#endif + +#ifdef yyset_lval +#define igraph_lgl_yyset_lval_ALREADY_DEFINED +#else +#define yyset_lval igraph_lgl_yyset_lval +#endif + +#ifdef yyget_lloc +#define igraph_lgl_yyget_lloc_ALREADY_DEFINED +#else +#define yyget_lloc igraph_lgl_yyget_lloc +#endif + +#ifdef yyset_lloc +#define igraph_lgl_yyset_lloc_ALREADY_DEFINED +#else +#define yyset_lloc igraph_lgl_yyset_lloc +#endif + +#ifdef yyalloc +#define igraph_lgl_yyalloc_ALREADY_DEFINED +#else +#define yyalloc igraph_lgl_yyalloc +#endif + +#ifdef yyrealloc +#define igraph_lgl_yyrealloc_ALREADY_DEFINED +#else +#define yyrealloc igraph_lgl_yyrealloc +#endif + +#ifdef yyfree +#define igraph_lgl_yyfree_ALREADY_DEFINED +#else +#define yyfree igraph_lgl_yyfree +#endif + +/* First, we deal with platform-specific or compiler-specific issues. */ + +/* begin standard C headers. */ +#include +#include +#include +#include + +/* end standard C headers. */ + +/* flex integer type definitions */ + +#ifndef FLEXINT_H +#define FLEXINT_H + +/* C99 systems have . 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 +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; +#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; + +/* 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 + +#ifndef SIZE_MAX +#define SIZE_MAX (~(size_t)0) +#endif + +#endif /* ! C99 */ + +#endif /* ! FLEXINT_H */ + +/* begin standard C++ headers. */ + +/* TODO: this is always defined, so inline it */ +#define yyconst const + +#if defined(__GNUC__) && __GNUC__ >= 3 +#define yynoreturn __attribute__((__noreturn__)) +#else +#define yynoreturn +#endif + +/* Returned upon end-of-file. */ +#define YY_NULL 0 + +/* Promotes a possibly negative, possibly signed char to an + * integer in range [0..255] for use as an array index. + */ +#define YY_SC_TO_UI(c) ((YY_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 yyrestart( yyin , yyscanner ) +#define YY_END_OF_BUFFER_CHAR 0 + +/* Size of default input buffer. */ +#ifndef YY_BUF_SIZE +#ifdef __ia64__ +/* On IA-64, the buffer size is 16k, not 8k. + * Moreover, YY_BUF_SIZE is 2*YY_READ_BUF_SIZE in the general case. + * Ditto for the __ia64__ case accordingly. + */ +#define YY_BUF_SIZE 32768 +#else +#define YY_BUF_SIZE 16384 +#endif /* __ia64__ */ +#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) + #define YY_LINENO_REWIND_TO(ptr) + +/* 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. + */ + int yy_buf_size; + + /* Number of characters read into yy_ch_buf, not including EOB + * characters. + */ + int 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 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 yyrestart ( FILE *input_file , yyscan_t yyscanner ); +void yy_switch_to_buffer ( YY_BUFFER_STATE new_buffer , yyscan_t yyscanner ); +YY_BUFFER_STATE yy_create_buffer ( FILE *file, int size , yyscan_t yyscanner ); +void yy_delete_buffer ( YY_BUFFER_STATE b , yyscan_t yyscanner ); +void yy_flush_buffer ( YY_BUFFER_STATE b , yyscan_t yyscanner ); +void yypush_buffer_state ( YY_BUFFER_STATE new_buffer , yyscan_t yyscanner ); +void yypop_buffer_state ( yyscan_t yyscanner ); + +static void yyensure_buffer_stack ( yyscan_t yyscanner ); +static void yy_load_buffer_state ( yyscan_t yyscanner ); +static void yy_init_buffer ( YY_BUFFER_STATE b, FILE *file , yyscan_t yyscanner ); +#define YY_FLUSH_BUFFER yy_flush_buffer( YY_CURRENT_BUFFER , yyscanner) + +YY_BUFFER_STATE yy_scan_buffer ( char *base, yy_size_t size , yyscan_t yyscanner ); +YY_BUFFER_STATE yy_scan_string ( const char *yy_str , yyscan_t yyscanner ); +YY_BUFFER_STATE yy_scan_bytes ( const char *bytes, int len , yyscan_t yyscanner ); + +void *yyalloc ( yy_size_t , yyscan_t yyscanner ); +void *yyrealloc ( void *, yy_size_t , yyscan_t yyscanner ); +void yyfree ( void * , yyscan_t yyscanner ); + +#define yy_new_buffer yy_create_buffer +#define yy_set_interactive(is_interactive) \ + { \ + if ( ! YY_CURRENT_BUFFER ){ \ + yyensure_buffer_stack (yyscanner); \ + YY_CURRENT_BUFFER_LVALUE = \ + 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 ){\ + yyensure_buffer_stack (yyscanner); \ + YY_CURRENT_BUFFER_LVALUE = \ + 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(yyscanner) (/*CONSTCOND*/1) +#define YY_SKIP_YYWRAP +typedef flex_uint8_t 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 __attribute__((unused)) yy_fatal_error ( const 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 = (int) (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 const flex_int16_t yy_accept[14] = + { 0, + 0, 0, 7, 4, 2, 3, 3, 1, 5, 4, + 2, 3, 0 + } ; + +static const YY_CHAR 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 const YY_CHAR yy_meta[7] = + { 0, + 1, 2, 3, 4, 5, 5 + } ; + +static const flex_int16_t yy_base[18] = + { 0, + 0, 0, 11, 0, 0, 0, 0, 12, 12, 0, + 0, 12, 12, 9, 7, 4, 4 + } ; + +static const flex_int16_t yy_def[18] = + { 0, + 13, 1, 13, 14, 15, 16, 17, 13, 13, 14, + 15, 13, 0, 13, 13, 13, 13 + } ; + +static const flex_int16_t yy_nxt[19] = + { 0, + 4, 5, 6, 7, 8, 9, 12, 12, 11, 10, + 13, 3, 13, 13, 13, 13, 13, 13 + } ; + +static const flex_int16_t yy_chk[19] = + { 0, + 1, 1, 1, 1, 1, 1, 17, 16, 15, 14, + 3, 13, 13, 13, 13, 13, 13, 13 + } ; + +/* 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/core/io/lgl-lexer.l" +/* + IGraph library. + Copyright (C) 2007-2012 Gabor Csardi + 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/core/io/lgl-lexer.l" + +/* + IGraph library. + Copyright (C) 2007-2012 Gabor Csardi + 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 + +#include "io/lgl-header.h" +#include "io/lgl-parser.h" + +#define YY_EXTRA_TYPE igraph_i_lgl_parsedata_t* +#define YY_USER_ACTION yylloc->first_line = yylineno; +#define YY_FATAL_ERROR(msg) IGRAPH_FATAL("Error in LGL parser: " # msg) +#ifdef USING_R +#define fprintf(file, msg, ...) (1) +#ifdef stdout +# undef stdout +#endif +#define stdout 0 +#endif +#line 722 "src/core/io/lgl-lexer.c" +#define YY_NO_INPUT 1 +#line 724 "src/core/io/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 +#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; + int yy_n_chars; + int 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 yylex_init (yyscan_t* scanner); + +int 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 yylex_destroy ( yyscan_t yyscanner ); + +int yyget_debug ( yyscan_t yyscanner ); + +void yyset_debug ( int debug_flag , yyscan_t yyscanner ); + +YY_EXTRA_TYPE yyget_extra ( yyscan_t yyscanner ); + +void yyset_extra ( YY_EXTRA_TYPE user_defined , yyscan_t yyscanner ); + +FILE *yyget_in ( yyscan_t yyscanner ); + +void yyset_in ( FILE * _in_str , yyscan_t yyscanner ); + +FILE *yyget_out ( yyscan_t yyscanner ); + +void yyset_out ( FILE * _out_str , yyscan_t yyscanner ); + + yy_size_t yyget_leng ( yyscan_t yyscanner ); + +char *yyget_text ( yyscan_t yyscanner ); + +int yyget_lineno ( yyscan_t yyscanner ); + +void yyset_lineno ( int _line_number , yyscan_t yyscanner ); + +int yyget_column ( yyscan_t yyscanner ); + +void yyset_column ( int _column_no , yyscan_t yyscanner ); + +YYSTYPE * yyget_lval ( yyscan_t yyscanner ); + +void yyset_lval ( YYSTYPE * yylval_param , yyscan_t yyscanner ); + + YYLTYPE *yyget_lloc ( yyscan_t yyscanner ); + + void 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 yywrap ( yyscan_t yyscanner ); +#else +extern int yywrap ( yyscan_t yyscanner ); +#endif +#endif + +#ifndef YY_NO_UNPUT + +#endif + +#ifndef yytext_ptr +static void yy_flex_strncpy ( char *, const char *, int , yyscan_t yyscanner); +#endif + +#ifdef YY_NEED_STRLEN +static int yy_flex_strlen ( const 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 +#ifdef __ia64__ +/* On IA-64, the buffer size is 16k, not 8k */ +#define YY_READ_BUF_SIZE 16384 +#else +#define YY_READ_BUF_SIZE 8192 +#endif /* __ia64__ */ +#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 do { if (fwrite( yytext, (size_t) yyleng, 1, yyout )) {} } while (0) +#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 = '*'; \ + int 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 = (int) fread(buf, 1, (yy_size_t) 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 yylex \ + (YYSTYPE * yylval_param, YYLTYPE * yylloc_param , yyscan_t yyscanner); + +#define YY_DECL int 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 /*LINTED*/break; +#endif + +#define YY_RULE_SETUP \ + YY_USER_ACTION + +/** The main scanner function which does all the work. + */ +YY_DECL +{ + yy_state_type yy_current_state; + char *yy_cp, *yy_bp; + int yy_act; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + 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 ) { + yyensure_buffer_stack (yyscanner); + YY_CURRENT_BUFFER_LVALUE = + yy_create_buffer( yyin, YY_BUF_SIZE , yyscanner); + } + + yy_load_buffer_state( yyscanner ); + } + + { +#line 76 "src/core/io/lgl-lexer.l" + + +#line 79 "src/core/io/lgl-lexer.l" + /* --------------------------------------------------hashmark------*/ +#line 1011 "src/core/io/lgl-lexer.c" + + while ( /*CONSTCOND*/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 + { + 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 >= 14 ) + yy_c = yy_meta[yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c]; + ++yy_cp; + } + while ( yy_base[yy_current_state] != 12 ); + +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/core/io/lgl-lexer.l" +{ return HASH; } + YY_BREAK +/* ------------------------------------------------whitespace------*/ +case 2: +YY_RULE_SETUP +#line 83 "src/core/io/lgl-lexer.l" +{ } + YY_BREAK +/* ---------------------------------------------------newline------*/ +case 3: +/* rule 3 can match eol */ +YY_RULE_SETUP +#line 86 "src/core/io/lgl-lexer.l" +{ return NEWLINE; } + YY_BREAK +/* ----------------------------------------------alphanumeric------*/ +case 4: +YY_RULE_SETUP +#line 89 "src/core/io/lgl-lexer.l" +{ return ALNUM; } + YY_BREAK +case YY_STATE_EOF(INITIAL): +#line 91 "src/core/io/lgl-lexer.l" +{ if (yyextra->eof) { + yyterminate(); + } else { + yyextra->eof=1; + return NEWLINE; + } + } + YY_BREAK +case 5: +YY_RULE_SETUP +#line 99 "src/core/io/lgl-lexer.l" +{ return ERROR; } + YY_BREAK +case 6: +YY_RULE_SETUP +#line 101 "src/core/io/lgl-lexer.l" +YY_FATAL_ERROR( "flex scanner jammed" ); + YY_BREAK +#line 1112 "src/core/io/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 + * 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 ( 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 user's declarations */ +} /* end of 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; + char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf; + char *source = yyg->yytext_ptr; + 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 + { + int 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_LVALUE; + + int yy_c_buf_p_offset = + (int) (yyg->yy_c_buf_p - b->yy_ch_buf); + + if ( b->yy_is_our_buffer ) + { + int 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. */ + yyrealloc( (void *) b->yy_ch_buf, + (yy_size_t) (b->yy_buf_size + 2) , yyscanner ); + } + else + /* Can't grow it, we don't own it. */ + b->yy_ch_buf = NULL; + + 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; + 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 ((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. */ + int new_size = yyg->yy_n_chars + number_to_move + (yyg->yy_n_chars >> 1); + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) yyrealloc( + (void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf, (yy_size_t) new_size , yyscanner ); + if ( ! YY_CURRENT_BUFFER_LVALUE->yy_ch_buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_get_next_buffer()" ); + /* "- 2" to take care of EOB's */ + YY_CURRENT_BUFFER_LVALUE->yy_buf_size = (int) (new_size - 2); + } + + 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) +{ + yy_state_type yy_current_state; + 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 ) + { + YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 6); + 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 >= 14 ) + yy_c = yy_meta[yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + 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) +{ + int yy_is_jam; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* This var may be unused depending upon options. */ + char *yy_cp = yyg->yy_c_buf_p; + + YY_CHAR yy_c = 6; + 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 >= 14 ) + yy_c = yy_meta[yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c]; + yy_is_jam = (yy_current_state == 13); + + (void)yyg; + return yy_is_jam ? 0 : yy_current_state; +} + +#ifndef YY_NO_UNPUT + +#endif + +#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 */ + int offset = (int) (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. */ + yyrestart( yyin , yyscanner); + + /*FALLTHROUGH*/ + + case EOB_ACT_END_OF_FILE: + { + if ( 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 yyrestart (FILE * input_file , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + if ( ! YY_CURRENT_BUFFER ){ + yyensure_buffer_stack (yyscanner); + YY_CURRENT_BUFFER_LVALUE = + yy_create_buffer( yyin, YY_BUF_SIZE , yyscanner); + } + + yy_init_buffer( YY_CURRENT_BUFFER, input_file , yyscanner); + yy_load_buffer_state( yyscanner ); +} + +/** Switch to a different input buffer. + * @param new_buffer The new input buffer. + * @param yyscanner The scanner object. + */ + void 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 + * yypop_buffer_state(); + * yypush_buffer_state(new_buffer); + */ + 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; + yy_load_buffer_state( yyscanner ); + + /* We don't actually know whether we did this switch during + * EOF (yywrap()) processing, but the only time this flag + * is looked at is after yywrap() is called, so it's safe + * to go ahead and always set it. + */ + yyg->yy_did_buffer_switch_on_eof = 1; +} + +static void 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 yy_create_buffer (FILE * file, int size , yyscan_t yyscanner) +{ + YY_BUFFER_STATE b; + + b = (YY_BUFFER_STATE) yyalloc( sizeof( struct yy_buffer_state ) , yyscanner ); + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in 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 *) yyalloc( (yy_size_t) (b->yy_buf_size + 2) , yyscanner ); + if ( ! b->yy_ch_buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); + + b->yy_is_our_buffer = 1; + + yy_init_buffer( b, file , yyscanner); + + return b; +} + +/** Destroy the buffer. + * @param b a buffer created with yy_create_buffer() + * @param yyscanner The scanner object. + */ + void 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 ) + yyfree( (void *) b->yy_ch_buf , yyscanner ); + + yyfree( (void *) b , yyscanner ); +} + +/* Initializes or reinitializes a buffer. + * This function is sometimes called more than once on the same buffer, + * such as during a yyrestart() or at EOF. + */ + static void yy_init_buffer (YY_BUFFER_STATE b, FILE * file , yyscan_t yyscanner) + +{ + int oerrno = errno; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + yy_flush_buffer( b , yyscanner); + + b->yy_input_file = file; + b->yy_fill_buffer = 1; + + /* If b is the current buffer, then yy_init_buffer was _probably_ + * called from 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 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 ) + 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 yypush_buffer_state (YY_BUFFER_STATE new_buffer , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + if (new_buffer == NULL) + return; + + yyensure_buffer_stack(yyscanner); + + /* This block is copied from 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 yy_switch_to_buffer. */ + 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 yypop_buffer_state (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + if (!YY_CURRENT_BUFFER) + return; + + 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) { + 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 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; /* After all that talk, this was set to 1 anyways... */ + yyg->yy_buffer_stack = (struct yy_buffer_state**)yyalloc + (num_to_alloc * sizeof(struct yy_buffer_state*) + , yyscanner); + if ( ! yyg->yy_buffer_stack ) + YY_FATAL_ERROR( "out of dynamic memory in 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. */ + yy_size_t grow_size = 8 /* arbitrary grow size */; + + num_to_alloc = yyg->yy_buffer_stack_max + grow_size; + yyg->yy_buffer_stack = (struct yy_buffer_state**)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 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 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 NULL; + + b = (YY_BUFFER_STATE) yyalloc( sizeof( struct yy_buffer_state ) , yyscanner ); + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in yy_scan_buffer()" ); + + b->yy_buf_size = (int) (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 = NULL; + 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; + + yy_switch_to_buffer( b , yyscanner ); + + return b; +} + +/** Setup the input buffer state to scan a string. The next call to 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 + * yy_scan_bytes() instead. + */ +YY_BUFFER_STATE yy_scan_string (const char * yystr , yyscan_t yyscanner) +{ + + return yy_scan_bytes( yystr, (int) strlen(yystr) , yyscanner); +} + +/** Setup the input buffer state to scan the given bytes. The next call to yylex() will + * scan from a @e copy of @a bytes. + * @param yybytes the byte buffer to scan + * @param _yybytes_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 yy_scan_bytes (const char * yybytes, int _yybytes_len , yyscan_t yyscanner) +{ + YY_BUFFER_STATE b; + char *buf; + yy_size_t n; + int i; + + /* Get memory for full buffer, including space for trailing EOB's. */ + n = (yy_size_t) (_yybytes_len + 2); + buf = (char *) yyalloc( n , yyscanner ); + if ( ! buf ) + YY_FATAL_ERROR( "out of dynamic memory in 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 = yy_scan_buffer( buf, n , yyscanner); + if ( ! b ) + YY_FATAL_ERROR( "bad buffer in 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 __attribute__((unused)) yy_fatal_error (const char* msg , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + (void)yyg; + fprintf( stderr, "%s\n", msg ); +} + +/* 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 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 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 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 *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 *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 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 *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 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 line number + * @param yyscanner The scanner object. + */ +void 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( "yyset_lineno called with no buffer" ); + + yylineno = _line_number; +} + +/** Set the current column. + * @param _column_no column number + * @param yyscanner The scanner object. + */ +void 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( "yyset_column called with no buffer" ); + + 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 yy_switch_to_buffer + */ +void yyset_in (FILE * _in_str , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yyin = _in_str ; +} + +void yyset_out (FILE * _out_str , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yyout = _out_str ; +} + +int yyget_debug (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yy_flex_debug; +} + +void 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 * yyget_lval (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yylval; +} + +void yyset_lval (YYSTYPE * yylval_param , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yylval = yylval_param; +} + +YYLTYPE *yyget_lloc (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yylloc; +} + +void yyset_lloc (YYLTYPE * yylloc_param , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yylloc = yylloc_param; +} + +/* User-visible API */ + +/* 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 yylex_init(yyscan_t* ptr_yy_globals) +{ + if (ptr_yy_globals == NULL){ + errno = EINVAL; + return 1; + } + + *ptr_yy_globals = (yyscan_t) 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 ); +} + +/* yylex_init_extra has the same functionality as 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 yyalloc in + * the yyextra field. + */ +int yylex_init_extra( YY_EXTRA_TYPE yy_user_defined, yyscan_t* ptr_yy_globals ) +{ + struct yyguts_t dummy_yyguts; + + yyset_extra (yy_user_defined, &dummy_yyguts); + + if (ptr_yy_globals == NULL){ + errno = EINVAL; + return 1; + } + + *ptr_yy_globals = (yyscan_t) 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)); + + 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 yylex_destroy(), so don't allocate here. + */ + + yyg->yy_buffer_stack = NULL; + yyg->yy_buffer_stack_top = 0; + yyg->yy_buffer_stack_max = 0; + yyg->yy_c_buf_p = NULL; + 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 = NULL; + yyout = NULL; +#endif + + /* For future reference: Set errno on error, since we are called by + * yylex_init() + */ + return 0; +} + +/* yylex_destroy is for both reentrant and non-reentrant scanners. */ +int yylex_destroy (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + /* Pop the buffer stack, destroying each element. */ + while(YY_CURRENT_BUFFER){ + yy_delete_buffer( YY_CURRENT_BUFFER , yyscanner ); + YY_CURRENT_BUFFER_LVALUE = NULL; + yypop_buffer_state(yyscanner); + } + + /* Destroy the stack itself. */ + yyfree(yyg->yy_buffer_stack , yyscanner); + yyg->yy_buffer_stack = NULL; + + /* Destroy the start condition stack. */ + 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 + * yylex() is called, initialization will occur. */ + yy_init_globals( yyscanner); + + /* Destroy the main struct (reentrant only). */ + yyfree ( yyscanner , yyscanner ); + yyscanner = NULL; + return 0; +} + +/* + * Internal utility routines. + */ + +#ifndef yytext_ptr +static void yy_flex_strncpy (char* s1, const char * s2, int n , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + (void)yyg; + + int i; + for ( i = 0; i < n; ++i ) + s1[i] = s2[i]; +} +#endif + +#ifdef YY_NEED_STRLEN +static int yy_flex_strlen (const char * s , yyscan_t yyscanner) +{ + int n; + for ( n = 0; s[n]; ++n ) + ; + + return n; +} +#endif + +void *yyalloc (yy_size_t size , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + (void)yyg; + return malloc(size); +} + +void *yyrealloc (void * ptr, yy_size_t size , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + (void)yyg; + + /* 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 realloc(ptr, size); +} + +void yyfree (void * ptr , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + (void)yyg; + free( (char *) ptr ); /* see yyrealloc() for (char *) cast */ +} + +#define YYTABLES_NAME "yytables" + +#line 101 "src/core/io/lgl-lexer.l" + + diff --git a/src/rigraph/core/io/lgl-lexer.h b/src/rigraph/core/io/lgl-lexer.h new file mode 100644 index 0000000..117b4cb --- /dev/null +++ b/src/rigraph/core/io/lgl-lexer.h @@ -0,0 +1,736 @@ +#ifndef igraph_lgl_yyHEADER_H +#define igraph_lgl_yyHEADER_H 1 +#define igraph_lgl_yyIN_HEADER 1 + +#line 6 "src/core/io/lgl-lexer.h" + +#line 8 "src/core/io/lgl-lexer.h" + +#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 6 +#define YY_FLEX_SUBMINOR_VERSION 4 +#if YY_FLEX_SUBMINOR_VERSION > 0 +#define FLEX_BETA +#endif + +#ifdef yy_create_buffer +#define igraph_lgl_yy_create_buffer_ALREADY_DEFINED +#else +#define yy_create_buffer igraph_lgl_yy_create_buffer +#endif + +#ifdef yy_delete_buffer +#define igraph_lgl_yy_delete_buffer_ALREADY_DEFINED +#else +#define yy_delete_buffer igraph_lgl_yy_delete_buffer +#endif + +#ifdef yy_scan_buffer +#define igraph_lgl_yy_scan_buffer_ALREADY_DEFINED +#else +#define yy_scan_buffer igraph_lgl_yy_scan_buffer +#endif + +#ifdef yy_scan_string +#define igraph_lgl_yy_scan_string_ALREADY_DEFINED +#else +#define yy_scan_string igraph_lgl_yy_scan_string +#endif + +#ifdef yy_scan_bytes +#define igraph_lgl_yy_scan_bytes_ALREADY_DEFINED +#else +#define yy_scan_bytes igraph_lgl_yy_scan_bytes +#endif + +#ifdef yy_init_buffer +#define igraph_lgl_yy_init_buffer_ALREADY_DEFINED +#else +#define yy_init_buffer igraph_lgl_yy_init_buffer +#endif + +#ifdef yy_flush_buffer +#define igraph_lgl_yy_flush_buffer_ALREADY_DEFINED +#else +#define yy_flush_buffer igraph_lgl_yy_flush_buffer +#endif + +#ifdef yy_load_buffer_state +#define igraph_lgl_yy_load_buffer_state_ALREADY_DEFINED +#else +#define yy_load_buffer_state igraph_lgl_yy_load_buffer_state +#endif + +#ifdef yy_switch_to_buffer +#define igraph_lgl_yy_switch_to_buffer_ALREADY_DEFINED +#else +#define yy_switch_to_buffer igraph_lgl_yy_switch_to_buffer +#endif + +#ifdef yypush_buffer_state +#define igraph_lgl_yypush_buffer_state_ALREADY_DEFINED +#else +#define yypush_buffer_state igraph_lgl_yypush_buffer_state +#endif + +#ifdef yypop_buffer_state +#define igraph_lgl_yypop_buffer_state_ALREADY_DEFINED +#else +#define yypop_buffer_state igraph_lgl_yypop_buffer_state +#endif + +#ifdef yyensure_buffer_stack +#define igraph_lgl_yyensure_buffer_stack_ALREADY_DEFINED +#else +#define yyensure_buffer_stack igraph_lgl_yyensure_buffer_stack +#endif + +#ifdef yylex +#define igraph_lgl_yylex_ALREADY_DEFINED +#else +#define yylex igraph_lgl_yylex +#endif + +#ifdef yyrestart +#define igraph_lgl_yyrestart_ALREADY_DEFINED +#else +#define yyrestart igraph_lgl_yyrestart +#endif + +#ifdef yylex_init +#define igraph_lgl_yylex_init_ALREADY_DEFINED +#else +#define yylex_init igraph_lgl_yylex_init +#endif + +#ifdef yylex_init_extra +#define igraph_lgl_yylex_init_extra_ALREADY_DEFINED +#else +#define yylex_init_extra igraph_lgl_yylex_init_extra +#endif + +#ifdef yylex_destroy +#define igraph_lgl_yylex_destroy_ALREADY_DEFINED +#else +#define yylex_destroy igraph_lgl_yylex_destroy +#endif + +#ifdef yyget_debug +#define igraph_lgl_yyget_debug_ALREADY_DEFINED +#else +#define yyget_debug igraph_lgl_yyget_debug +#endif + +#ifdef yyset_debug +#define igraph_lgl_yyset_debug_ALREADY_DEFINED +#else +#define yyset_debug igraph_lgl_yyset_debug +#endif + +#ifdef yyget_extra +#define igraph_lgl_yyget_extra_ALREADY_DEFINED +#else +#define yyget_extra igraph_lgl_yyget_extra +#endif + +#ifdef yyset_extra +#define igraph_lgl_yyset_extra_ALREADY_DEFINED +#else +#define yyset_extra igraph_lgl_yyset_extra +#endif + +#ifdef yyget_in +#define igraph_lgl_yyget_in_ALREADY_DEFINED +#else +#define yyget_in igraph_lgl_yyget_in +#endif + +#ifdef yyset_in +#define igraph_lgl_yyset_in_ALREADY_DEFINED +#else +#define yyset_in igraph_lgl_yyset_in +#endif + +#ifdef yyget_out +#define igraph_lgl_yyget_out_ALREADY_DEFINED +#else +#define yyget_out igraph_lgl_yyget_out +#endif + +#ifdef yyset_out +#define igraph_lgl_yyset_out_ALREADY_DEFINED +#else +#define yyset_out igraph_lgl_yyset_out +#endif + +#ifdef yyget_leng +#define igraph_lgl_yyget_leng_ALREADY_DEFINED +#else +#define yyget_leng igraph_lgl_yyget_leng +#endif + +#ifdef yyget_text +#define igraph_lgl_yyget_text_ALREADY_DEFINED +#else +#define yyget_text igraph_lgl_yyget_text +#endif + +#ifdef yyget_lineno +#define igraph_lgl_yyget_lineno_ALREADY_DEFINED +#else +#define yyget_lineno igraph_lgl_yyget_lineno +#endif + +#ifdef yyset_lineno +#define igraph_lgl_yyset_lineno_ALREADY_DEFINED +#else +#define yyset_lineno igraph_lgl_yyset_lineno +#endif + +#ifdef yyget_column +#define igraph_lgl_yyget_column_ALREADY_DEFINED +#else +#define yyget_column igraph_lgl_yyget_column +#endif + +#ifdef yyset_column +#define igraph_lgl_yyset_column_ALREADY_DEFINED +#else +#define yyset_column igraph_lgl_yyset_column +#endif + +#ifdef yywrap +#define igraph_lgl_yywrap_ALREADY_DEFINED +#else +#define yywrap igraph_lgl_yywrap +#endif + +#ifdef yyget_lval +#define igraph_lgl_yyget_lval_ALREADY_DEFINED +#else +#define yyget_lval igraph_lgl_yyget_lval +#endif + +#ifdef yyset_lval +#define igraph_lgl_yyset_lval_ALREADY_DEFINED +#else +#define yyset_lval igraph_lgl_yyset_lval +#endif + +#ifdef yyget_lloc +#define igraph_lgl_yyget_lloc_ALREADY_DEFINED +#else +#define yyget_lloc igraph_lgl_yyget_lloc +#endif + +#ifdef yyset_lloc +#define igraph_lgl_yyset_lloc_ALREADY_DEFINED +#else +#define yyset_lloc igraph_lgl_yyset_lloc +#endif + +#ifdef yyalloc +#define igraph_lgl_yyalloc_ALREADY_DEFINED +#else +#define yyalloc igraph_lgl_yyalloc +#endif + +#ifdef yyrealloc +#define igraph_lgl_yyrealloc_ALREADY_DEFINED +#else +#define yyrealloc igraph_lgl_yyrealloc +#endif + +#ifdef yyfree +#define igraph_lgl_yyfree_ALREADY_DEFINED +#else +#define yyfree igraph_lgl_yyfree +#endif + +/* First, we deal with platform-specific or compiler-specific issues. */ + +/* begin standard C headers. */ +#include +#include +#include +#include + +/* end standard C headers. */ + +/* flex integer type definitions */ + +#ifndef FLEXINT_H +#define FLEXINT_H + +/* C99 systems have . 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 +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; +#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; + +/* 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 + +#ifndef SIZE_MAX +#define SIZE_MAX (~(size_t)0) +#endif + +#endif /* ! C99 */ + +#endif /* ! FLEXINT_H */ + +/* begin standard C++ headers. */ + +/* TODO: this is always defined, so inline it */ +#define yyconst const + +#if defined(__GNUC__) && __GNUC__ >= 3 +#define yynoreturn __attribute__((__noreturn__)) +#else +#define yynoreturn +#endif + +/* 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 + +/* Size of default input buffer. */ +#ifndef YY_BUF_SIZE +#ifdef __ia64__ +/* On IA-64, the buffer size is 16k, not 8k. + * Moreover, YY_BUF_SIZE is 2*YY_READ_BUF_SIZE in the general case. + * Ditto for the __ia64__ case accordingly. + */ +#define YY_BUF_SIZE 32768 +#else +#define YY_BUF_SIZE 16384 +#endif /* __ia64__ */ +#endif + +#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 + +#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. + */ + int yy_buf_size; + + /* Number of characters read into yy_ch_buf, not including EOB + * characters. + */ + int 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; + + }; +#endif /* !YY_STRUCT_YY_BUFFER_STATE */ + +void yyrestart ( FILE *input_file , yyscan_t yyscanner ); +void yy_switch_to_buffer ( YY_BUFFER_STATE new_buffer , yyscan_t yyscanner ); +YY_BUFFER_STATE yy_create_buffer ( FILE *file, int size , yyscan_t yyscanner ); +void yy_delete_buffer ( YY_BUFFER_STATE b , yyscan_t yyscanner ); +void yy_flush_buffer ( YY_BUFFER_STATE b , yyscan_t yyscanner ); +void yypush_buffer_state ( YY_BUFFER_STATE new_buffer , yyscan_t yyscanner ); +void yypop_buffer_state ( yyscan_t yyscanner ); + +YY_BUFFER_STATE yy_scan_buffer ( char *base, yy_size_t size , yyscan_t yyscanner ); +YY_BUFFER_STATE yy_scan_string ( const char *yy_str , yyscan_t yyscanner ); +YY_BUFFER_STATE yy_scan_bytes ( const char *bytes, int len , yyscan_t yyscanner ); + +void *yyalloc ( yy_size_t , yyscan_t yyscanner ); +void *yyrealloc ( void *, yy_size_t , yyscan_t yyscanner ); +void yyfree ( void * , yyscan_t yyscanner ); + +/* Begin user sect3 */ + +#define igraph_lgl_yywrap(yyscanner) (/*CONSTCOND*/1) +#define YY_SKIP_YYWRAP + +#define yytext_ptr yytext_r + +#ifdef YY_HEADER_EXPORT_START_CONDITIONS +#define INITIAL 0 + +#endif + +#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 +#endif + +#ifndef YY_EXTRA_TYPE +#define YY_EXTRA_TYPE void * +#endif + +int yylex_init (yyscan_t* scanner); + +int 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 yylex_destroy ( yyscan_t yyscanner ); + +int yyget_debug ( yyscan_t yyscanner ); + +void yyset_debug ( int debug_flag , yyscan_t yyscanner ); + +YY_EXTRA_TYPE yyget_extra ( yyscan_t yyscanner ); + +void yyset_extra ( YY_EXTRA_TYPE user_defined , yyscan_t yyscanner ); + +FILE *yyget_in ( yyscan_t yyscanner ); + +void yyset_in ( FILE * _in_str , yyscan_t yyscanner ); + +FILE *yyget_out ( yyscan_t yyscanner ); + +void yyset_out ( FILE * _out_str , yyscan_t yyscanner ); + + int yyget_leng ( yyscan_t yyscanner ); + +char *yyget_text ( yyscan_t yyscanner ); + +int yyget_lineno ( yyscan_t yyscanner ); + +void yyset_lineno ( int _line_number , yyscan_t yyscanner ); + +int yyget_column ( yyscan_t yyscanner ); + +void yyset_column ( int _column_no , yyscan_t yyscanner ); + +YYSTYPE * yyget_lval ( yyscan_t yyscanner ); + +void yyset_lval ( YYSTYPE * yylval_param , yyscan_t yyscanner ); + + YYLTYPE *yyget_lloc ( yyscan_t yyscanner ); + + void 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 yywrap ( yyscan_t yyscanner ); +#else +extern int yywrap ( yyscan_t yyscanner ); +#endif +#endif + +#ifndef yytext_ptr +static void yy_flex_strncpy ( char *, const char *, int , yyscan_t yyscanner); +#endif + +#ifdef YY_NEED_STRLEN +static int yy_flex_strlen ( const char * , yyscan_t yyscanner); +#endif + +#ifndef YY_NO_INPUT + +#endif + +/* Amount of stuff to slurp up with each read. */ +#ifndef YY_READ_BUF_SIZE +#ifdef __ia64__ +/* On IA-64, the buffer size is 16k, not 8k */ +#define YY_READ_BUF_SIZE 16384 +#else +#define YY_READ_BUF_SIZE 8192 +#endif /* __ia64__ */ +#endif + +/* Number of entries by which start-condition stack grows. */ +#ifndef YY_START_STACK_INCR +#define YY_START_STACK_INCR 25 +#endif + +/* 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 yylex \ + (YYSTYPE * yylval_param, YYLTYPE * yylloc_param , yyscan_t yyscanner); + +#define YY_DECL int yylex \ + (YYSTYPE * yylval_param, YYLTYPE * yylloc_param , yyscan_t yyscanner) +#endif /* !YY_DECL */ + +/* yy_get_previous_state - get the state just before the EOB char was reached */ + +#undef YY_NEW_FILE +#undef YY_FLUSH_BUFFER +#undef yy_set_bol +#undef yy_new_buffer +#undef yy_set_interactive +#undef YY_DO_BEFORE_ACTION + +#ifdef YY_DECL_IS_OURS +#undef YY_DECL_IS_OURS +#undef YY_DECL +#endif + +#ifndef igraph_lgl_yy_create_buffer_ALREADY_DEFINED +#undef yy_create_buffer +#endif +#ifndef igraph_lgl_yy_delete_buffer_ALREADY_DEFINED +#undef yy_delete_buffer +#endif +#ifndef igraph_lgl_yy_scan_buffer_ALREADY_DEFINED +#undef yy_scan_buffer +#endif +#ifndef igraph_lgl_yy_scan_string_ALREADY_DEFINED +#undef yy_scan_string +#endif +#ifndef igraph_lgl_yy_scan_bytes_ALREADY_DEFINED +#undef yy_scan_bytes +#endif +#ifndef igraph_lgl_yy_init_buffer_ALREADY_DEFINED +#undef yy_init_buffer +#endif +#ifndef igraph_lgl_yy_flush_buffer_ALREADY_DEFINED +#undef yy_flush_buffer +#endif +#ifndef igraph_lgl_yy_load_buffer_state_ALREADY_DEFINED +#undef yy_load_buffer_state +#endif +#ifndef igraph_lgl_yy_switch_to_buffer_ALREADY_DEFINED +#undef yy_switch_to_buffer +#endif +#ifndef igraph_lgl_yypush_buffer_state_ALREADY_DEFINED +#undef yypush_buffer_state +#endif +#ifndef igraph_lgl_yypop_buffer_state_ALREADY_DEFINED +#undef yypop_buffer_state +#endif +#ifndef igraph_lgl_yyensure_buffer_stack_ALREADY_DEFINED +#undef yyensure_buffer_stack +#endif +#ifndef igraph_lgl_yylex_ALREADY_DEFINED +#undef yylex +#endif +#ifndef igraph_lgl_yyrestart_ALREADY_DEFINED +#undef yyrestart +#endif +#ifndef igraph_lgl_yylex_init_ALREADY_DEFINED +#undef yylex_init +#endif +#ifndef igraph_lgl_yylex_init_extra_ALREADY_DEFINED +#undef yylex_init_extra +#endif +#ifndef igraph_lgl_yylex_destroy_ALREADY_DEFINED +#undef yylex_destroy +#endif +#ifndef igraph_lgl_yyget_debug_ALREADY_DEFINED +#undef yyget_debug +#endif +#ifndef igraph_lgl_yyset_debug_ALREADY_DEFINED +#undef yyset_debug +#endif +#ifndef igraph_lgl_yyget_extra_ALREADY_DEFINED +#undef yyget_extra +#endif +#ifndef igraph_lgl_yyset_extra_ALREADY_DEFINED +#undef yyset_extra +#endif +#ifndef igraph_lgl_yyget_in_ALREADY_DEFINED +#undef yyget_in +#endif +#ifndef igraph_lgl_yyset_in_ALREADY_DEFINED +#undef yyset_in +#endif +#ifndef igraph_lgl_yyget_out_ALREADY_DEFINED +#undef yyget_out +#endif +#ifndef igraph_lgl_yyset_out_ALREADY_DEFINED +#undef yyset_out +#endif +#ifndef igraph_lgl_yyget_leng_ALREADY_DEFINED +#undef yyget_leng +#endif +#ifndef igraph_lgl_yyget_text_ALREADY_DEFINED +#undef yyget_text +#endif +#ifndef igraph_lgl_yyget_lineno_ALREADY_DEFINED +#undef yyget_lineno +#endif +#ifndef igraph_lgl_yyset_lineno_ALREADY_DEFINED +#undef yyset_lineno +#endif +#ifndef igraph_lgl_yyget_column_ALREADY_DEFINED +#undef yyget_column +#endif +#ifndef igraph_lgl_yyset_column_ALREADY_DEFINED +#undef yyset_column +#endif +#ifndef igraph_lgl_yywrap_ALREADY_DEFINED +#undef yywrap +#endif +#ifndef igraph_lgl_yyget_lval_ALREADY_DEFINED +#undef yyget_lval +#endif +#ifndef igraph_lgl_yyset_lval_ALREADY_DEFINED +#undef yyset_lval +#endif +#ifndef igraph_lgl_yyget_lloc_ALREADY_DEFINED +#undef yyget_lloc +#endif +#ifndef igraph_lgl_yyset_lloc_ALREADY_DEFINED +#undef yyset_lloc +#endif +#ifndef igraph_lgl_yyalloc_ALREADY_DEFINED +#undef yyalloc +#endif +#ifndef igraph_lgl_yyrealloc_ALREADY_DEFINED +#undef yyrealloc +#endif +#ifndef igraph_lgl_yyfree_ALREADY_DEFINED +#undef yyfree +#endif +#ifndef igraph_lgl_yytext_ALREADY_DEFINED +#undef yytext +#endif +#ifndef igraph_lgl_yyleng_ALREADY_DEFINED +#undef yyleng +#endif +#ifndef igraph_lgl_yyin_ALREADY_DEFINED +#undef yyin +#endif +#ifndef igraph_lgl_yyout_ALREADY_DEFINED +#undef yyout +#endif +#ifndef igraph_lgl_yy_flex_debug_ALREADY_DEFINED +#undef yy_flex_debug +#endif +#ifndef igraph_lgl_yylineno_ALREADY_DEFINED +#undef yylineno +#endif +#ifndef igraph_lgl_yytables_fload_ALREADY_DEFINED +#undef yytables_fload +#endif +#ifndef igraph_lgl_yytables_destroy_ALREADY_DEFINED +#undef yytables_destroy +#endif +#ifndef igraph_lgl_yyTABLES_NAME_ALREADY_DEFINED +#undef yyTABLES_NAME +#endif + +#line 101 "src/core/io/lgl-lexer.l" + + +#line 735 "src/core/io/lgl-lexer.h" +#undef igraph_lgl_yyIN_HEADER +#endif /* igraph_lgl_yyHEADER_H */ diff --git a/src/rigraph/core/io/lgl-lexer.l b/src/rigraph/core/io/lgl-lexer.l new file mode 100644 index 0000000..e1ed9ac --- /dev/null +++ b/src/rigraph/core/io/lgl-lexer.l @@ -0,0 +1,100 @@ +/* + IGraph library. + Copyright (C) 2007-2012 Gabor Csardi + 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 + +*/ + +%{ + +/* + IGraph library. + Copyright (C) 2007-2012 Gabor Csardi + 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 + +#include "io/lgl-header.h" +#include "io/parsers/lgl-parser.h" + +#define YY_EXTRA_TYPE igraph_i_lgl_parsedata_t* +#define YY_USER_ACTION yylloc->first_line = yylineno; +#define YY_FATAL_ERROR(msg) IGRAPH_FATAL("Error in LGL parser: " # msg) +#ifdef USING_R +#define fprintf(file, msg, ...) (1) +#ifdef stdout +# undef stdout +#endif +#define stdout 0 +#endif +%} + +%option noyywrap +%option prefix="igraph_lgl_yy" +%option nounput +%option noinput +%option nodefault +%option reentrant +%option bison-bridge +%option bison-locations + +alnum [^ \t\r\n\0#] + +%% + + /* --------------------------------------------------hashmark------*/ +# { return HASH; } + + /* ------------------------------------------------whitespace------*/ +[ \t]+ { } + + /* ---------------------------------------------------newline------*/ +\n\r|\r\n|\n|\r { return NEWLINE; } + + /* ----------------------------------------------alphanumeric------*/ +{alnum}+ { return ALNUM; } + +<> { if (yyextra->eof) { + yyterminate(); + } else { + yyextra->eof=1; + return NEWLINE; + } + } + +. { return ERROR; } + +%% diff --git a/src/rigraph/core/io/lgl-parser.c b/src/rigraph/core/io/lgl-parser.c new file mode 100644 index 0000000..6dfae34 --- /dev/null +++ b/src/rigraph/core/io/lgl-parser.c @@ -0,0 +1,1773 @@ +/* A Bison parser, made by GNU Bison 3.5.1. */ + +/* Bison implementation for Yacc-like parsers in C + + Copyright (C) 1984, 1989-1990, 2000-2015, 2018-2020 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 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 . */ + +/* 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. */ + +/* Undocumented macros, especially those whose name start with YY_, + are private implementation details. Do not rely on them. */ + +/* Identify Bison output. */ +#define YYBISON 1 + +/* Bison version. */ +#define YYBISON_VERSION "3.5.1" + +/* Skeleton name. */ +#define YYSKELETON_NAME "yacc.c" + +/* Pure parsers. */ +#define YYPURE 1 + +/* Push parsers. */ +#define YYPUSH 0 + +/* Pull parsers. */ +#define YYPULL 1 + + +/* Substitute the variable and function names. */ +#define yyparse igraph_lgl_yyparse +#define yylex igraph_lgl_yylex +#define yyerror igraph_lgl_yyerror +#define yydebug igraph_lgl_yydebug +#define yynerrs igraph_lgl_yynerrs + +/* First part of user prologue. */ +#line 23 "src/core/io/lgl-parser.y" + + +/* + IGraph library. + Copyright (C) 2006-2012 Gabor Csardi + 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 +#include + +#include "igraph_types.h" +#include "igraph_memory.h" +#include "igraph_error.h" +#include "config.h" + +#include "core/math.h" +#include "io/lgl-header.h" +#include "io/lgl-parser.h" +#include "io/lgl-lexer.h" +#include "internal/hacks.h" + +int igraph_lgl_yyerror(YYLTYPE* locp, igraph_i_lgl_parsedata_t *context, + const char *s); +igraph_real_t igraph_lgl_get_number(const char *str, long int len); + +#define scanner context->scanner + +#line 120 "yy.tab.c" + +# ifndef YY_CAST +# ifdef __cplusplus +# define YY_CAST(Type, Val) static_cast (Val) +# define YY_REINTERPRET_CAST(Type, Val) reinterpret_cast (Val) +# else +# define YY_CAST(Type, Val) ((Type) (Val)) +# define YY_REINTERPRET_CAST(Type, Val) ((Type) (Val)) +# endif +# endif +# ifndef YY_NULLPTR +# if defined __cplusplus +# if 201103L <= __cplusplus +# define YY_NULLPTR nullptr +# else +# define YY_NULLPTR 0 +# endif +# else +# define YY_NULLPTR ((void*)0) +# endif +# endif + +/* Enabling verbose error messages. */ +#ifdef YYERROR_VERBOSE +# undef YYERROR_VERBOSE +# define YYERROR_VERBOSE 1 +#else +# define YYERROR_VERBOSE 1 +#endif + +/* Use api.header.include to #include this header + instead of duplicating it here. */ +#ifndef YY_IGRAPH_LGL_YY_YY_TAB_H_INCLUDED +# define YY_IGRAPH_LGL_YY_YY_TAB_H_INCLUDED +/* Debug traces. */ +#ifndef YYDEBUG +# define YYDEBUG 0 +#endif +#if YYDEBUG +extern int igraph_lgl_yydebug; +#endif + +/* Token type. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + enum yytokentype + { + ALNUM = 258, + NEWLINE = 259, + HASH = 260, + ERROR = 261 + }; +#endif + +/* Value type. */ +#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED +union YYSTYPE +{ +#line 78 "src/core/io/lgl-parser.y" + + long int edgenum; + double weightnum; + +#line 184 "yy.tab.c" + +}; +typedef union YYSTYPE YYSTYPE; +# define YYSTYPE_IS_TRIVIAL 1 +# define YYSTYPE_IS_DECLARED 1 +#endif + +/* Location type. */ +#if ! defined YYLTYPE && ! defined YYLTYPE_IS_DECLARED +typedef struct YYLTYPE YYLTYPE; +struct YYLTYPE +{ + int first_line; + int first_column; + int last_line; + int last_column; +}; +# define YYLTYPE_IS_DECLARED 1 +# define YYLTYPE_IS_TRIVIAL 1 +#endif + + + +int igraph_lgl_yyparse (igraph_i_lgl_parsedata_t* context); + +#endif /* !YY_IGRAPH_LGL_YY_YY_TAB_H_INCLUDED */ + + + +#ifdef short +# undef short +#endif + +/* On compilers that do not define __PTRDIFF_MAX__ etc., make sure + and (if available) are included + so that the code can choose integer types of a good width. */ + +#ifndef __PTRDIFF_MAX__ +# include /* INFRINGES ON USER NAME SPACE */ +# if defined __STDC_VERSION__ && 199901 <= __STDC_VERSION__ +# include /* INFRINGES ON USER NAME SPACE */ +# define YY_STDINT_H +# endif +#endif + +/* Narrow types that promote to a signed type and that can represent a + signed or unsigned integer of at least N bits. In tables they can + save space and decrease cache pressure. Promoting to a signed type + helps avoid bugs in integer arithmetic. */ + +#ifdef __INT_LEAST8_MAX__ +typedef __INT_LEAST8_TYPE__ yytype_int8; +#elif defined YY_STDINT_H +typedef int_least8_t yytype_int8; +#else +typedef signed char yytype_int8; +#endif + +#ifdef __INT_LEAST16_MAX__ +typedef __INT_LEAST16_TYPE__ yytype_int16; +#elif defined YY_STDINT_H +typedef int_least16_t yytype_int16; +#else +typedef short yytype_int16; +#endif + +#if defined __UINT_LEAST8_MAX__ && __UINT_LEAST8_MAX__ <= __INT_MAX__ +typedef __UINT_LEAST8_TYPE__ yytype_uint8; +#elif (!defined __UINT_LEAST8_MAX__ && defined YY_STDINT_H \ + && UINT_LEAST8_MAX <= INT_MAX) +typedef uint_least8_t yytype_uint8; +#elif !defined __UINT_LEAST8_MAX__ && UCHAR_MAX <= INT_MAX +typedef unsigned char yytype_uint8; +#else +typedef short yytype_uint8; +#endif + +#if defined __UINT_LEAST16_MAX__ && __UINT_LEAST16_MAX__ <= __INT_MAX__ +typedef __UINT_LEAST16_TYPE__ yytype_uint16; +#elif (!defined __UINT_LEAST16_MAX__ && defined YY_STDINT_H \ + && UINT_LEAST16_MAX <= INT_MAX) +typedef uint_least16_t yytype_uint16; +#elif !defined __UINT_LEAST16_MAX__ && USHRT_MAX <= INT_MAX +typedef unsigned short yytype_uint16; +#else +typedef int yytype_uint16; +#endif + +#ifndef YYPTRDIFF_T +# if defined __PTRDIFF_TYPE__ && defined __PTRDIFF_MAX__ +# define YYPTRDIFF_T __PTRDIFF_TYPE__ +# define YYPTRDIFF_MAXIMUM __PTRDIFF_MAX__ +# elif defined PTRDIFF_MAX +# ifndef ptrdiff_t +# include /* INFRINGES ON USER NAME SPACE */ +# endif +# define YYPTRDIFF_T ptrdiff_t +# define YYPTRDIFF_MAXIMUM PTRDIFF_MAX +# else +# define YYPTRDIFF_T long +# define YYPTRDIFF_MAXIMUM LONG_MAX +# endif +#endif + +#ifndef YYSIZE_T +# ifdef __SIZE_TYPE__ +# define YYSIZE_T __SIZE_TYPE__ +# elif defined size_t +# define YYSIZE_T size_t +# elif defined __STDC_VERSION__ && 199901 <= __STDC_VERSION__ +# include /* INFRINGES ON USER NAME SPACE */ +# define YYSIZE_T size_t +# else +# define YYSIZE_T unsigned +# endif +#endif + +#define YYSIZE_MAXIMUM \ + YY_CAST (YYPTRDIFF_T, \ + (YYPTRDIFF_MAXIMUM < YY_CAST (YYSIZE_T, -1) \ + ? YYPTRDIFF_MAXIMUM \ + : YY_CAST (YYSIZE_T, -1))) + +#define YYSIZEOF(X) YY_CAST (YYPTRDIFF_T, sizeof (X)) + +/* Stored state numbers (used for stacks). */ +typedef yytype_int8 yy_state_t; + +/* State numbers in computations. */ +typedef int yy_state_fast_t; + +#ifndef YY_ +# if defined YYENABLE_NLS && YYENABLE_NLS +# if ENABLE_NLS +# include /* INFRINGES ON USER NAME SPACE */ +# define YY_(Msgid) dgettext ("bison-runtime", Msgid) +# endif +# endif +# ifndef YY_ +# define YY_(Msgid) Msgid +# endif +#endif + +#ifndef YY_ATTRIBUTE_PURE +# if defined __GNUC__ && 2 < __GNUC__ + (96 <= __GNUC_MINOR__) +# define YY_ATTRIBUTE_PURE __attribute__ ((__pure__)) +# else +# define YY_ATTRIBUTE_PURE +# endif +#endif + +#ifndef YY_ATTRIBUTE_UNUSED +# if defined __GNUC__ && 2 < __GNUC__ + (7 <= __GNUC_MINOR__) +# define YY_ATTRIBUTE_UNUSED __attribute__ ((__unused__)) +# else +# define YY_ATTRIBUTE_UNUSED +# 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 + +#if defined __GNUC__ && ! defined __ICC && 407 <= __GNUC__ * 100 + __GNUC_MINOR__ +/* Suppress an incorrect diagnostic about yylval being uninitialized. */ +# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN \ + _Pragma ("GCC diagnostic push") \ + _Pragma ("GCC diagnostic ignored \"-Wuninitialized\"") \ + _Pragma ("GCC diagnostic ignored \"-Wmaybe-uninitialized\"") +# define YY_IGNORE_MAYBE_UNINITIALIZED_END \ + _Pragma ("GCC diagnostic pop") +#else +# define YY_INITIAL_VALUE(Value) Value +#endif +#ifndef YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN +# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN +# define YY_IGNORE_MAYBE_UNINITIALIZED_END +#endif +#ifndef YY_INITIAL_VALUE +# define YY_INITIAL_VALUE(Value) /* Nothing. */ +#endif + +#if defined __cplusplus && defined __GNUC__ && ! defined __ICC && 6 <= __GNUC__ +# define YY_IGNORE_USELESS_CAST_BEGIN \ + _Pragma ("GCC diagnostic push") \ + _Pragma ("GCC diagnostic ignored \"-Wuseless-cast\"") +# define YY_IGNORE_USELESS_CAST_END \ + _Pragma ("GCC diagnostic pop") +#endif +#ifndef YY_IGNORE_USELESS_CAST_BEGIN +# define YY_IGNORE_USELESS_CAST_BEGIN +# define YY_IGNORE_USELESS_CAST_END +#endif + + +#define YY_ASSERT(E) ((void) (0 && (E))) + +#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 /* INFRINGES ON USER NAME SPACE */ +# elif defined _AIX +# define YYSTACK_ALLOC __alloca +# elif defined _MSC_VER +# include /* INFRINGES ON USER NAME SPACE */ +# define alloca _alloca +# else +# define YYSTACK_ALLOC alloca +# if ! defined _ALLOCA_H && ! defined EXIT_SUCCESS +# include /* INFRINGES ON USER NAME SPACE */ + /* Use EXIT_SUCCESS as a witness for stdlib.h. */ +# ifndef EXIT_SUCCESS +# define EXIT_SUCCESS 0 +# endif +# endif +# endif +# endif +# endif + +# ifdef YYSTACK_ALLOC + /* Pacify GCC's 'empty if-body' warning. */ +# define YYSTACK_FREE(Ptr) do { /* empty */; } while (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 EXIT_SUCCESS \ + && ! ((defined YYMALLOC || defined malloc) \ + && (defined YYFREE || defined free))) +# include /* INFRINGES ON USER NAME SPACE */ +# ifndef EXIT_SUCCESS +# define EXIT_SUCCESS 0 +# endif +# endif +# ifndef YYMALLOC +# define YYMALLOC malloc +# if ! defined malloc && ! defined EXIT_SUCCESS +void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# ifndef YYFREE +# define YYFREE free +# if ! defined free && ! defined EXIT_SUCCESS +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 +{ + yy_state_t yyss_alloc; + YYSTYPE yyvs_alloc; + YYLTYPE yyls_alloc; +}; + +/* The size of the maximum gap between one aligned stack and the next. */ +# define YYSTACK_GAP_MAXIMUM (YYSIZEOF (union yyalloc) - 1) + +/* The size of an array large to enough to hold all stacks, each with + N elements. */ +# define YYSTACK_BYTES(N) \ + ((N) * (YYSIZEOF (yy_state_t) + YYSIZEOF (YYSTYPE) \ + + YYSIZEOF (YYLTYPE)) \ + + 2 * YYSTACK_GAP_MAXIMUM) + +# define YYCOPY_NEEDED 1 + +/* 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_alloc, Stack) \ + do \ + { \ + YYPTRDIFF_T yynewbytes; \ + YYCOPY (&yyptr->Stack_alloc, Stack, yysize); \ + Stack = &yyptr->Stack_alloc; \ + yynewbytes = yystacksize * YYSIZEOF (*Stack) + YYSTACK_GAP_MAXIMUM; \ + yyptr += yynewbytes / YYSIZEOF (*yyptr); \ + } \ + while (0) + +#endif + +#if defined YYCOPY_NEEDED && YYCOPY_NEEDED +/* Copy COUNT objects from SRC to DST. The source and destination do + not overlap. */ +# ifndef YYCOPY +# if defined __GNUC__ && 1 < __GNUC__ +# define YYCOPY(Dst, Src, Count) \ + __builtin_memcpy (Dst, Src, YY_CAST (YYSIZE_T, (Count)) * sizeof (*(Src))) +# else +# define YYCOPY(Dst, Src, Count) \ + do \ + { \ + YYPTRDIFF_T yyi; \ + for (yyi = 0; yyi < (Count); yyi++) \ + (Dst)[yyi] = (Src)[yyi]; \ + } \ + while (0) +# endif +# endif +#endif /* !YYCOPY_NEEDED */ + +/* 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 +/* YYNSTATES -- Number of states. */ +#define YYNSTATES 17 + +#define YYUNDEFTOK 2 +#define YYMAXUTOK 261 + + +/* YYTRANSLATE(TOKEN-NUM) -- Symbol number corresponding to TOKEN-NUM + as returned by yylex, with out-of-bounds checking. */ +#define YYTRANSLATE(YYX) \ + (0 <= (YYX) && (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK) + +/* YYTRANSLATE[TOKEN-NUM] -- Symbol number corresponding to TOKEN-NUM + as returned by yylex. */ +static const yytype_int8 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 + /* YYRLINE[YYN] -- Source line where rule number YYN was defined. */ +static const yytype_int8 yyrline[] = +{ + 0, 93, 93, 94, 95, 98, 100, 102, 102, 104, + 109, 118, 123 +}; +#endif + +#if YYDEBUG || YYERROR_VERBOSE || 1 +/* 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", YY_NULLPTR +}; +#endif + +# ifdef YYPRINT +/* YYTOKNUM[NUM] -- (External) token number corresponding to the + (internal) symbol number NUM (which must be that of a token). */ +static const yytype_int16 yytoknum[] = +{ + 0, 256, 257, 258, 259, 260, 261 +}; +# endif + +#define YYPACT_NINF (-3) + +#define yypact_value_is_default(Yyn) \ + ((Yyn) == YYPACT_NINF) + +#define YYTABLE_NINF (-1) + +#define yytable_value_is_error(Yyn) \ + 0 + + /* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing + STATE-NUM. */ +static const yytype_int8 yypact[] = +{ + -3, 0, -3, -3, 3, -3, -3, -3, -1, 3, + -3, -3, -2, -3, -3, 4, -3 +}; + + /* YYDEFACT[STATE-NUM] -- Default reduction number in state STATE-NUM. + Performed when YYTABLE does not specify something else to do. Zero + means the default is an error. */ +static const yytype_int8 yydefact[] = +{ + 2, 0, 1, 3, 0, 4, 7, 11, 0, 5, + 6, 8, 0, 12, 9, 0, 10 +}; + + /* YYPGOTO[NTERM-NUM]. */ +static const yytype_int8 yypgoto[] = +{ + -3, -3, -3, -3, -3, -3, 1, -3 +}; + + /* YYDEFGOTO[NTERM-NUM]. */ +static const yytype_int8 yydefgoto[] = +{ + -1, 1, 5, 6, 9, 11, 8, 15 +}; + + /* YYTABLE[YYPACT[STATE-NUM]] -- What to do in state STATE-NUM. If + positive, shift that token. If negative, reduce the rule whose + number is the opposite. If YYTABLE_NINF, syntax error. */ +static const yytype_int8 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_int8 yystos[] = +{ + 0, 8, 0, 4, 5, 9, 10, 3, 13, 11, + 4, 12, 13, 3, 4, 14, 4 +}; + + /* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ +static const yytype_int8 yyr1[] = +{ + 0, 7, 8, 8, 8, 9, 10, 11, 11, 12, + 12, 13, 14 +}; + + /* YYR2[YYN] -- Number of symbols on the right hand side of rule YYN. */ +static const yytype_int8 yyr2[] = +{ + 0, 2, 0, 2, 2, 2, 3, 0, 2, 2, + 3, 1, 1 +}; + + +#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 + + +#define YYRECOVERING() (!!yyerrstatus) + +#define YYBACKUP(Token, Value) \ + do \ + if (yychar == YYEMPTY) \ + { \ + yychar = (Token); \ + yylval = (Value); \ + YYPOPSTACK (yylen); \ + yystate = *yyssp; \ + goto yybackup; \ + } \ + else \ + { \ + yyerror (&yylloc, context, YY_("syntax error: cannot back up")); \ + YYERROR; \ + } \ + while (0) + +/* Error token number */ +#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). */ + +#ifndef YYLLOC_DEFAULT +# define YYLLOC_DEFAULT(Current, Rhs, N) \ + do \ + if (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 (0) +#endif + +#define YYRHSLOC(Rhs, K) ((Rhs)[K]) + + +/* Enable debugging if requested. */ +#if YYDEBUG + +# ifndef YYFPRINTF +# include /* INFRINGES ON USER NAME SPACE */ +# define YYFPRINTF fprintf +# endif + +# define YYDPRINTF(Args) \ +do { \ + if (yydebug) \ + YYFPRINTF Args; \ +} while (0) + + +/* 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 + +/* Print *YYLOCP on YYO. Private, do not rely on its existence. */ + +YY_ATTRIBUTE_UNUSED +static int +yy_location_print_ (FILE *yyo, YYLTYPE const * const yylocp) +{ + int res = 0; + int end_col = 0 != yylocp->last_column ? yylocp->last_column - 1 : 0; + if (0 <= yylocp->first_line) + { + res += YYFPRINTF (yyo, "%d", yylocp->first_line); + if (0 <= yylocp->first_column) + res += YYFPRINTF (yyo, ".%d", yylocp->first_column); + } + if (0 <= yylocp->last_line) + { + if (yylocp->first_line < yylocp->last_line) + { + res += YYFPRINTF (yyo, "-%d", yylocp->last_line); + if (0 <= end_col) + res += YYFPRINTF (yyo, ".%d", end_col); + } + else if (0 <= end_col && yylocp->first_column < end_col) + res += YYFPRINTF (yyo, "-%d", end_col); + } + return res; + } + +# define YY_LOCATION_PRINT(File, Loc) \ + yy_location_print_ (File, &(Loc)) + +# else +# define YY_LOCATION_PRINT(File, Loc) ((void) 0) +# endif +#endif + + +# 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 (0) + + +/*-----------------------------------. +| Print this symbol's value on YYO. | +`-----------------------------------*/ + +static void +yy_symbol_value_print (FILE *yyo, int yytype, YYSTYPE const * const yyvaluep, YYLTYPE const * const yylocationp, igraph_i_lgl_parsedata_t* context) +{ + FILE *yyoutput = yyo; + YYUSE (yyoutput); + YYUSE (yylocationp); + YYUSE (context); + if (!yyvaluep) + return; +# ifdef YYPRINT + if (yytype < YYNTOKENS) + YYPRINT (yyo, yytoknum[yytype], *yyvaluep); +# endif + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + YYUSE (yytype); + YY_IGNORE_MAYBE_UNINITIALIZED_END +} + + +/*---------------------------. +| Print this symbol on YYO. | +`---------------------------*/ + +static void +yy_symbol_print (FILE *yyo, int yytype, YYSTYPE const * const yyvaluep, YYLTYPE const * const yylocationp, igraph_i_lgl_parsedata_t* context) +{ + YYFPRINTF (yyo, "%s %s (", + yytype < YYNTOKENS ? "token" : "nterm", yytname[yytype]); + + YY_LOCATION_PRINT (yyo, *yylocationp); + YYFPRINTF (yyo, ": "); + yy_symbol_value_print (yyo, yytype, yyvaluep, yylocationp, context); + YYFPRINTF (yyo, ")"); +} + +/*------------------------------------------------------------------. +| yy_stack_print -- Print the state stack from its BOTTOM up to its | +| TOP (included). | +`------------------------------------------------------------------*/ + +static void +yy_stack_print (yy_state_t *yybottom, yy_state_t *yytop) +{ + YYFPRINTF (stderr, "Stack now"); + for (; yybottom <= yytop; yybottom++) + { + int yybot = *yybottom; + YYFPRINTF (stderr, " %d", yybot); + } + YYFPRINTF (stderr, "\n"); +} + +# define YY_STACK_PRINT(Bottom, Top) \ +do { \ + if (yydebug) \ + yy_stack_print ((Bottom), (Top)); \ +} while (0) + + +/*------------------------------------------------. +| Report that the YYRULE is going to be reduced. | +`------------------------------------------------*/ + +static void +yy_reduce_print (yy_state_t *yyssp, YYSTYPE *yyvsp, YYLTYPE *yylsp, int yyrule, igraph_i_lgl_parsedata_t* context) +{ + int yylno = yyrline[yyrule]; + int yynrhs = yyr2[yyrule]; + int yyi; + YYFPRINTF (stderr, "Reducing stack by rule %d (line %d):\n", + yyrule - 1, yylno); + /* The symbols being reduced. */ + for (yyi = 0; yyi < yynrhs; yyi++) + { + YYFPRINTF (stderr, " $%d = ", yyi + 1); + yy_symbol_print (stderr, + yystos[+yyssp[yyi + 1 - yynrhs]], + &yyvsp[(yyi + 1) - (yynrhs)] + , &(yylsp[(yyi + 1) - (yynrhs)]) , context); + YYFPRINTF (stderr, "\n"); + } +} + +# define YY_REDUCE_PRINT(Rule) \ +do { \ + if (yydebug) \ + yy_reduce_print (yyssp, yyvsp, yylsp, Rule, context); \ +} while (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(S) (YY_CAST (YYPTRDIFF_T, strlen (S))) +# else +/* Return the length of YYSTR. */ +static YYPTRDIFF_T +yystrlen (const char *yystr) +{ + YYPTRDIFF_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. */ +static char * +yystpcpy (char *yydest, const char *yysrc) +{ + 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 YYPTRDIFF_T +yytnamerr (char *yyres, const char *yystr) +{ + if (*yystr == '"') + { + YYPTRDIFF_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; + else + goto append; + + append: + default: + if (yyres) + yyres[yyn] = *yyp; + yyn++; + break; + + case '"': + if (yyres) + yyres[yyn] = '\0'; + return yyn; + } + do_not_strip_quotes: ; + } + + if (yyres) + return yystpcpy (yyres, yystr) - yyres; + else + return yystrlen (yystr); +} +# endif + +/* Copy into *YYMSG, which is of size *YYMSG_ALLOC, an error message + about the unexpected token YYTOKEN for the state stack whose top is + YYSSP. + + Return 0 if *YYMSG was successfully written. Return 1 if *YYMSG is + not large enough to hold the message. In that case, also set + *YYMSG_ALLOC to the required number of bytes. Return 2 if the + required number of bytes is too large to store. */ +static int +yysyntax_error (YYPTRDIFF_T *yymsg_alloc, char **yymsg, + yy_state_t *yyssp, int yytoken) +{ + enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 }; + /* Internationalized format string. */ + const char *yyformat = YY_NULLPTR; + /* Arguments of yyformat: reported tokens (one for the "unexpected", + one per "expected"). */ + char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM]; + /* Actual size of YYARG. */ + int yycount = 0; + /* Cumulated lengths of YYARG. */ + YYPTRDIFF_T yysize = 0; + + /* There are many possibilities here to consider: + - If this state is a consistent state with a default action, then + the only way this function was invoked is if the default action + is an error action. In that case, don't check for expected + tokens because there are none. + - The only way there can be no lookahead present (in yychar) is if + this state is a consistent state with a default action. Thus, + detecting the absence of a lookahead is sufficient to determine + that there is no unexpected or expected token to report. In that + case, just report a simple "syntax error". + - Don't assume there isn't a lookahead just because this state is a + consistent state with a default action. There might have been a + previous inconsistent state, consistent state with a non-default + action, or user semantic action that manipulated yychar. + - Of course, the expected token list depends on states to have + correct lookahead information, and it depends on the parser not + to perform extra reductions after fetching a lookahead from the + scanner and before detecting a syntax error. Thus, state merging + (from LALR or IELR) and default reductions corrupt the expected + token list. However, the list is correct for canonical LR with + one exception: it will still contain any token that will not be + accepted due to an error action in a later state. + */ + if (yytoken != YYEMPTY) + { + int yyn = yypact[+*yyssp]; + YYPTRDIFF_T yysize0 = yytnamerr (YY_NULLPTR, yytname[yytoken]); + yysize = yysize0; + yyarg[yycount++] = yytname[yytoken]; + if (!yypact_value_is_default (yyn)) + { + /* Start YYX at -YYN if negative to avoid negative indexes in + YYCHECK. In other words, skip the first -YYN actions for + this state because they are default actions. */ + 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 yyx; + + for (yyx = yyxbegin; yyx < yyxend; ++yyx) + if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR + && !yytable_value_is_error (yytable[yyx + yyn])) + { + if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM) + { + yycount = 1; + yysize = yysize0; + break; + } + yyarg[yycount++] = yytname[yyx]; + { + YYPTRDIFF_T yysize1 + = yysize + yytnamerr (YY_NULLPTR, yytname[yyx]); + if (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM) + yysize = yysize1; + else + return 2; + } + } + } + } + + switch (yycount) + { +# define YYCASE_(N, S) \ + case N: \ + yyformat = S; \ + break + default: /* Avoid compiler warnings. */ + YYCASE_(0, YY_("syntax error")); + YYCASE_(1, YY_("syntax error, unexpected %s")); + YYCASE_(2, YY_("syntax error, unexpected %s, expecting %s")); + YYCASE_(3, YY_("syntax error, unexpected %s, expecting %s or %s")); + YYCASE_(4, YY_("syntax error, unexpected %s, expecting %s or %s or %s")); + YYCASE_(5, YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s")); +# undef YYCASE_ + } + + { + /* Don't count the "%s"s in the final size, but reserve room for + the terminator. */ + YYPTRDIFF_T yysize1 = yysize + (yystrlen (yyformat) - 2 * yycount) + 1; + if (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM) + yysize = yysize1; + else + return 2; + } + + if (*yymsg_alloc < yysize) + { + *yymsg_alloc = 2 * yysize; + if (! (yysize <= *yymsg_alloc + && *yymsg_alloc <= YYSTACK_ALLOC_MAXIMUM)) + *yymsg_alloc = YYSTACK_ALLOC_MAXIMUM; + return 1; + } + + /* 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 = *yymsg; + int yyi = 0; + while ((*yyp = *yyformat) != '\0') + if (*yyp == '%' && yyformat[1] == 's' && yyi < yycount) + { + yyp += yytnamerr (yyp, yyarg[yyi++]); + yyformat += 2; + } + else + { + ++yyp; + ++yyformat; + } + } + return 0; +} +#endif /* YYERROR_VERBOSE */ + +/*-----------------------------------------------. +| Release the memory associated to this symbol. | +`-----------------------------------------------*/ + +static void +yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep, YYLTYPE *yylocationp, igraph_i_lgl_parsedata_t* context) +{ + YYUSE (yyvaluep); + YYUSE (yylocationp); + YYUSE (context); + if (!yymsg) + yymsg = "Deleting"; + YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp); + + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + YYUSE (yytype); + YY_IGNORE_MAYBE_UNINITIALIZED_END +} + + + + +/*----------. +| yyparse. | +`----------*/ + +int +yyparse (igraph_i_lgl_parsedata_t* context) +{ +/* The lookahead symbol. */ +int yychar; + + +/* The semantic value of the lookahead symbol. */ +/* Default value used for initialization, for pacifying older GCCs + or non-GCC compilers. */ +YY_INITIAL_VALUE (static YYSTYPE yyval_default;) +YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); + +/* Location data for the lookahead symbol. */ +static YYLTYPE yyloc_default +# if defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL + = { 1, 1, 1, 1 } +# endif +; +YYLTYPE yylloc = yyloc_default; + + /* Number of syntax errors so far. */ + int yynerrs; + + yy_state_fast_t yystate; + /* Number of tokens to shift before error messages enabled. */ + int yyerrstatus; + + /* The stacks and their tools: + 'yyss': related to states. + 'yyvs': related to semantic values. + 'yyls': related to locations. + + Refer to the stacks through separate pointers, to allow yyoverflow + to reallocate them elsewhere. */ + + /* The state stack. */ + yy_state_t yyssa[YYINITDEPTH]; + yy_state_t *yyss; + yy_state_t *yyssp; + + /* The semantic value stack. */ + YYSTYPE yyvsa[YYINITDEPTH]; + YYSTYPE *yyvs; + YYSTYPE *yyvsp; + + /* The location stack. */ + YYLTYPE yylsa[YYINITDEPTH]; + YYLTYPE *yyls; + YYLTYPE *yylsp; + + /* The locations where the error started and ended. */ + YYLTYPE yyerror_range[3]; + + YYPTRDIFF_T yystacksize; + + int yyn; + int yyresult; + /* Lookahead token as an internal (translated) token number. */ + int yytoken = 0; + /* The variables used to return semantic value and location from the + action routines. */ + YYSTYPE yyval; + YYLTYPE yyloc; + +#if YYERROR_VERBOSE + /* Buffer for error messages, and its allocated size. */ + char yymsgbuf[128]; + char *yymsg = yymsgbuf; + YYPTRDIFF_T yymsg_alloc = sizeof yymsgbuf; +#endif + +#define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N), yylsp -= (N)) + + /* The number of symbols on the RHS of the reduced rule. + Keep to zero when no symbol should be popped. */ + int yylen = 0; + + yyssp = yyss = yyssa; + yyvsp = yyvs = yyvsa; + yylsp = yyls = yylsa; + yystacksize = YYINITDEPTH; + + YYDPRINTF ((stderr, "Starting parse\n")); + + yystate = 0; + yyerrstatus = 0; + yynerrs = 0; + yychar = YYEMPTY; /* Cause a token to be read. */ + yylsp[0] = yylloc; + 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 -- set current state (the top of the stack) to yystate. | +`--------------------------------------------------------------------*/ +yysetstate: + YYDPRINTF ((stderr, "Entering state %d\n", yystate)); + YY_ASSERT (0 <= yystate && yystate < YYNSTATES); + YY_IGNORE_USELESS_CAST_BEGIN + *yyssp = YY_CAST (yy_state_t, yystate); + YY_IGNORE_USELESS_CAST_END + + if (yyss + yystacksize - 1 <= yyssp) +#if !defined yyoverflow && !defined YYSTACK_RELOCATE + goto yyexhaustedlab; +#else + { + /* Get the current used size of the three stacks, in elements. */ + YYPTRDIFF_T yysize = yyssp - yyss + 1; + +# if defined 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. */ + yy_state_t *yyss1 = yyss; + YYSTYPE *yyvs1 = yyvs; + 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 * YYSIZEOF (*yyssp), + &yyvs1, yysize * YYSIZEOF (*yyvsp), + &yyls1, yysize * YYSIZEOF (*yylsp), + &yystacksize); + yyss = yyss1; + yyvs = yyvs1; + yyls = yyls1; + } +# else /* defined YYSTACK_RELOCATE */ + /* Extend the stack our own way. */ + if (YYMAXDEPTH <= yystacksize) + goto yyexhaustedlab; + yystacksize *= 2; + if (YYMAXDEPTH < yystacksize) + yystacksize = YYMAXDEPTH; + + { + yy_state_t *yyss1 = yyss; + union yyalloc *yyptr = + YY_CAST (union yyalloc *, + YYSTACK_ALLOC (YY_CAST (YYSIZE_T, YYSTACK_BYTES (yystacksize)))); + if (! yyptr) + goto yyexhaustedlab; + YYSTACK_RELOCATE (yyss_alloc, yyss); + YYSTACK_RELOCATE (yyvs_alloc, yyvs); + YYSTACK_RELOCATE (yyls_alloc, yyls); +# undef YYSTACK_RELOCATE + if (yyss1 != yyssa) + YYSTACK_FREE (yyss1); + } +# endif + + yyssp = yyss + yysize - 1; + yyvsp = yyvs + yysize - 1; + yylsp = yyls + yysize - 1; + + YY_IGNORE_USELESS_CAST_BEGIN + YYDPRINTF ((stderr, "Stack size increased to %ld\n", + YY_CAST (long, yystacksize))); + YY_IGNORE_USELESS_CAST_END + + if (yyss + yystacksize - 1 <= yyssp) + YYABORT; + } +#endif /* !defined yyoverflow && !defined YYSTACK_RELOCATE */ + + if (yystate == YYFINAL) + YYACCEPT; + + goto yybackup; + + +/*-----------. +| yybackup. | +`-----------*/ +yybackup: + /* Do appropriate processing given the current state. Read a + lookahead token if we need one and don't already have one. */ + + /* First try to decide what to do without reference to lookahead token. */ + yyn = yypact[yystate]; + if (yypact_value_is_default (yyn)) + goto yydefault; + + /* Not known => get a lookahead token if don't already have one. */ + + /* YYCHAR is either YYEMPTY or YYEOF or a valid lookahead symbol. */ + if (yychar == YYEMPTY) + { + YYDPRINTF ((stderr, "Reading a token: ")); + yychar = yylex (&yylval, &yylloc, scanner); + } + + 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 (yytable_value_is_error (yyn)) + goto yyerrlab; + yyn = -yyn; + goto yyreduce; + } + + /* Count tokens shifted since error; after three, turn off error + status. */ + if (yyerrstatus) + yyerrstatus--; + + /* Shift the lookahead token. */ + YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc); + yystate = yyn; + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + *++yyvsp = yylval; + YY_IGNORE_MAYBE_UNINITIALIZED_END + *++yylsp = yylloc; + + /* Discard the shifted token. */ + yychar = YYEMPTY; + 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); + yyerror_range[1] = yyloc; + YY_REDUCE_PRINT (yyn); + switch (yyn) + { + case 6: +#line 100 "src/core/io/lgl-parser.y" + { context->actvertex=(yyvsp[-1].edgenum); } +#line 1475 "yy.tab.c" + break; + + case 9: +#line 104 "src/core/io/lgl-parser.y" + { + igraph_vector_push_back(context->vector, context->actvertex); + igraph_vector_push_back(context->vector, (yyvsp[-1].edgenum)); + igraph_vector_push_back(context->weights, 0); + } +#line 1485 "yy.tab.c" + break; + + case 10: +#line 109 "src/core/io/lgl-parser.y" + { + igraph_vector_push_back(context->vector, context->actvertex); + igraph_vector_push_back(context->vector, (yyvsp[-2].edgenum)); + igraph_vector_push_back(context->weights, (yyvsp[-1].weightnum)); + context->has_weights = 1; + } +#line 1496 "yy.tab.c" + break; + + case 11: +#line 118 "src/core/io/lgl-parser.y" + { igraph_trie_get2(context->trie, + igraph_lgl_yyget_text(scanner), + igraph_lgl_yyget_leng(scanner), + &(yyval.edgenum)); } +#line 1505 "yy.tab.c" + break; + + case 12: +#line 123 "src/core/io/lgl-parser.y" + { (yyval.weightnum)=igraph_lgl_get_number(igraph_lgl_yyget_text(scanner), + igraph_lgl_yyget_leng(scanner)); } +#line 1512 "yy.tab.c" + break; + + +#line 1516 "yy.tab.c" + + default: break; + } + /* User semantic actions sometimes alter yychar, and that requires + that yytoken be updated with the new translation. We take the + approach of translating immediately before every use of yytoken. + One alternative is translating here after every semantic action, + but that translation would be missed if the semantic action invokes + YYABORT, YYACCEPT, or YYERROR immediately after altering yychar or + if it invokes YYBACKUP. In the case of YYABORT or YYACCEPT, an + incorrect destructor might then be invoked immediately. In the + case of YYERROR or YYBACKUP, subsequent parser actions might lead + to an incorrect destructor call or verbose syntax error message + before the lookahead is translated. */ + 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. */ + { + const int yylhs = yyr1[yyn] - YYNTOKENS; + const int yyi = yypgoto[yylhs] + *yyssp; + yystate = (0 <= yyi && yyi <= YYLAST && yycheck[yyi] == *yyssp + ? yytable[yyi] + : yydefgoto[yylhs]); + } + + goto yynewstate; + + +/*--------------------------------------. +| yyerrlab -- here on detecting error. | +`--------------------------------------*/ +yyerrlab: + /* Make sure we have latest lookahead translation. See comments at + user semantic actions for why this is necessary. */ + yytoken = yychar == YYEMPTY ? YYEMPTY : YYTRANSLATE (yychar); + + /* If not already recovering from an error, report this error. */ + if (!yyerrstatus) + { + ++yynerrs; +#if ! YYERROR_VERBOSE + yyerror (&yylloc, context, YY_("syntax error")); +#else +# define YYSYNTAX_ERROR yysyntax_error (&yymsg_alloc, &yymsg, \ + yyssp, yytoken) + { + char const *yymsgp = YY_("syntax error"); + int yysyntax_error_status; + yysyntax_error_status = YYSYNTAX_ERROR; + if (yysyntax_error_status == 0) + yymsgp = yymsg; + else if (yysyntax_error_status == 1) + { + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); + yymsg = YY_CAST (char *, YYSTACK_ALLOC (YY_CAST (YYSIZE_T, yymsg_alloc))); + if (!yymsg) + { + yymsg = yymsgbuf; + yymsg_alloc = sizeof yymsgbuf; + yysyntax_error_status = 2; + } + else + { + yysyntax_error_status = YYSYNTAX_ERROR; + yymsgp = yymsg; + } + } + yyerror (&yylloc, context, yymsgp); + if (yysyntax_error_status == 2) + goto yyexhaustedlab; + } +# undef YYSYNTAX_ERROR +#endif + } + + yyerror_range[1] = yylloc; + + if (yyerrstatus == 3) + { + /* If just tried and failed to reuse lookahead 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 lookahead token after shifting the error + token. */ + goto yyerrlab1; + + +/*---------------------------------------------------. +| yyerrorlab -- error raised explicitly by YYERROR. | +`---------------------------------------------------*/ +yyerrorlab: + /* Pacify compilers when the user code never invokes YYERROR and the + label yyerrorlab therefore never appears in user code. */ + if (0) + YYERROR; + + /* Do not reclaim the symbols of the rule whose 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 (!yypact_value_is_default (yyn)) + { + 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[1] = *yylsp; + yydestruct ("Error: popping", + yystos[yystate], yyvsp, yylsp, context); + YYPOPSTACK (1); + yystate = *yyssp; + YY_STACK_PRINT (yyss, yyssp); + } + + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + *++yyvsp = yylval; + YY_IGNORE_MAYBE_UNINITIALIZED_END + + yyerror_range[2] = yylloc; + /* Using YYLLOC is tempting, but would change the location of + the lookahead. YYLOC is available though. */ + YYLLOC_DEFAULT (yyloc, yyerror_range, 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; + + +#if !defined yyoverflow || YYERROR_VERBOSE +/*-------------------------------------------------. +| yyexhaustedlab -- memory exhaustion comes here. | +`-------------------------------------------------*/ +yyexhaustedlab: + yyerror (&yylloc, context, YY_("memory exhausted")); + yyresult = 2; + /* Fall through. */ +#endif + + +/*-----------------------------------------------------. +| yyreturn -- parsing is finished, return the result. | +`-----------------------------------------------------*/ +yyreturn: + if (yychar != YYEMPTY) + { + /* Make sure we have latest lookahead translation. See comments at + user semantic actions for why this is necessary. */ + yytoken = YYTRANSLATE (yychar); + yydestruct ("Cleanup: discarding lookahead", + yytoken, &yylval, &yylloc, context); + } + /* Do not reclaim the symbols of the rule whose 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 + return yyresult; +} +#line 126 "src/core/io/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/src/rigraph/core/io/lgl-parser.h b/src/rigraph/core/io/lgl-parser.h new file mode 100644 index 0000000..72a7b95 --- /dev/null +++ b/src/rigraph/core/io/lgl-parser.h @@ -0,0 +1,94 @@ +/* A Bison parser, made by GNU Bison 3.5.1. */ + +/* Bison interface for Yacc-like parsers in C + + Copyright (C) 1984, 1989-1990, 2000-2015, 2018-2020 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 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 . */ + +/* 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. */ + +/* Undocumented macros, especially those whose name start with YY_, + are private implementation details. Do not rely on them. */ + +#ifndef YY_IGRAPH_LGL_YY_YY_TAB_H_INCLUDED +# define YY_IGRAPH_LGL_YY_YY_TAB_H_INCLUDED +/* Debug traces. */ +#ifndef YYDEBUG +# define YYDEBUG 0 +#endif +#if YYDEBUG +extern int igraph_lgl_yydebug; +#endif + +/* Token type. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + enum yytokentype + { + ALNUM = 258, + NEWLINE = 259, + HASH = 260, + ERROR = 261 + }; +#endif + +/* Value type. */ +#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED +union YYSTYPE +{ +#line 78 "src/core/io/lgl-parser.y" + + long int edgenum; + double weightnum; + +#line 69 "yy.tab.h" + +}; +typedef union YYSTYPE YYSTYPE; +# define YYSTYPE_IS_TRIVIAL 1 +# define YYSTYPE_IS_DECLARED 1 +#endif + +/* Location type. */ +#if ! defined YYLTYPE && ! defined YYLTYPE_IS_DECLARED +typedef struct YYLTYPE YYLTYPE; +struct YYLTYPE +{ + int first_line; + int first_column; + int last_line; + int last_column; +}; +# define YYLTYPE_IS_DECLARED 1 +# define YYLTYPE_IS_TRIVIAL 1 +#endif + + + +int igraph_lgl_yyparse (igraph_i_lgl_parsedata_t* context); + +#endif /* !YY_IGRAPH_LGL_YY_YY_TAB_H_INCLUDED */ diff --git a/src/rigraph/core/io/lgl-parser.y b/src/rigraph/core/io/lgl-parser.y new file mode 100644 index 0000000..2bc834c --- /dev/null +++ b/src/rigraph/core/io/lgl-parser.y @@ -0,0 +1,145 @@ +/* + IGraph library. + Copyright (C) 2006-2012 Gabor Csardi + 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 + +*/ + +%{ + +/* + IGraph library. + Copyright (C) 2006-2012 Gabor Csardi + 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 +#include + +#include "igraph_types.h" +#include "igraph_memory.h" +#include "igraph_error.h" +#include "config.h" + +#include "core/math.h" +#include "io/lgl-header.h" +#include "io/parsers/lgl-parser.h" +#include "io/parsers/lgl-lexer.h" +#include "internal/hacks.h" + +int igraph_lgl_yyerror(YYLTYPE* locp, igraph_i_lgl_parsedata_t *context, + const char *s); +igraph_real_t igraph_lgl_get_number(const char *str, long int len); + +#define scanner context->scanner +%} + +%pure-parser +/* bison: do not remove the equals sign; macOS XCode ships with bison 2.3, which + * needs the equals sign */ +%name-prefix="igraph_lgl_yy" +%defines +%locations +%error-verbose +%parse-param { igraph_i_lgl_parsedata_t* context } +%lex-param { void *scanner } + +%union { + long int edgenum; + double weightnum; +} + +%type edgeid +%type weight + +%token ALNUM +%token NEWLINE +%token HASH +%token ERROR + +%% + +input : /* empty */ + | input NEWLINE + | input vertex +; + +vertex : vertexdef edges ; + +vertexdef : HASH edgeid NEWLINE { context->actvertex=$2; } ; + +edges : /* empty */ | edges edge ; + +edge : edgeid NEWLINE { + igraph_vector_push_back(context->vector, context->actvertex); + igraph_vector_push_back(context->vector, $1); + igraph_vector_push_back(context->weights, 0); + } + | edgeid weight NEWLINE { + igraph_vector_push_back(context->vector, context->actvertex); + igraph_vector_push_back(context->vector, $1); + igraph_vector_push_back(context->weights, $2); + context->has_weights = 1; + } +; + + +edgeid : ALNUM { igraph_trie_get2(context->trie, + igraph_lgl_yyget_text(scanner), + igraph_lgl_yyget_leng(scanner), + &$$); }; + +weight : ALNUM { $$=igraph_lgl_get_number(igraph_lgl_yyget_text(scanner), + igraph_lgl_yyget_leng(scanner)); } ; + +%% + +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/src/rigraph/core/io/lgl.c b/src/rigraph/core/io/lgl.c new file mode 100644 index 0000000..5d66bfc --- /dev/null +++ b/src/rigraph/core/io/lgl.c @@ -0,0 +1,413 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2005-2020 The igraph development 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., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_foreign.h" + +#include "igraph_attributes.h" +#include "igraph_interface.h" + +#include "graph/attributes.h" + +#include "lgl-header.h" + +int igraph_lgl_yylex_init_extra (igraph_i_lgl_parsedata_t* user_defined, + void* scanner); +void 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 .lgl file + * + * + * The .lgl 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 The second format is the LGL file format + * (.lgl 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. \endblockquote + * + * + * 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 = ≜ + 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; +} + +/** + * \ingroup loadsave + * \function igraph_write_graph_lgl + * \brief Writes the graph to a file in .lgl format + * + * + * .lgl is a format used by LGL, see \ref + * igraph_read_graph_lgl() for details. + * + * + * Note that having multiple or loop edges in an + * .lgl 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 a string vertex attribute, if symbolic names + * are to be written to the file. Supply \c NULL to write vertex + * ids instead. + * \param weights The name of a numerical edge attribute, which will be + * written as weights to the file. Supply \c NULL to skip writing + * edge weights. + * \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 (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 (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); + 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) { + /* 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); + if (from == actvertex) { + ret1 = fprintf(outstream, "%li ", (long)to); + } else { + actvertex = from; + ret1 = fprintf(outstream, "# %li\n%li ", (long)from, (long)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; + char *str1, *str2; + igraph_edge(graph, edge, &from, &to); + igraph_strvector_get(&nvec, to, &str2); + 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 = 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); + } + + 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(°, 1); + IGRAPH_CHECK(igraph_strvector_init(&nvec, 1)); + IGRAPH_FINALLY(igraph_strvector_destroy, &nvec); + for (i = 0; i < nov; i++) { + igraph_degree(graph, °, 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(°); + IGRAPH_FINALLY_CLEAN(2); + } + + igraph_eit_destroy(&it); + IGRAPH_FINALLY_CLEAN(1); + return 0; +} diff --git a/src/rigraph/core/io/ncol-header.h b/src/rigraph/core/io/ncol-header.h new file mode 100644 index 0000000..2ed06f2 --- /dev/null +++ b/src/rigraph/core/io/ncol-header.h @@ -0,0 +1,36 @@ +/* + IGraph library. + Copyright (C) 2011-2012 Gabor Csardi + 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_error.h" +#include "igraph_vector.h" + +#include "core/trie.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/src/rigraph/core/io/ncol-lexer.c b/src/rigraph/core/io/ncol-lexer.c new file mode 100644 index 0000000..bfab00c --- /dev/null +++ b/src/rigraph/core/io/ncol-lexer.c @@ -0,0 +1,2260 @@ +#line 2 "src/core/io/ncol-lexer.c" + +#line 4 "src/core/io/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 6 +#define YY_FLEX_SUBMINOR_VERSION 4 +#if YY_FLEX_SUBMINOR_VERSION > 0 +#define FLEX_BETA +#endif + +#ifdef yy_create_buffer +#define igraph_ncol_yy_create_buffer_ALREADY_DEFINED +#else +#define yy_create_buffer igraph_ncol_yy_create_buffer +#endif + +#ifdef yy_delete_buffer +#define igraph_ncol_yy_delete_buffer_ALREADY_DEFINED +#else +#define yy_delete_buffer igraph_ncol_yy_delete_buffer +#endif + +#ifdef yy_scan_buffer +#define igraph_ncol_yy_scan_buffer_ALREADY_DEFINED +#else +#define yy_scan_buffer igraph_ncol_yy_scan_buffer +#endif + +#ifdef yy_scan_string +#define igraph_ncol_yy_scan_string_ALREADY_DEFINED +#else +#define yy_scan_string igraph_ncol_yy_scan_string +#endif + +#ifdef yy_scan_bytes +#define igraph_ncol_yy_scan_bytes_ALREADY_DEFINED +#else +#define yy_scan_bytes igraph_ncol_yy_scan_bytes +#endif + +#ifdef yy_init_buffer +#define igraph_ncol_yy_init_buffer_ALREADY_DEFINED +#else +#define yy_init_buffer igraph_ncol_yy_init_buffer +#endif + +#ifdef yy_flush_buffer +#define igraph_ncol_yy_flush_buffer_ALREADY_DEFINED +#else +#define yy_flush_buffer igraph_ncol_yy_flush_buffer +#endif + +#ifdef yy_load_buffer_state +#define igraph_ncol_yy_load_buffer_state_ALREADY_DEFINED +#else +#define yy_load_buffer_state igraph_ncol_yy_load_buffer_state +#endif + +#ifdef yy_switch_to_buffer +#define igraph_ncol_yy_switch_to_buffer_ALREADY_DEFINED +#else +#define yy_switch_to_buffer igraph_ncol_yy_switch_to_buffer +#endif + +#ifdef yypush_buffer_state +#define igraph_ncol_yypush_buffer_state_ALREADY_DEFINED +#else +#define yypush_buffer_state igraph_ncol_yypush_buffer_state +#endif + +#ifdef yypop_buffer_state +#define igraph_ncol_yypop_buffer_state_ALREADY_DEFINED +#else +#define yypop_buffer_state igraph_ncol_yypop_buffer_state +#endif + +#ifdef yyensure_buffer_stack +#define igraph_ncol_yyensure_buffer_stack_ALREADY_DEFINED +#else +#define yyensure_buffer_stack igraph_ncol_yyensure_buffer_stack +#endif + +#ifdef yylex +#define igraph_ncol_yylex_ALREADY_DEFINED +#else +#define yylex igraph_ncol_yylex +#endif + +#ifdef yyrestart +#define igraph_ncol_yyrestart_ALREADY_DEFINED +#else +#define yyrestart igraph_ncol_yyrestart +#endif + +#ifdef yylex_init +#define igraph_ncol_yylex_init_ALREADY_DEFINED +#else +#define yylex_init igraph_ncol_yylex_init +#endif + +#ifdef yylex_init_extra +#define igraph_ncol_yylex_init_extra_ALREADY_DEFINED +#else +#define yylex_init_extra igraph_ncol_yylex_init_extra +#endif + +#ifdef yylex_destroy +#define igraph_ncol_yylex_destroy_ALREADY_DEFINED +#else +#define yylex_destroy igraph_ncol_yylex_destroy +#endif + +#ifdef yyget_debug +#define igraph_ncol_yyget_debug_ALREADY_DEFINED +#else +#define yyget_debug igraph_ncol_yyget_debug +#endif + +#ifdef yyset_debug +#define igraph_ncol_yyset_debug_ALREADY_DEFINED +#else +#define yyset_debug igraph_ncol_yyset_debug +#endif + +#ifdef yyget_extra +#define igraph_ncol_yyget_extra_ALREADY_DEFINED +#else +#define yyget_extra igraph_ncol_yyget_extra +#endif + +#ifdef yyset_extra +#define igraph_ncol_yyset_extra_ALREADY_DEFINED +#else +#define yyset_extra igraph_ncol_yyset_extra +#endif + +#ifdef yyget_in +#define igraph_ncol_yyget_in_ALREADY_DEFINED +#else +#define yyget_in igraph_ncol_yyget_in +#endif + +#ifdef yyset_in +#define igraph_ncol_yyset_in_ALREADY_DEFINED +#else +#define yyset_in igraph_ncol_yyset_in +#endif + +#ifdef yyget_out +#define igraph_ncol_yyget_out_ALREADY_DEFINED +#else +#define yyget_out igraph_ncol_yyget_out +#endif + +#ifdef yyset_out +#define igraph_ncol_yyset_out_ALREADY_DEFINED +#else +#define yyset_out igraph_ncol_yyset_out +#endif + +#ifdef yyget_leng +#define igraph_ncol_yyget_leng_ALREADY_DEFINED +#else +#define yyget_leng igraph_ncol_yyget_leng +#endif + +#ifdef yyget_text +#define igraph_ncol_yyget_text_ALREADY_DEFINED +#else +#define yyget_text igraph_ncol_yyget_text +#endif + +#ifdef yyget_lineno +#define igraph_ncol_yyget_lineno_ALREADY_DEFINED +#else +#define yyget_lineno igraph_ncol_yyget_lineno +#endif + +#ifdef yyset_lineno +#define igraph_ncol_yyset_lineno_ALREADY_DEFINED +#else +#define yyset_lineno igraph_ncol_yyset_lineno +#endif + +#ifdef yyget_column +#define igraph_ncol_yyget_column_ALREADY_DEFINED +#else +#define yyget_column igraph_ncol_yyget_column +#endif + +#ifdef yyset_column +#define igraph_ncol_yyset_column_ALREADY_DEFINED +#else +#define yyset_column igraph_ncol_yyset_column +#endif + +#ifdef yywrap +#define igraph_ncol_yywrap_ALREADY_DEFINED +#else +#define yywrap igraph_ncol_yywrap +#endif + +#ifdef yyget_lval +#define igraph_ncol_yyget_lval_ALREADY_DEFINED +#else +#define yyget_lval igraph_ncol_yyget_lval +#endif + +#ifdef yyset_lval +#define igraph_ncol_yyset_lval_ALREADY_DEFINED +#else +#define yyset_lval igraph_ncol_yyset_lval +#endif + +#ifdef yyget_lloc +#define igraph_ncol_yyget_lloc_ALREADY_DEFINED +#else +#define yyget_lloc igraph_ncol_yyget_lloc +#endif + +#ifdef yyset_lloc +#define igraph_ncol_yyset_lloc_ALREADY_DEFINED +#else +#define yyset_lloc igraph_ncol_yyset_lloc +#endif + +#ifdef yyalloc +#define igraph_ncol_yyalloc_ALREADY_DEFINED +#else +#define yyalloc igraph_ncol_yyalloc +#endif + +#ifdef yyrealloc +#define igraph_ncol_yyrealloc_ALREADY_DEFINED +#else +#define yyrealloc igraph_ncol_yyrealloc +#endif + +#ifdef yyfree +#define igraph_ncol_yyfree_ALREADY_DEFINED +#else +#define yyfree igraph_ncol_yyfree +#endif + +/* First, we deal with platform-specific or compiler-specific issues. */ + +/* begin standard C headers. */ +#include +#include +#include +#include + +/* end standard C headers. */ + +/* flex integer type definitions */ + +#ifndef FLEXINT_H +#define FLEXINT_H + +/* C99 systems have . 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 +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; +#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; + +/* 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 + +#ifndef SIZE_MAX +#define SIZE_MAX (~(size_t)0) +#endif + +#endif /* ! C99 */ + +#endif /* ! FLEXINT_H */ + +/* begin standard C++ headers. */ + +/* TODO: this is always defined, so inline it */ +#define yyconst const + +#if defined(__GNUC__) && __GNUC__ >= 3 +#define yynoreturn __attribute__((__noreturn__)) +#else +#define yynoreturn +#endif + +/* Returned upon end-of-file. */ +#define YY_NULL 0 + +/* Promotes a possibly negative, possibly signed char to an + * integer in range [0..255] for use as an array index. + */ +#define YY_SC_TO_UI(c) ((YY_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 yyrestart( yyin , yyscanner ) +#define YY_END_OF_BUFFER_CHAR 0 + +/* Size of default input buffer. */ +#ifndef YY_BUF_SIZE +#ifdef __ia64__ +/* On IA-64, the buffer size is 16k, not 8k. + * Moreover, YY_BUF_SIZE is 2*YY_READ_BUF_SIZE in the general case. + * Ditto for the __ia64__ case accordingly. + */ +#define YY_BUF_SIZE 32768 +#else +#define YY_BUF_SIZE 16384 +#endif /* __ia64__ */ +#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) + #define YY_LINENO_REWIND_TO(ptr) + +/* 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. + */ + int yy_buf_size; + + /* Number of characters read into yy_ch_buf, not including EOB + * characters. + */ + int 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 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 yyrestart ( FILE *input_file , yyscan_t yyscanner ); +void yy_switch_to_buffer ( YY_BUFFER_STATE new_buffer , yyscan_t yyscanner ); +YY_BUFFER_STATE yy_create_buffer ( FILE *file, int size , yyscan_t yyscanner ); +void yy_delete_buffer ( YY_BUFFER_STATE b , yyscan_t yyscanner ); +void yy_flush_buffer ( YY_BUFFER_STATE b , yyscan_t yyscanner ); +void yypush_buffer_state ( YY_BUFFER_STATE new_buffer , yyscan_t yyscanner ); +void yypop_buffer_state ( yyscan_t yyscanner ); + +static void yyensure_buffer_stack ( yyscan_t yyscanner ); +static void yy_load_buffer_state ( yyscan_t yyscanner ); +static void yy_init_buffer ( YY_BUFFER_STATE b, FILE *file , yyscan_t yyscanner ); +#define YY_FLUSH_BUFFER yy_flush_buffer( YY_CURRENT_BUFFER , yyscanner) + +YY_BUFFER_STATE yy_scan_buffer ( char *base, yy_size_t size , yyscan_t yyscanner ); +YY_BUFFER_STATE yy_scan_string ( const char *yy_str , yyscan_t yyscanner ); +YY_BUFFER_STATE yy_scan_bytes ( const char *bytes, int len , yyscan_t yyscanner ); + +void *yyalloc ( yy_size_t , yyscan_t yyscanner ); +void *yyrealloc ( void *, yy_size_t , yyscan_t yyscanner ); +void yyfree ( void * , yyscan_t yyscanner ); + +#define yy_new_buffer yy_create_buffer +#define yy_set_interactive(is_interactive) \ + { \ + if ( ! YY_CURRENT_BUFFER ){ \ + yyensure_buffer_stack (yyscanner); \ + YY_CURRENT_BUFFER_LVALUE = \ + 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 ){\ + yyensure_buffer_stack (yyscanner); \ + YY_CURRENT_BUFFER_LVALUE = \ + 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(yyscanner) (/*CONSTCOND*/1) +#define YY_SKIP_YYWRAP +typedef flex_uint8_t 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 __attribute__((unused)) yy_fatal_error ( const 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 = (int) (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 const flex_int16_t yy_accept[13] = + { 0, + 0, 0, 6, 3, 1, 2, 2, 4, 3, 1, + 2, 0 + } ; + +static const YY_CHAR 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 const YY_CHAR yy_meta[6] = + { 0, + 1, 2, 3, 4, 5 + } ; + +static const flex_int16_t yy_base[17] = + { 0, + 0, 0, 10, 0, 0, 0, 0, 11, 0, 0, + 11, 11, 8, 6, 3, 3 + } ; + +static const flex_int16_t yy_def[17] = + { 0, + 12, 1, 12, 13, 14, 15, 16, 12, 13, 14, + 12, 0, 12, 12, 12, 12 + } ; + +static const flex_int16_t yy_nxt[17] = + { 0, + 4, 5, 6, 7, 8, 11, 11, 10, 9, 12, + 3, 12, 12, 12, 12, 12 + } ; + +static const 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/core/io/ncol-lexer.l" +/* + IGraph library. + Copyright (C) 2006-2012 Gabor Csardi + 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/core/io/ncol-lexer.l" + +/* + IGraph library. + Copyright (C) 2006-2012 Gabor Csardi + 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 + +#include "io/ncol-header.h" +#include "io/ncol-parser.h" + +#define YY_EXTRA_TYPE igraph_i_ncol_parsedata_t* +#define YY_USER_ACTION yylloc->first_line = yylineno; +#define YY_FATAL_ERROR(msg) IGRAPH_FATAL("Error in NCOL parser: " # msg) +#ifdef USING_R +#define fprintf(file, msg, ...) (1) +#ifdef stdout +# undef stdout +#endif +#define stdout 0 +#endif +#line 722 "src/core/io/ncol-lexer.c" +#define YY_NO_INPUT 1 +#line 724 "src/core/io/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 +#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; + int yy_n_chars; + int 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 yylex_init (yyscan_t* scanner); + +int 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 yylex_destroy ( yyscan_t yyscanner ); + +int yyget_debug ( yyscan_t yyscanner ); + +void yyset_debug ( int debug_flag , yyscan_t yyscanner ); + +YY_EXTRA_TYPE yyget_extra ( yyscan_t yyscanner ); + +void yyset_extra ( YY_EXTRA_TYPE user_defined , yyscan_t yyscanner ); + +FILE *yyget_in ( yyscan_t yyscanner ); + +void yyset_in ( FILE * _in_str , yyscan_t yyscanner ); + +FILE *yyget_out ( yyscan_t yyscanner ); + +void yyset_out ( FILE * _out_str , yyscan_t yyscanner ); + + yy_size_t yyget_leng ( yyscan_t yyscanner ); + +char *yyget_text ( yyscan_t yyscanner ); + +int yyget_lineno ( yyscan_t yyscanner ); + +void yyset_lineno ( int _line_number , yyscan_t yyscanner ); + +int yyget_column ( yyscan_t yyscanner ); + +void yyset_column ( int _column_no , yyscan_t yyscanner ); + +YYSTYPE * yyget_lval ( yyscan_t yyscanner ); + +void yyset_lval ( YYSTYPE * yylval_param , yyscan_t yyscanner ); + + YYLTYPE *yyget_lloc ( yyscan_t yyscanner ); + + void 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 yywrap ( yyscan_t yyscanner ); +#else +extern int yywrap ( yyscan_t yyscanner ); +#endif +#endif + +#ifndef YY_NO_UNPUT + +#endif + +#ifndef yytext_ptr +static void yy_flex_strncpy ( char *, const char *, int , yyscan_t yyscanner); +#endif + +#ifdef YY_NEED_STRLEN +static int yy_flex_strlen ( const 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 +#ifdef __ia64__ +/* On IA-64, the buffer size is 16k, not 8k */ +#define YY_READ_BUF_SIZE 16384 +#else +#define YY_READ_BUF_SIZE 8192 +#endif /* __ia64__ */ +#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 do { if (fwrite( yytext, (size_t) yyleng, 1, yyout )) {} } while (0) +#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 = '*'; \ + int 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 = (int) fread(buf, 1, (yy_size_t) 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 yylex \ + (YYSTYPE * yylval_param, YYLTYPE * yylloc_param , yyscan_t yyscanner); + +#define YY_DECL int 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 /*LINTED*/break; +#endif + +#define YY_RULE_SETUP \ + YY_USER_ACTION + +/** The main scanner function which does all the work. + */ +YY_DECL +{ + yy_state_type yy_current_state; + char *yy_cp, *yy_bp; + int yy_act; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + 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 ) { + yyensure_buffer_stack (yyscanner); + YY_CURRENT_BUFFER_LVALUE = + yy_create_buffer( yyin, YY_BUF_SIZE , yyscanner); + } + + yy_load_buffer_state( yyscanner ); + } + + { +#line 76 "src/core/io/ncol-lexer.l" + + +#line 79 "src/core/io/ncol-lexer.l" + /* ------------------------------------------------whitespace------*/ +#line 1011 "src/core/io/ncol-lexer.c" + + while ( /*CONSTCOND*/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 + { + 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[yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + 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/core/io/ncol-lexer.l" +{ } + YY_BREAK +/* ---------------------------------------------------newline------*/ +case 2: +/* rule 2 can match eol */ +YY_RULE_SETUP +#line 83 "src/core/io/ncol-lexer.l" +{ return NEWLINE; } + YY_BREAK +/* ----------------------------------------------alphanumeric------*/ +case 3: +YY_RULE_SETUP +#line 86 "src/core/io/ncol-lexer.l" +{ return ALNUM; } + YY_BREAK +case YY_STATE_EOF(INITIAL): +#line 88 "src/core/io/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/core/io/ncol-lexer.l" +{ return ERROR; } + YY_BREAK +case 5: +YY_RULE_SETUP +#line 99 "src/core/io/ncol-lexer.l" +YY_FATAL_ERROR( "flex scanner jammed" ); + YY_BREAK +#line 1107 "src/core/io/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 + * 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 ( 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 user's declarations */ +} /* end of 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; + char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf; + char *source = yyg->yytext_ptr; + 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 + { + int 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_LVALUE; + + int yy_c_buf_p_offset = + (int) (yyg->yy_c_buf_p - b->yy_ch_buf); + + if ( b->yy_is_our_buffer ) + { + int 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. */ + yyrealloc( (void *) b->yy_ch_buf, + (yy_size_t) (b->yy_buf_size + 2) , yyscanner ); + } + else + /* Can't grow it, we don't own it. */ + b->yy_ch_buf = NULL; + + 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; + 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 ((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. */ + int new_size = yyg->yy_n_chars + number_to_move + (yyg->yy_n_chars >> 1); + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) yyrealloc( + (void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf, (yy_size_t) new_size , yyscanner ); + if ( ! YY_CURRENT_BUFFER_LVALUE->yy_ch_buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_get_next_buffer()" ); + /* "- 2" to take care of EOB's */ + YY_CURRENT_BUFFER_LVALUE->yy_buf_size = (int) (new_size - 2); + } + + 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) +{ + yy_state_type yy_current_state; + 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 ) + { + YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 5); + 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[yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + 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) +{ + int yy_is_jam; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* This var may be unused depending upon options. */ + char *yy_cp = yyg->yy_c_buf_p; + + YY_CHAR yy_c = 5; + 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[yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c]; + yy_is_jam = (yy_current_state == 12); + + (void)yyg; + return yy_is_jam ? 0 : yy_current_state; +} + +#ifndef YY_NO_UNPUT + +#endif + +#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 */ + int offset = (int) (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. */ + yyrestart( yyin , yyscanner); + + /*FALLTHROUGH*/ + + case EOB_ACT_END_OF_FILE: + { + if ( 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 yyrestart (FILE * input_file , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + if ( ! YY_CURRENT_BUFFER ){ + yyensure_buffer_stack (yyscanner); + YY_CURRENT_BUFFER_LVALUE = + yy_create_buffer( yyin, YY_BUF_SIZE , yyscanner); + } + + yy_init_buffer( YY_CURRENT_BUFFER, input_file , yyscanner); + yy_load_buffer_state( yyscanner ); +} + +/** Switch to a different input buffer. + * @param new_buffer The new input buffer. + * @param yyscanner The scanner object. + */ + void 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 + * yypop_buffer_state(); + * yypush_buffer_state(new_buffer); + */ + 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; + yy_load_buffer_state( yyscanner ); + + /* We don't actually know whether we did this switch during + * EOF (yywrap()) processing, but the only time this flag + * is looked at is after yywrap() is called, so it's safe + * to go ahead and always set it. + */ + yyg->yy_did_buffer_switch_on_eof = 1; +} + +static void 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 yy_create_buffer (FILE * file, int size , yyscan_t yyscanner) +{ + YY_BUFFER_STATE b; + + b = (YY_BUFFER_STATE) yyalloc( sizeof( struct yy_buffer_state ) , yyscanner ); + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in 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 *) yyalloc( (yy_size_t) (b->yy_buf_size + 2) , yyscanner ); + if ( ! b->yy_ch_buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); + + b->yy_is_our_buffer = 1; + + yy_init_buffer( b, file , yyscanner); + + return b; +} + +/** Destroy the buffer. + * @param b a buffer created with yy_create_buffer() + * @param yyscanner The scanner object. + */ + void 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 ) + yyfree( (void *) b->yy_ch_buf , yyscanner ); + + yyfree( (void *) b , yyscanner ); +} + +/* Initializes or reinitializes a buffer. + * This function is sometimes called more than once on the same buffer, + * such as during a yyrestart() or at EOF. + */ + static void yy_init_buffer (YY_BUFFER_STATE b, FILE * file , yyscan_t yyscanner) + +{ + int oerrno = errno; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + yy_flush_buffer( b , yyscanner); + + b->yy_input_file = file; + b->yy_fill_buffer = 1; + + /* If b is the current buffer, then yy_init_buffer was _probably_ + * called from 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 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 ) + 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 yypush_buffer_state (YY_BUFFER_STATE new_buffer , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + if (new_buffer == NULL) + return; + + yyensure_buffer_stack(yyscanner); + + /* This block is copied from 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 yy_switch_to_buffer. */ + 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 yypop_buffer_state (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + if (!YY_CURRENT_BUFFER) + return; + + 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) { + 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 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; /* After all that talk, this was set to 1 anyways... */ + yyg->yy_buffer_stack = (struct yy_buffer_state**)yyalloc + (num_to_alloc * sizeof(struct yy_buffer_state*) + , yyscanner); + if ( ! yyg->yy_buffer_stack ) + YY_FATAL_ERROR( "out of dynamic memory in 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. */ + yy_size_t grow_size = 8 /* arbitrary grow size */; + + num_to_alloc = yyg->yy_buffer_stack_max + grow_size; + yyg->yy_buffer_stack = (struct yy_buffer_state**)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 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 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 NULL; + + b = (YY_BUFFER_STATE) yyalloc( sizeof( struct yy_buffer_state ) , yyscanner ); + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in yy_scan_buffer()" ); + + b->yy_buf_size = (int) (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 = NULL; + 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; + + yy_switch_to_buffer( b , yyscanner ); + + return b; +} + +/** Setup the input buffer state to scan a string. The next call to 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 + * yy_scan_bytes() instead. + */ +YY_BUFFER_STATE yy_scan_string (const char * yystr , yyscan_t yyscanner) +{ + + return yy_scan_bytes( yystr, (int) strlen(yystr) , yyscanner); +} + +/** Setup the input buffer state to scan the given bytes. The next call to yylex() will + * scan from a @e copy of @a bytes. + * @param yybytes the byte buffer to scan + * @param _yybytes_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 yy_scan_bytes (const char * yybytes, int _yybytes_len , yyscan_t yyscanner) +{ + YY_BUFFER_STATE b; + char *buf; + yy_size_t n; + int i; + + /* Get memory for full buffer, including space for trailing EOB's. */ + n = (yy_size_t) (_yybytes_len + 2); + buf = (char *) yyalloc( n , yyscanner ); + if ( ! buf ) + YY_FATAL_ERROR( "out of dynamic memory in 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 = yy_scan_buffer( buf, n , yyscanner); + if ( ! b ) + YY_FATAL_ERROR( "bad buffer in 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 __attribute__((unused)) yy_fatal_error (const char* msg , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + (void)yyg; + fprintf( stderr, "%s\n", msg ); +} + +/* 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 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 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 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 *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 *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 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 *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 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 line number + * @param yyscanner The scanner object. + */ +void 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( "yyset_lineno called with no buffer" ); + + yylineno = _line_number; +} + +/** Set the current column. + * @param _column_no column number + * @param yyscanner The scanner object. + */ +void 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( "yyset_column called with no buffer" ); + + 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 yy_switch_to_buffer + */ +void yyset_in (FILE * _in_str , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yyin = _in_str ; +} + +void yyset_out (FILE * _out_str , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yyout = _out_str ; +} + +int yyget_debug (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yy_flex_debug; +} + +void 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 * yyget_lval (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yylval; +} + +void yyset_lval (YYSTYPE * yylval_param , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yylval = yylval_param; +} + +YYLTYPE *yyget_lloc (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yylloc; +} + +void yyset_lloc (YYLTYPE * yylloc_param , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yylloc = yylloc_param; +} + +/* User-visible API */ + +/* 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 yylex_init(yyscan_t* ptr_yy_globals) +{ + if (ptr_yy_globals == NULL){ + errno = EINVAL; + return 1; + } + + *ptr_yy_globals = (yyscan_t) 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 ); +} + +/* yylex_init_extra has the same functionality as 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 yyalloc in + * the yyextra field. + */ +int yylex_init_extra( YY_EXTRA_TYPE yy_user_defined, yyscan_t* ptr_yy_globals ) +{ + struct yyguts_t dummy_yyguts; + + yyset_extra (yy_user_defined, &dummy_yyguts); + + if (ptr_yy_globals == NULL){ + errno = EINVAL; + return 1; + } + + *ptr_yy_globals = (yyscan_t) 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)); + + 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 yylex_destroy(), so don't allocate here. + */ + + yyg->yy_buffer_stack = NULL; + yyg->yy_buffer_stack_top = 0; + yyg->yy_buffer_stack_max = 0; + yyg->yy_c_buf_p = NULL; + 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 = NULL; + yyout = NULL; +#endif + + /* For future reference: Set errno on error, since we are called by + * yylex_init() + */ + return 0; +} + +/* yylex_destroy is for both reentrant and non-reentrant scanners. */ +int yylex_destroy (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + /* Pop the buffer stack, destroying each element. */ + while(YY_CURRENT_BUFFER){ + yy_delete_buffer( YY_CURRENT_BUFFER , yyscanner ); + YY_CURRENT_BUFFER_LVALUE = NULL; + yypop_buffer_state(yyscanner); + } + + /* Destroy the stack itself. */ + yyfree(yyg->yy_buffer_stack , yyscanner); + yyg->yy_buffer_stack = NULL; + + /* Destroy the start condition stack. */ + 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 + * yylex() is called, initialization will occur. */ + yy_init_globals( yyscanner); + + /* Destroy the main struct (reentrant only). */ + yyfree ( yyscanner , yyscanner ); + yyscanner = NULL; + return 0; +} + +/* + * Internal utility routines. + */ + +#ifndef yytext_ptr +static void yy_flex_strncpy (char* s1, const char * s2, int n , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + (void)yyg; + + int i; + for ( i = 0; i < n; ++i ) + s1[i] = s2[i]; +} +#endif + +#ifdef YY_NEED_STRLEN +static int yy_flex_strlen (const char * s , yyscan_t yyscanner) +{ + int n; + for ( n = 0; s[n]; ++n ) + ; + + return n; +} +#endif + +void *yyalloc (yy_size_t size , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + (void)yyg; + return malloc(size); +} + +void *yyrealloc (void * ptr, yy_size_t size , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + (void)yyg; + + /* 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 realloc(ptr, size); +} + +void yyfree (void * ptr , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + (void)yyg; + free( (char *) ptr ); /* see yyrealloc() for (char *) cast */ +} + +#define YYTABLES_NAME "yytables" + +#line 99 "src/core/io/ncol-lexer.l" + + diff --git a/src/rigraph/core/io/ncol-lexer.h b/src/rigraph/core/io/ncol-lexer.h new file mode 100644 index 0000000..2dbaa76 --- /dev/null +++ b/src/rigraph/core/io/ncol-lexer.h @@ -0,0 +1,736 @@ +#ifndef igraph_ncol_yyHEADER_H +#define igraph_ncol_yyHEADER_H 1 +#define igraph_ncol_yyIN_HEADER 1 + +#line 6 "src/core/io/ncol-lexer.h" + +#line 8 "src/core/io/ncol-lexer.h" + +#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 6 +#define YY_FLEX_SUBMINOR_VERSION 4 +#if YY_FLEX_SUBMINOR_VERSION > 0 +#define FLEX_BETA +#endif + +#ifdef yy_create_buffer +#define igraph_ncol_yy_create_buffer_ALREADY_DEFINED +#else +#define yy_create_buffer igraph_ncol_yy_create_buffer +#endif + +#ifdef yy_delete_buffer +#define igraph_ncol_yy_delete_buffer_ALREADY_DEFINED +#else +#define yy_delete_buffer igraph_ncol_yy_delete_buffer +#endif + +#ifdef yy_scan_buffer +#define igraph_ncol_yy_scan_buffer_ALREADY_DEFINED +#else +#define yy_scan_buffer igraph_ncol_yy_scan_buffer +#endif + +#ifdef yy_scan_string +#define igraph_ncol_yy_scan_string_ALREADY_DEFINED +#else +#define yy_scan_string igraph_ncol_yy_scan_string +#endif + +#ifdef yy_scan_bytes +#define igraph_ncol_yy_scan_bytes_ALREADY_DEFINED +#else +#define yy_scan_bytes igraph_ncol_yy_scan_bytes +#endif + +#ifdef yy_init_buffer +#define igraph_ncol_yy_init_buffer_ALREADY_DEFINED +#else +#define yy_init_buffer igraph_ncol_yy_init_buffer +#endif + +#ifdef yy_flush_buffer +#define igraph_ncol_yy_flush_buffer_ALREADY_DEFINED +#else +#define yy_flush_buffer igraph_ncol_yy_flush_buffer +#endif + +#ifdef yy_load_buffer_state +#define igraph_ncol_yy_load_buffer_state_ALREADY_DEFINED +#else +#define yy_load_buffer_state igraph_ncol_yy_load_buffer_state +#endif + +#ifdef yy_switch_to_buffer +#define igraph_ncol_yy_switch_to_buffer_ALREADY_DEFINED +#else +#define yy_switch_to_buffer igraph_ncol_yy_switch_to_buffer +#endif + +#ifdef yypush_buffer_state +#define igraph_ncol_yypush_buffer_state_ALREADY_DEFINED +#else +#define yypush_buffer_state igraph_ncol_yypush_buffer_state +#endif + +#ifdef yypop_buffer_state +#define igraph_ncol_yypop_buffer_state_ALREADY_DEFINED +#else +#define yypop_buffer_state igraph_ncol_yypop_buffer_state +#endif + +#ifdef yyensure_buffer_stack +#define igraph_ncol_yyensure_buffer_stack_ALREADY_DEFINED +#else +#define yyensure_buffer_stack igraph_ncol_yyensure_buffer_stack +#endif + +#ifdef yylex +#define igraph_ncol_yylex_ALREADY_DEFINED +#else +#define yylex igraph_ncol_yylex +#endif + +#ifdef yyrestart +#define igraph_ncol_yyrestart_ALREADY_DEFINED +#else +#define yyrestart igraph_ncol_yyrestart +#endif + +#ifdef yylex_init +#define igraph_ncol_yylex_init_ALREADY_DEFINED +#else +#define yylex_init igraph_ncol_yylex_init +#endif + +#ifdef yylex_init_extra +#define igraph_ncol_yylex_init_extra_ALREADY_DEFINED +#else +#define yylex_init_extra igraph_ncol_yylex_init_extra +#endif + +#ifdef yylex_destroy +#define igraph_ncol_yylex_destroy_ALREADY_DEFINED +#else +#define yylex_destroy igraph_ncol_yylex_destroy +#endif + +#ifdef yyget_debug +#define igraph_ncol_yyget_debug_ALREADY_DEFINED +#else +#define yyget_debug igraph_ncol_yyget_debug +#endif + +#ifdef yyset_debug +#define igraph_ncol_yyset_debug_ALREADY_DEFINED +#else +#define yyset_debug igraph_ncol_yyset_debug +#endif + +#ifdef yyget_extra +#define igraph_ncol_yyget_extra_ALREADY_DEFINED +#else +#define yyget_extra igraph_ncol_yyget_extra +#endif + +#ifdef yyset_extra +#define igraph_ncol_yyset_extra_ALREADY_DEFINED +#else +#define yyset_extra igraph_ncol_yyset_extra +#endif + +#ifdef yyget_in +#define igraph_ncol_yyget_in_ALREADY_DEFINED +#else +#define yyget_in igraph_ncol_yyget_in +#endif + +#ifdef yyset_in +#define igraph_ncol_yyset_in_ALREADY_DEFINED +#else +#define yyset_in igraph_ncol_yyset_in +#endif + +#ifdef yyget_out +#define igraph_ncol_yyget_out_ALREADY_DEFINED +#else +#define yyget_out igraph_ncol_yyget_out +#endif + +#ifdef yyset_out +#define igraph_ncol_yyset_out_ALREADY_DEFINED +#else +#define yyset_out igraph_ncol_yyset_out +#endif + +#ifdef yyget_leng +#define igraph_ncol_yyget_leng_ALREADY_DEFINED +#else +#define yyget_leng igraph_ncol_yyget_leng +#endif + +#ifdef yyget_text +#define igraph_ncol_yyget_text_ALREADY_DEFINED +#else +#define yyget_text igraph_ncol_yyget_text +#endif + +#ifdef yyget_lineno +#define igraph_ncol_yyget_lineno_ALREADY_DEFINED +#else +#define yyget_lineno igraph_ncol_yyget_lineno +#endif + +#ifdef yyset_lineno +#define igraph_ncol_yyset_lineno_ALREADY_DEFINED +#else +#define yyset_lineno igraph_ncol_yyset_lineno +#endif + +#ifdef yyget_column +#define igraph_ncol_yyget_column_ALREADY_DEFINED +#else +#define yyget_column igraph_ncol_yyget_column +#endif + +#ifdef yyset_column +#define igraph_ncol_yyset_column_ALREADY_DEFINED +#else +#define yyset_column igraph_ncol_yyset_column +#endif + +#ifdef yywrap +#define igraph_ncol_yywrap_ALREADY_DEFINED +#else +#define yywrap igraph_ncol_yywrap +#endif + +#ifdef yyget_lval +#define igraph_ncol_yyget_lval_ALREADY_DEFINED +#else +#define yyget_lval igraph_ncol_yyget_lval +#endif + +#ifdef yyset_lval +#define igraph_ncol_yyset_lval_ALREADY_DEFINED +#else +#define yyset_lval igraph_ncol_yyset_lval +#endif + +#ifdef yyget_lloc +#define igraph_ncol_yyget_lloc_ALREADY_DEFINED +#else +#define yyget_lloc igraph_ncol_yyget_lloc +#endif + +#ifdef yyset_lloc +#define igraph_ncol_yyset_lloc_ALREADY_DEFINED +#else +#define yyset_lloc igraph_ncol_yyset_lloc +#endif + +#ifdef yyalloc +#define igraph_ncol_yyalloc_ALREADY_DEFINED +#else +#define yyalloc igraph_ncol_yyalloc +#endif + +#ifdef yyrealloc +#define igraph_ncol_yyrealloc_ALREADY_DEFINED +#else +#define yyrealloc igraph_ncol_yyrealloc +#endif + +#ifdef yyfree +#define igraph_ncol_yyfree_ALREADY_DEFINED +#else +#define yyfree igraph_ncol_yyfree +#endif + +/* First, we deal with platform-specific or compiler-specific issues. */ + +/* begin standard C headers. */ +#include +#include +#include +#include + +/* end standard C headers. */ + +/* flex integer type definitions */ + +#ifndef FLEXINT_H +#define FLEXINT_H + +/* C99 systems have . 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 +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; +#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; + +/* 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 + +#ifndef SIZE_MAX +#define SIZE_MAX (~(size_t)0) +#endif + +#endif /* ! C99 */ + +#endif /* ! FLEXINT_H */ + +/* begin standard C++ headers. */ + +/* TODO: this is always defined, so inline it */ +#define yyconst const + +#if defined(__GNUC__) && __GNUC__ >= 3 +#define yynoreturn __attribute__((__noreturn__)) +#else +#define yynoreturn +#endif + +/* 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 + +/* Size of default input buffer. */ +#ifndef YY_BUF_SIZE +#ifdef __ia64__ +/* On IA-64, the buffer size is 16k, not 8k. + * Moreover, YY_BUF_SIZE is 2*YY_READ_BUF_SIZE in the general case. + * Ditto for the __ia64__ case accordingly. + */ +#define YY_BUF_SIZE 32768 +#else +#define YY_BUF_SIZE 16384 +#endif /* __ia64__ */ +#endif + +#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 + +#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. + */ + int yy_buf_size; + + /* Number of characters read into yy_ch_buf, not including EOB + * characters. + */ + int 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; + + }; +#endif /* !YY_STRUCT_YY_BUFFER_STATE */ + +void yyrestart ( FILE *input_file , yyscan_t yyscanner ); +void yy_switch_to_buffer ( YY_BUFFER_STATE new_buffer , yyscan_t yyscanner ); +YY_BUFFER_STATE yy_create_buffer ( FILE *file, int size , yyscan_t yyscanner ); +void yy_delete_buffer ( YY_BUFFER_STATE b , yyscan_t yyscanner ); +void yy_flush_buffer ( YY_BUFFER_STATE b , yyscan_t yyscanner ); +void yypush_buffer_state ( YY_BUFFER_STATE new_buffer , yyscan_t yyscanner ); +void yypop_buffer_state ( yyscan_t yyscanner ); + +YY_BUFFER_STATE yy_scan_buffer ( char *base, yy_size_t size , yyscan_t yyscanner ); +YY_BUFFER_STATE yy_scan_string ( const char *yy_str , yyscan_t yyscanner ); +YY_BUFFER_STATE yy_scan_bytes ( const char *bytes, int len , yyscan_t yyscanner ); + +void *yyalloc ( yy_size_t , yyscan_t yyscanner ); +void *yyrealloc ( void *, yy_size_t , yyscan_t yyscanner ); +void yyfree ( void * , yyscan_t yyscanner ); + +/* Begin user sect3 */ + +#define igraph_ncol_yywrap(yyscanner) (/*CONSTCOND*/1) +#define YY_SKIP_YYWRAP + +#define yytext_ptr yytext_r + +#ifdef YY_HEADER_EXPORT_START_CONDITIONS +#define INITIAL 0 + +#endif + +#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 +#endif + +#ifndef YY_EXTRA_TYPE +#define YY_EXTRA_TYPE void * +#endif + +int yylex_init (yyscan_t* scanner); + +int 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 yylex_destroy ( yyscan_t yyscanner ); + +int yyget_debug ( yyscan_t yyscanner ); + +void yyset_debug ( int debug_flag , yyscan_t yyscanner ); + +YY_EXTRA_TYPE yyget_extra ( yyscan_t yyscanner ); + +void yyset_extra ( YY_EXTRA_TYPE user_defined , yyscan_t yyscanner ); + +FILE *yyget_in ( yyscan_t yyscanner ); + +void yyset_in ( FILE * _in_str , yyscan_t yyscanner ); + +FILE *yyget_out ( yyscan_t yyscanner ); + +void yyset_out ( FILE * _out_str , yyscan_t yyscanner ); + + int yyget_leng ( yyscan_t yyscanner ); + +char *yyget_text ( yyscan_t yyscanner ); + +int yyget_lineno ( yyscan_t yyscanner ); + +void yyset_lineno ( int _line_number , yyscan_t yyscanner ); + +int yyget_column ( yyscan_t yyscanner ); + +void yyset_column ( int _column_no , yyscan_t yyscanner ); + +YYSTYPE * yyget_lval ( yyscan_t yyscanner ); + +void yyset_lval ( YYSTYPE * yylval_param , yyscan_t yyscanner ); + + YYLTYPE *yyget_lloc ( yyscan_t yyscanner ); + + void 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 yywrap ( yyscan_t yyscanner ); +#else +extern int yywrap ( yyscan_t yyscanner ); +#endif +#endif + +#ifndef yytext_ptr +static void yy_flex_strncpy ( char *, const char *, int , yyscan_t yyscanner); +#endif + +#ifdef YY_NEED_STRLEN +static int yy_flex_strlen ( const char * , yyscan_t yyscanner); +#endif + +#ifndef YY_NO_INPUT + +#endif + +/* Amount of stuff to slurp up with each read. */ +#ifndef YY_READ_BUF_SIZE +#ifdef __ia64__ +/* On IA-64, the buffer size is 16k, not 8k */ +#define YY_READ_BUF_SIZE 16384 +#else +#define YY_READ_BUF_SIZE 8192 +#endif /* __ia64__ */ +#endif + +/* Number of entries by which start-condition stack grows. */ +#ifndef YY_START_STACK_INCR +#define YY_START_STACK_INCR 25 +#endif + +/* 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 yylex \ + (YYSTYPE * yylval_param, YYLTYPE * yylloc_param , yyscan_t yyscanner); + +#define YY_DECL int yylex \ + (YYSTYPE * yylval_param, YYLTYPE * yylloc_param , yyscan_t yyscanner) +#endif /* !YY_DECL */ + +/* yy_get_previous_state - get the state just before the EOB char was reached */ + +#undef YY_NEW_FILE +#undef YY_FLUSH_BUFFER +#undef yy_set_bol +#undef yy_new_buffer +#undef yy_set_interactive +#undef YY_DO_BEFORE_ACTION + +#ifdef YY_DECL_IS_OURS +#undef YY_DECL_IS_OURS +#undef YY_DECL +#endif + +#ifndef igraph_ncol_yy_create_buffer_ALREADY_DEFINED +#undef yy_create_buffer +#endif +#ifndef igraph_ncol_yy_delete_buffer_ALREADY_DEFINED +#undef yy_delete_buffer +#endif +#ifndef igraph_ncol_yy_scan_buffer_ALREADY_DEFINED +#undef yy_scan_buffer +#endif +#ifndef igraph_ncol_yy_scan_string_ALREADY_DEFINED +#undef yy_scan_string +#endif +#ifndef igraph_ncol_yy_scan_bytes_ALREADY_DEFINED +#undef yy_scan_bytes +#endif +#ifndef igraph_ncol_yy_init_buffer_ALREADY_DEFINED +#undef yy_init_buffer +#endif +#ifndef igraph_ncol_yy_flush_buffer_ALREADY_DEFINED +#undef yy_flush_buffer +#endif +#ifndef igraph_ncol_yy_load_buffer_state_ALREADY_DEFINED +#undef yy_load_buffer_state +#endif +#ifndef igraph_ncol_yy_switch_to_buffer_ALREADY_DEFINED +#undef yy_switch_to_buffer +#endif +#ifndef igraph_ncol_yypush_buffer_state_ALREADY_DEFINED +#undef yypush_buffer_state +#endif +#ifndef igraph_ncol_yypop_buffer_state_ALREADY_DEFINED +#undef yypop_buffer_state +#endif +#ifndef igraph_ncol_yyensure_buffer_stack_ALREADY_DEFINED +#undef yyensure_buffer_stack +#endif +#ifndef igraph_ncol_yylex_ALREADY_DEFINED +#undef yylex +#endif +#ifndef igraph_ncol_yyrestart_ALREADY_DEFINED +#undef yyrestart +#endif +#ifndef igraph_ncol_yylex_init_ALREADY_DEFINED +#undef yylex_init +#endif +#ifndef igraph_ncol_yylex_init_extra_ALREADY_DEFINED +#undef yylex_init_extra +#endif +#ifndef igraph_ncol_yylex_destroy_ALREADY_DEFINED +#undef yylex_destroy +#endif +#ifndef igraph_ncol_yyget_debug_ALREADY_DEFINED +#undef yyget_debug +#endif +#ifndef igraph_ncol_yyset_debug_ALREADY_DEFINED +#undef yyset_debug +#endif +#ifndef igraph_ncol_yyget_extra_ALREADY_DEFINED +#undef yyget_extra +#endif +#ifndef igraph_ncol_yyset_extra_ALREADY_DEFINED +#undef yyset_extra +#endif +#ifndef igraph_ncol_yyget_in_ALREADY_DEFINED +#undef yyget_in +#endif +#ifndef igraph_ncol_yyset_in_ALREADY_DEFINED +#undef yyset_in +#endif +#ifndef igraph_ncol_yyget_out_ALREADY_DEFINED +#undef yyget_out +#endif +#ifndef igraph_ncol_yyset_out_ALREADY_DEFINED +#undef yyset_out +#endif +#ifndef igraph_ncol_yyget_leng_ALREADY_DEFINED +#undef yyget_leng +#endif +#ifndef igraph_ncol_yyget_text_ALREADY_DEFINED +#undef yyget_text +#endif +#ifndef igraph_ncol_yyget_lineno_ALREADY_DEFINED +#undef yyget_lineno +#endif +#ifndef igraph_ncol_yyset_lineno_ALREADY_DEFINED +#undef yyset_lineno +#endif +#ifndef igraph_ncol_yyget_column_ALREADY_DEFINED +#undef yyget_column +#endif +#ifndef igraph_ncol_yyset_column_ALREADY_DEFINED +#undef yyset_column +#endif +#ifndef igraph_ncol_yywrap_ALREADY_DEFINED +#undef yywrap +#endif +#ifndef igraph_ncol_yyget_lval_ALREADY_DEFINED +#undef yyget_lval +#endif +#ifndef igraph_ncol_yyset_lval_ALREADY_DEFINED +#undef yyset_lval +#endif +#ifndef igraph_ncol_yyget_lloc_ALREADY_DEFINED +#undef yyget_lloc +#endif +#ifndef igraph_ncol_yyset_lloc_ALREADY_DEFINED +#undef yyset_lloc +#endif +#ifndef igraph_ncol_yyalloc_ALREADY_DEFINED +#undef yyalloc +#endif +#ifndef igraph_ncol_yyrealloc_ALREADY_DEFINED +#undef yyrealloc +#endif +#ifndef igraph_ncol_yyfree_ALREADY_DEFINED +#undef yyfree +#endif +#ifndef igraph_ncol_yytext_ALREADY_DEFINED +#undef yytext +#endif +#ifndef igraph_ncol_yyleng_ALREADY_DEFINED +#undef yyleng +#endif +#ifndef igraph_ncol_yyin_ALREADY_DEFINED +#undef yyin +#endif +#ifndef igraph_ncol_yyout_ALREADY_DEFINED +#undef yyout +#endif +#ifndef igraph_ncol_yy_flex_debug_ALREADY_DEFINED +#undef yy_flex_debug +#endif +#ifndef igraph_ncol_yylineno_ALREADY_DEFINED +#undef yylineno +#endif +#ifndef igraph_ncol_yytables_fload_ALREADY_DEFINED +#undef yytables_fload +#endif +#ifndef igraph_ncol_yytables_destroy_ALREADY_DEFINED +#undef yytables_destroy +#endif +#ifndef igraph_ncol_yyTABLES_NAME_ALREADY_DEFINED +#undef yyTABLES_NAME +#endif + +#line 99 "src/core/io/ncol-lexer.l" + + +#line 735 "src/core/io/ncol-lexer.h" +#undef igraph_ncol_yyIN_HEADER +#endif /* igraph_ncol_yyHEADER_H */ diff --git a/src/rigraph/core/io/ncol-lexer.l b/src/rigraph/core/io/ncol-lexer.l new file mode 100644 index 0000000..a2ed8d3 --- /dev/null +++ b/src/rigraph/core/io/ncol-lexer.l @@ -0,0 +1,98 @@ +/* + IGraph library. + Copyright (C) 2006-2012 Gabor Csardi + 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 + +*/ + +%{ + +/* + IGraph library. + Copyright (C) 2006-2012 Gabor Csardi + 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 + +#include "io/ncol-header.h" +#include "io/parsers/ncol-parser.h" + +#define YY_EXTRA_TYPE igraph_i_ncol_parsedata_t* +#define YY_USER_ACTION yylloc->first_line = yylineno; +#define YY_FATAL_ERROR(msg) IGRAPH_FATAL("Error in NCOL parser: " # msg) +#ifdef USING_R +#define fprintf(file, msg, ...) (1) +#ifdef stdout +# undef stdout +#endif +#define stdout 0 +#endif +%} + +%option noyywrap +%option prefix="igraph_ncol_yy" +%option nounput +%option noinput +%option nodefault +%option reentrant +%option bison-bridge +%option bison-locations + +alnum [^ \t\n\r\0] + +%% + + /* ------------------------------------------------whitespace------*/ +[ \t]+ { } + + /* ---------------------------------------------------newline------*/ +\n\r|\r\n|\n|\r { return NEWLINE; } + + /* ----------------------------------------------alphanumeric------*/ +{alnum}+ { return ALNUM; } + +<> { if (yyextra->eof) { + yyterminate(); + } else { + yyextra->eof=1; + return NEWLINE; + } + } + + /* ---------------------------------------------anything else------*/ +. { return ERROR; } + +%% diff --git a/src/rigraph/core/io/ncol-parser.c b/src/rigraph/core/io/ncol-parser.c new file mode 100644 index 0000000..e54271f --- /dev/null +++ b/src/rigraph/core/io/ncol-parser.c @@ -0,0 +1,1764 @@ +/* A Bison parser, made by GNU Bison 3.5.1. */ + +/* Bison implementation for Yacc-like parsers in C + + Copyright (C) 1984, 1989-1990, 2000-2015, 2018-2020 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 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 . */ + +/* 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. */ + +/* Undocumented macros, especially those whose name start with YY_, + are private implementation details. Do not rely on them. */ + +/* Identify Bison output. */ +#define YYBISON 1 + +/* Bison version. */ +#define YYBISON_VERSION "3.5.1" + +/* Skeleton name. */ +#define YYSKELETON_NAME "yacc.c" + +/* Pure parsers. */ +#define YYPURE 1 + +/* Push parsers. */ +#define YYPUSH 0 + +/* Pull parsers. */ +#define YYPULL 1 + + +/* Substitute the variable and function names. */ +#define yyparse igraph_ncol_yyparse +#define yylex igraph_ncol_yylex +#define yyerror igraph_ncol_yyerror +#define yydebug igraph_ncol_yydebug +#define yynerrs igraph_ncol_yynerrs + +/* First part of user prologue. */ +#line 23 "src/core/io/ncol-parser.y" + + +/* + IGraph library. + Copyright (C) 2006-2012 Gabor Csardi + 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 +#include + +#include "igraph_types.h" +#include "igraph_memory.h" +#include "igraph_error.h" +#include "config.h" + +#include "core/math.h" +#include "io/ncol-header.h" +#include "io/ncol-parser.h" +#include "io/ncol-lexer.h" +#include "internal/hacks.h" + +int igraph_ncol_yyerror(YYLTYPE* locp, + igraph_i_ncol_parsedata_t *context, + const char *s); +igraph_real_t igraph_ncol_get_number(const char *str, long int len); + +#define scanner context->scanner + +#line 121 "yy.tab.c" + +# ifndef YY_CAST +# ifdef __cplusplus +# define YY_CAST(Type, Val) static_cast (Val) +# define YY_REINTERPRET_CAST(Type, Val) reinterpret_cast (Val) +# else +# define YY_CAST(Type, Val) ((Type) (Val)) +# define YY_REINTERPRET_CAST(Type, Val) ((Type) (Val)) +# endif +# endif +# ifndef YY_NULLPTR +# if defined __cplusplus +# if 201103L <= __cplusplus +# define YY_NULLPTR nullptr +# else +# define YY_NULLPTR 0 +# endif +# else +# define YY_NULLPTR ((void*)0) +# endif +# endif + +/* Enabling verbose error messages. */ +#ifdef YYERROR_VERBOSE +# undef YYERROR_VERBOSE +# define YYERROR_VERBOSE 1 +#else +# define YYERROR_VERBOSE 1 +#endif + +/* Use api.header.include to #include this header + instead of duplicating it here. */ +#ifndef YY_IGRAPH_NCOL_YY_YY_TAB_H_INCLUDED +# define YY_IGRAPH_NCOL_YY_YY_TAB_H_INCLUDED +/* Debug traces. */ +#ifndef YYDEBUG +# define YYDEBUG 0 +#endif +#if YYDEBUG +extern int igraph_ncol_yydebug; +#endif + +/* Token type. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + enum yytokentype + { + ALNUM = 258, + NEWLINE = 259, + ERROR = 260 + }; +#endif + +/* Value type. */ +#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED +union YYSTYPE +{ +#line 79 "src/core/io/ncol-parser.y" + + long int edgenum; + double weightnum; + +#line 184 "yy.tab.c" + +}; +typedef union YYSTYPE YYSTYPE; +# define YYSTYPE_IS_TRIVIAL 1 +# define YYSTYPE_IS_DECLARED 1 +#endif + +/* Location type. */ +#if ! defined YYLTYPE && ! defined YYLTYPE_IS_DECLARED +typedef struct YYLTYPE YYLTYPE; +struct YYLTYPE +{ + int first_line; + int first_column; + int last_line; + int last_column; +}; +# define YYLTYPE_IS_DECLARED 1 +# define YYLTYPE_IS_TRIVIAL 1 +#endif + + + +int igraph_ncol_yyparse (igraph_i_ncol_parsedata_t* context); + +#endif /* !YY_IGRAPH_NCOL_YY_YY_TAB_H_INCLUDED */ + + + +#ifdef short +# undef short +#endif + +/* On compilers that do not define __PTRDIFF_MAX__ etc., make sure + and (if available) are included + so that the code can choose integer types of a good width. */ + +#ifndef __PTRDIFF_MAX__ +# include /* INFRINGES ON USER NAME SPACE */ +# if defined __STDC_VERSION__ && 199901 <= __STDC_VERSION__ +# include /* INFRINGES ON USER NAME SPACE */ +# define YY_STDINT_H +# endif +#endif + +/* Narrow types that promote to a signed type and that can represent a + signed or unsigned integer of at least N bits. In tables they can + save space and decrease cache pressure. Promoting to a signed type + helps avoid bugs in integer arithmetic. */ + +#ifdef __INT_LEAST8_MAX__ +typedef __INT_LEAST8_TYPE__ yytype_int8; +#elif defined YY_STDINT_H +typedef int_least8_t yytype_int8; +#else +typedef signed char yytype_int8; +#endif + +#ifdef __INT_LEAST16_MAX__ +typedef __INT_LEAST16_TYPE__ yytype_int16; +#elif defined YY_STDINT_H +typedef int_least16_t yytype_int16; +#else +typedef short yytype_int16; +#endif + +#if defined __UINT_LEAST8_MAX__ && __UINT_LEAST8_MAX__ <= __INT_MAX__ +typedef __UINT_LEAST8_TYPE__ yytype_uint8; +#elif (!defined __UINT_LEAST8_MAX__ && defined YY_STDINT_H \ + && UINT_LEAST8_MAX <= INT_MAX) +typedef uint_least8_t yytype_uint8; +#elif !defined __UINT_LEAST8_MAX__ && UCHAR_MAX <= INT_MAX +typedef unsigned char yytype_uint8; +#else +typedef short yytype_uint8; +#endif + +#if defined __UINT_LEAST16_MAX__ && __UINT_LEAST16_MAX__ <= __INT_MAX__ +typedef __UINT_LEAST16_TYPE__ yytype_uint16; +#elif (!defined __UINT_LEAST16_MAX__ && defined YY_STDINT_H \ + && UINT_LEAST16_MAX <= INT_MAX) +typedef uint_least16_t yytype_uint16; +#elif !defined __UINT_LEAST16_MAX__ && USHRT_MAX <= INT_MAX +typedef unsigned short yytype_uint16; +#else +typedef int yytype_uint16; +#endif + +#ifndef YYPTRDIFF_T +# if defined __PTRDIFF_TYPE__ && defined __PTRDIFF_MAX__ +# define YYPTRDIFF_T __PTRDIFF_TYPE__ +# define YYPTRDIFF_MAXIMUM __PTRDIFF_MAX__ +# elif defined PTRDIFF_MAX +# ifndef ptrdiff_t +# include /* INFRINGES ON USER NAME SPACE */ +# endif +# define YYPTRDIFF_T ptrdiff_t +# define YYPTRDIFF_MAXIMUM PTRDIFF_MAX +# else +# define YYPTRDIFF_T long +# define YYPTRDIFF_MAXIMUM LONG_MAX +# endif +#endif + +#ifndef YYSIZE_T +# ifdef __SIZE_TYPE__ +# define YYSIZE_T __SIZE_TYPE__ +# elif defined size_t +# define YYSIZE_T size_t +# elif defined __STDC_VERSION__ && 199901 <= __STDC_VERSION__ +# include /* INFRINGES ON USER NAME SPACE */ +# define YYSIZE_T size_t +# else +# define YYSIZE_T unsigned +# endif +#endif + +#define YYSIZE_MAXIMUM \ + YY_CAST (YYPTRDIFF_T, \ + (YYPTRDIFF_MAXIMUM < YY_CAST (YYSIZE_T, -1) \ + ? YYPTRDIFF_MAXIMUM \ + : YY_CAST (YYSIZE_T, -1))) + +#define YYSIZEOF(X) YY_CAST (YYPTRDIFF_T, sizeof (X)) + +/* Stored state numbers (used for stacks). */ +typedef yytype_int8 yy_state_t; + +/* State numbers in computations. */ +typedef int yy_state_fast_t; + +#ifndef YY_ +# if defined YYENABLE_NLS && YYENABLE_NLS +# if ENABLE_NLS +# include /* INFRINGES ON USER NAME SPACE */ +# define YY_(Msgid) dgettext ("bison-runtime", Msgid) +# endif +# endif +# ifndef YY_ +# define YY_(Msgid) Msgid +# endif +#endif + +#ifndef YY_ATTRIBUTE_PURE +# if defined __GNUC__ && 2 < __GNUC__ + (96 <= __GNUC_MINOR__) +# define YY_ATTRIBUTE_PURE __attribute__ ((__pure__)) +# else +# define YY_ATTRIBUTE_PURE +# endif +#endif + +#ifndef YY_ATTRIBUTE_UNUSED +# if defined __GNUC__ && 2 < __GNUC__ + (7 <= __GNUC_MINOR__) +# define YY_ATTRIBUTE_UNUSED __attribute__ ((__unused__)) +# else +# define YY_ATTRIBUTE_UNUSED +# 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 + +#if defined __GNUC__ && ! defined __ICC && 407 <= __GNUC__ * 100 + __GNUC_MINOR__ +/* Suppress an incorrect diagnostic about yylval being uninitialized. */ +# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN \ + _Pragma ("GCC diagnostic push") \ + _Pragma ("GCC diagnostic ignored \"-Wuninitialized\"") \ + _Pragma ("GCC diagnostic ignored \"-Wmaybe-uninitialized\"") +# define YY_IGNORE_MAYBE_UNINITIALIZED_END \ + _Pragma ("GCC diagnostic pop") +#else +# define YY_INITIAL_VALUE(Value) Value +#endif +#ifndef YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN +# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN +# define YY_IGNORE_MAYBE_UNINITIALIZED_END +#endif +#ifndef YY_INITIAL_VALUE +# define YY_INITIAL_VALUE(Value) /* Nothing. */ +#endif + +#if defined __cplusplus && defined __GNUC__ && ! defined __ICC && 6 <= __GNUC__ +# define YY_IGNORE_USELESS_CAST_BEGIN \ + _Pragma ("GCC diagnostic push") \ + _Pragma ("GCC diagnostic ignored \"-Wuseless-cast\"") +# define YY_IGNORE_USELESS_CAST_END \ + _Pragma ("GCC diagnostic pop") +#endif +#ifndef YY_IGNORE_USELESS_CAST_BEGIN +# define YY_IGNORE_USELESS_CAST_BEGIN +# define YY_IGNORE_USELESS_CAST_END +#endif + + +#define YY_ASSERT(E) ((void) (0 && (E))) + +#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 /* INFRINGES ON USER NAME SPACE */ +# elif defined _AIX +# define YYSTACK_ALLOC __alloca +# elif defined _MSC_VER +# include /* INFRINGES ON USER NAME SPACE */ +# define alloca _alloca +# else +# define YYSTACK_ALLOC alloca +# if ! defined _ALLOCA_H && ! defined EXIT_SUCCESS +# include /* INFRINGES ON USER NAME SPACE */ + /* Use EXIT_SUCCESS as a witness for stdlib.h. */ +# ifndef EXIT_SUCCESS +# define EXIT_SUCCESS 0 +# endif +# endif +# endif +# endif +# endif + +# ifdef YYSTACK_ALLOC + /* Pacify GCC's 'empty if-body' warning. */ +# define YYSTACK_FREE(Ptr) do { /* empty */; } while (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 EXIT_SUCCESS \ + && ! ((defined YYMALLOC || defined malloc) \ + && (defined YYFREE || defined free))) +# include /* INFRINGES ON USER NAME SPACE */ +# ifndef EXIT_SUCCESS +# define EXIT_SUCCESS 0 +# endif +# endif +# ifndef YYMALLOC +# define YYMALLOC malloc +# if ! defined malloc && ! defined EXIT_SUCCESS +void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# ifndef YYFREE +# define YYFREE free +# if ! defined free && ! defined EXIT_SUCCESS +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 +{ + yy_state_t yyss_alloc; + YYSTYPE yyvs_alloc; + YYLTYPE yyls_alloc; +}; + +/* The size of the maximum gap between one aligned stack and the next. */ +# define YYSTACK_GAP_MAXIMUM (YYSIZEOF (union yyalloc) - 1) + +/* The size of an array large to enough to hold all stacks, each with + N elements. */ +# define YYSTACK_BYTES(N) \ + ((N) * (YYSIZEOF (yy_state_t) + YYSIZEOF (YYSTYPE) \ + + YYSIZEOF (YYLTYPE)) \ + + 2 * YYSTACK_GAP_MAXIMUM) + +# define YYCOPY_NEEDED 1 + +/* 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_alloc, Stack) \ + do \ + { \ + YYPTRDIFF_T yynewbytes; \ + YYCOPY (&yyptr->Stack_alloc, Stack, yysize); \ + Stack = &yyptr->Stack_alloc; \ + yynewbytes = yystacksize * YYSIZEOF (*Stack) + YYSTACK_GAP_MAXIMUM; \ + yyptr += yynewbytes / YYSIZEOF (*yyptr); \ + } \ + while (0) + +#endif + +#if defined YYCOPY_NEEDED && YYCOPY_NEEDED +/* Copy COUNT objects from SRC to DST. The source and destination do + not overlap. */ +# ifndef YYCOPY +# if defined __GNUC__ && 1 < __GNUC__ +# define YYCOPY(Dst, Src, Count) \ + __builtin_memcpy (Dst, Src, YY_CAST (YYSIZE_T, (Count)) * sizeof (*(Src))) +# else +# define YYCOPY(Dst, Src, Count) \ + do \ + { \ + YYPTRDIFF_T yyi; \ + for (yyi = 0; yyi < (Count); yyi++) \ + (Dst)[yyi] = (Src)[yyi]; \ + } \ + while (0) +# endif +# endif +#endif /* !YYCOPY_NEEDED */ + +/* 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 +/* YYNSTATES -- Number of states. */ +#define YYNSTATES 12 + +#define YYUNDEFTOK 2 +#define YYMAXUTOK 260 + + +/* YYTRANSLATE(TOKEN-NUM) -- Symbol number corresponding to TOKEN-NUM + as returned by yylex, with out-of-bounds checking. */ +#define YYTRANSLATE(YYX) \ + (0 <= (YYX) && (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK) + +/* YYTRANSLATE[TOKEN-NUM] -- Symbol number corresponding to TOKEN-NUM + as returned by yylex. */ +static const yytype_int8 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 + /* YYRLINE[YYN] -- Source line where rule number YYN was defined. */ +static const yytype_int8 yyrline[] = +{ + 0, 93, 93, 94, 95, 98, 103, 111, 116 +}; +#endif + +#if YYDEBUG || YYERROR_VERBOSE || 1 +/* 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", YY_NULLPTR +}; +#endif + +# ifdef YYPRINT +/* YYTOKNUM[NUM] -- (External) token number corresponding to the + (internal) symbol number NUM (which must be that of a token). */ +static const yytype_int16 yytoknum[] = +{ + 0, 256, 257, 258, 259, 260 +}; +# endif + +#define YYPACT_NINF (-3) + +#define yypact_value_is_default(Yyn) \ + ((Yyn) == YYPACT_NINF) + +#define YYTABLE_NINF (-1) + +#define yytable_value_is_error(Yyn) \ + 0 + + /* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing + STATE-NUM. */ +static const yytype_int8 yypact[] = +{ + -3, 0, -3, -3, -3, -3, 2, -2, -3, -3, + 3, -3 +}; + + /* YYDEFACT[STATE-NUM] -- Default reduction number in state STATE-NUM. + Performed when YYTABLE does not specify something else to do. Zero + means the default is an error. */ +static const yytype_int8 yydefact[] = +{ + 2, 0, 1, 7, 3, 4, 0, 0, 8, 5, + 0, 6 +}; + + /* YYPGOTO[NTERM-NUM]. */ +static const yytype_int8 yypgoto[] = +{ + -3, -3, -3, 4, -3 +}; + + /* YYDEFGOTO[NTERM-NUM]. */ +static const yytype_int8 yydefgoto[] = +{ + -1, 1, 5, 6, 10 +}; + + /* YYTABLE[YYPACT[STATE-NUM]] -- What to do in state STATE-NUM. If + positive, shift that token. If negative, reduce the rule whose + number is the opposite. If YYTABLE_NINF, syntax error. */ +static const yytype_int8 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_int8 yystos[] = +{ + 0, 7, 0, 3, 4, 8, 9, 9, 3, 4, + 10, 4 +}; + + /* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ +static const yytype_int8 yyr1[] = +{ + 0, 6, 7, 7, 7, 8, 8, 9, 10 +}; + + /* YYR2[YYN] -- Number of symbols on the right hand side of rule YYN. */ +static const yytype_int8 yyr2[] = +{ + 0, 2, 0, 2, 2, 3, 4, 1, 1 +}; + + +#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 + + +#define YYRECOVERING() (!!yyerrstatus) + +#define YYBACKUP(Token, Value) \ + do \ + if (yychar == YYEMPTY) \ + { \ + yychar = (Token); \ + yylval = (Value); \ + YYPOPSTACK (yylen); \ + yystate = *yyssp; \ + goto yybackup; \ + } \ + else \ + { \ + yyerror (&yylloc, context, YY_("syntax error: cannot back up")); \ + YYERROR; \ + } \ + while (0) + +/* Error token number */ +#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). */ + +#ifndef YYLLOC_DEFAULT +# define YYLLOC_DEFAULT(Current, Rhs, N) \ + do \ + if (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 (0) +#endif + +#define YYRHSLOC(Rhs, K) ((Rhs)[K]) + + +/* Enable debugging if requested. */ +#if YYDEBUG + +# ifndef YYFPRINTF +# include /* INFRINGES ON USER NAME SPACE */ +# define YYFPRINTF fprintf +# endif + +# define YYDPRINTF(Args) \ +do { \ + if (yydebug) \ + YYFPRINTF Args; \ +} while (0) + + +/* 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 + +/* Print *YYLOCP on YYO. Private, do not rely on its existence. */ + +YY_ATTRIBUTE_UNUSED +static int +yy_location_print_ (FILE *yyo, YYLTYPE const * const yylocp) +{ + int res = 0; + int end_col = 0 != yylocp->last_column ? yylocp->last_column - 1 : 0; + if (0 <= yylocp->first_line) + { + res += YYFPRINTF (yyo, "%d", yylocp->first_line); + if (0 <= yylocp->first_column) + res += YYFPRINTF (yyo, ".%d", yylocp->first_column); + } + if (0 <= yylocp->last_line) + { + if (yylocp->first_line < yylocp->last_line) + { + res += YYFPRINTF (yyo, "-%d", yylocp->last_line); + if (0 <= end_col) + res += YYFPRINTF (yyo, ".%d", end_col); + } + else if (0 <= end_col && yylocp->first_column < end_col) + res += YYFPRINTF (yyo, "-%d", end_col); + } + return res; + } + +# define YY_LOCATION_PRINT(File, Loc) \ + yy_location_print_ (File, &(Loc)) + +# else +# define YY_LOCATION_PRINT(File, Loc) ((void) 0) +# endif +#endif + + +# 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 (0) + + +/*-----------------------------------. +| Print this symbol's value on YYO. | +`-----------------------------------*/ + +static void +yy_symbol_value_print (FILE *yyo, int yytype, YYSTYPE const * const yyvaluep, YYLTYPE const * const yylocationp, igraph_i_ncol_parsedata_t* context) +{ + FILE *yyoutput = yyo; + YYUSE (yyoutput); + YYUSE (yylocationp); + YYUSE (context); + if (!yyvaluep) + return; +# ifdef YYPRINT + if (yytype < YYNTOKENS) + YYPRINT (yyo, yytoknum[yytype], *yyvaluep); +# endif + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + YYUSE (yytype); + YY_IGNORE_MAYBE_UNINITIALIZED_END +} + + +/*---------------------------. +| Print this symbol on YYO. | +`---------------------------*/ + +static void +yy_symbol_print (FILE *yyo, int yytype, YYSTYPE const * const yyvaluep, YYLTYPE const * const yylocationp, igraph_i_ncol_parsedata_t* context) +{ + YYFPRINTF (yyo, "%s %s (", + yytype < YYNTOKENS ? "token" : "nterm", yytname[yytype]); + + YY_LOCATION_PRINT (yyo, *yylocationp); + YYFPRINTF (yyo, ": "); + yy_symbol_value_print (yyo, yytype, yyvaluep, yylocationp, context); + YYFPRINTF (yyo, ")"); +} + +/*------------------------------------------------------------------. +| yy_stack_print -- Print the state stack from its BOTTOM up to its | +| TOP (included). | +`------------------------------------------------------------------*/ + +static void +yy_stack_print (yy_state_t *yybottom, yy_state_t *yytop) +{ + YYFPRINTF (stderr, "Stack now"); + for (; yybottom <= yytop; yybottom++) + { + int yybot = *yybottom; + YYFPRINTF (stderr, " %d", yybot); + } + YYFPRINTF (stderr, "\n"); +} + +# define YY_STACK_PRINT(Bottom, Top) \ +do { \ + if (yydebug) \ + yy_stack_print ((Bottom), (Top)); \ +} while (0) + + +/*------------------------------------------------. +| Report that the YYRULE is going to be reduced. | +`------------------------------------------------*/ + +static void +yy_reduce_print (yy_state_t *yyssp, YYSTYPE *yyvsp, YYLTYPE *yylsp, int yyrule, igraph_i_ncol_parsedata_t* context) +{ + int yylno = yyrline[yyrule]; + int yynrhs = yyr2[yyrule]; + int yyi; + YYFPRINTF (stderr, "Reducing stack by rule %d (line %d):\n", + yyrule - 1, yylno); + /* The symbols being reduced. */ + for (yyi = 0; yyi < yynrhs; yyi++) + { + YYFPRINTF (stderr, " $%d = ", yyi + 1); + yy_symbol_print (stderr, + yystos[+yyssp[yyi + 1 - yynrhs]], + &yyvsp[(yyi + 1) - (yynrhs)] + , &(yylsp[(yyi + 1) - (yynrhs)]) , context); + YYFPRINTF (stderr, "\n"); + } +} + +# define YY_REDUCE_PRINT(Rule) \ +do { \ + if (yydebug) \ + yy_reduce_print (yyssp, yyvsp, yylsp, Rule, context); \ +} while (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(S) (YY_CAST (YYPTRDIFF_T, strlen (S))) +# else +/* Return the length of YYSTR. */ +static YYPTRDIFF_T +yystrlen (const char *yystr) +{ + YYPTRDIFF_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. */ +static char * +yystpcpy (char *yydest, const char *yysrc) +{ + 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 YYPTRDIFF_T +yytnamerr (char *yyres, const char *yystr) +{ + if (*yystr == '"') + { + YYPTRDIFF_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; + else + goto append; + + append: + default: + if (yyres) + yyres[yyn] = *yyp; + yyn++; + break; + + case '"': + if (yyres) + yyres[yyn] = '\0'; + return yyn; + } + do_not_strip_quotes: ; + } + + if (yyres) + return yystpcpy (yyres, yystr) - yyres; + else + return yystrlen (yystr); +} +# endif + +/* Copy into *YYMSG, which is of size *YYMSG_ALLOC, an error message + about the unexpected token YYTOKEN for the state stack whose top is + YYSSP. + + Return 0 if *YYMSG was successfully written. Return 1 if *YYMSG is + not large enough to hold the message. In that case, also set + *YYMSG_ALLOC to the required number of bytes. Return 2 if the + required number of bytes is too large to store. */ +static int +yysyntax_error (YYPTRDIFF_T *yymsg_alloc, char **yymsg, + yy_state_t *yyssp, int yytoken) +{ + enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 }; + /* Internationalized format string. */ + const char *yyformat = YY_NULLPTR; + /* Arguments of yyformat: reported tokens (one for the "unexpected", + one per "expected"). */ + char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM]; + /* Actual size of YYARG. */ + int yycount = 0; + /* Cumulated lengths of YYARG. */ + YYPTRDIFF_T yysize = 0; + + /* There are many possibilities here to consider: + - If this state is a consistent state with a default action, then + the only way this function was invoked is if the default action + is an error action. In that case, don't check for expected + tokens because there are none. + - The only way there can be no lookahead present (in yychar) is if + this state is a consistent state with a default action. Thus, + detecting the absence of a lookahead is sufficient to determine + that there is no unexpected or expected token to report. In that + case, just report a simple "syntax error". + - Don't assume there isn't a lookahead just because this state is a + consistent state with a default action. There might have been a + previous inconsistent state, consistent state with a non-default + action, or user semantic action that manipulated yychar. + - Of course, the expected token list depends on states to have + correct lookahead information, and it depends on the parser not + to perform extra reductions after fetching a lookahead from the + scanner and before detecting a syntax error. Thus, state merging + (from LALR or IELR) and default reductions corrupt the expected + token list. However, the list is correct for canonical LR with + one exception: it will still contain any token that will not be + accepted due to an error action in a later state. + */ + if (yytoken != YYEMPTY) + { + int yyn = yypact[+*yyssp]; + YYPTRDIFF_T yysize0 = yytnamerr (YY_NULLPTR, yytname[yytoken]); + yysize = yysize0; + yyarg[yycount++] = yytname[yytoken]; + if (!yypact_value_is_default (yyn)) + { + /* Start YYX at -YYN if negative to avoid negative indexes in + YYCHECK. In other words, skip the first -YYN actions for + this state because they are default actions. */ + 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 yyx; + + for (yyx = yyxbegin; yyx < yyxend; ++yyx) + if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR + && !yytable_value_is_error (yytable[yyx + yyn])) + { + if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM) + { + yycount = 1; + yysize = yysize0; + break; + } + yyarg[yycount++] = yytname[yyx]; + { + YYPTRDIFF_T yysize1 + = yysize + yytnamerr (YY_NULLPTR, yytname[yyx]); + if (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM) + yysize = yysize1; + else + return 2; + } + } + } + } + + switch (yycount) + { +# define YYCASE_(N, S) \ + case N: \ + yyformat = S; \ + break + default: /* Avoid compiler warnings. */ + YYCASE_(0, YY_("syntax error")); + YYCASE_(1, YY_("syntax error, unexpected %s")); + YYCASE_(2, YY_("syntax error, unexpected %s, expecting %s")); + YYCASE_(3, YY_("syntax error, unexpected %s, expecting %s or %s")); + YYCASE_(4, YY_("syntax error, unexpected %s, expecting %s or %s or %s")); + YYCASE_(5, YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s")); +# undef YYCASE_ + } + + { + /* Don't count the "%s"s in the final size, but reserve room for + the terminator. */ + YYPTRDIFF_T yysize1 = yysize + (yystrlen (yyformat) - 2 * yycount) + 1; + if (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM) + yysize = yysize1; + else + return 2; + } + + if (*yymsg_alloc < yysize) + { + *yymsg_alloc = 2 * yysize; + if (! (yysize <= *yymsg_alloc + && *yymsg_alloc <= YYSTACK_ALLOC_MAXIMUM)) + *yymsg_alloc = YYSTACK_ALLOC_MAXIMUM; + return 1; + } + + /* 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 = *yymsg; + int yyi = 0; + while ((*yyp = *yyformat) != '\0') + if (*yyp == '%' && yyformat[1] == 's' && yyi < yycount) + { + yyp += yytnamerr (yyp, yyarg[yyi++]); + yyformat += 2; + } + else + { + ++yyp; + ++yyformat; + } + } + return 0; +} +#endif /* YYERROR_VERBOSE */ + +/*-----------------------------------------------. +| Release the memory associated to this symbol. | +`-----------------------------------------------*/ + +static void +yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep, YYLTYPE *yylocationp, igraph_i_ncol_parsedata_t* context) +{ + YYUSE (yyvaluep); + YYUSE (yylocationp); + YYUSE (context); + if (!yymsg) + yymsg = "Deleting"; + YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp); + + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + YYUSE (yytype); + YY_IGNORE_MAYBE_UNINITIALIZED_END +} + + + + +/*----------. +| yyparse. | +`----------*/ + +int +yyparse (igraph_i_ncol_parsedata_t* context) +{ +/* The lookahead symbol. */ +int yychar; + + +/* The semantic value of the lookahead symbol. */ +/* Default value used for initialization, for pacifying older GCCs + or non-GCC compilers. */ +YY_INITIAL_VALUE (static YYSTYPE yyval_default;) +YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); + +/* Location data for the lookahead symbol. */ +static YYLTYPE yyloc_default +# if defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL + = { 1, 1, 1, 1 } +# endif +; +YYLTYPE yylloc = yyloc_default; + + /* Number of syntax errors so far. */ + int yynerrs; + + yy_state_fast_t yystate; + /* Number of tokens to shift before error messages enabled. */ + int yyerrstatus; + + /* The stacks and their tools: + 'yyss': related to states. + 'yyvs': related to semantic values. + 'yyls': related to locations. + + Refer to the stacks through separate pointers, to allow yyoverflow + to reallocate them elsewhere. */ + + /* The state stack. */ + yy_state_t yyssa[YYINITDEPTH]; + yy_state_t *yyss; + yy_state_t *yyssp; + + /* The semantic value stack. */ + YYSTYPE yyvsa[YYINITDEPTH]; + YYSTYPE *yyvs; + YYSTYPE *yyvsp; + + /* The location stack. */ + YYLTYPE yylsa[YYINITDEPTH]; + YYLTYPE *yyls; + YYLTYPE *yylsp; + + /* The locations where the error started and ended. */ + YYLTYPE yyerror_range[3]; + + YYPTRDIFF_T yystacksize; + + int yyn; + int yyresult; + /* Lookahead token as an internal (translated) token number. */ + int yytoken = 0; + /* The variables used to return semantic value and location from the + action routines. */ + YYSTYPE yyval; + YYLTYPE yyloc; + +#if YYERROR_VERBOSE + /* Buffer for error messages, and its allocated size. */ + char yymsgbuf[128]; + char *yymsg = yymsgbuf; + YYPTRDIFF_T yymsg_alloc = sizeof yymsgbuf; +#endif + +#define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N), yylsp -= (N)) + + /* The number of symbols on the RHS of the reduced rule. + Keep to zero when no symbol should be popped. */ + int yylen = 0; + + yyssp = yyss = yyssa; + yyvsp = yyvs = yyvsa; + yylsp = yyls = yylsa; + yystacksize = YYINITDEPTH; + + YYDPRINTF ((stderr, "Starting parse\n")); + + yystate = 0; + yyerrstatus = 0; + yynerrs = 0; + yychar = YYEMPTY; /* Cause a token to be read. */ + yylsp[0] = yylloc; + 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 -- set current state (the top of the stack) to yystate. | +`--------------------------------------------------------------------*/ +yysetstate: + YYDPRINTF ((stderr, "Entering state %d\n", yystate)); + YY_ASSERT (0 <= yystate && yystate < YYNSTATES); + YY_IGNORE_USELESS_CAST_BEGIN + *yyssp = YY_CAST (yy_state_t, yystate); + YY_IGNORE_USELESS_CAST_END + + if (yyss + yystacksize - 1 <= yyssp) +#if !defined yyoverflow && !defined YYSTACK_RELOCATE + goto yyexhaustedlab; +#else + { + /* Get the current used size of the three stacks, in elements. */ + YYPTRDIFF_T yysize = yyssp - yyss + 1; + +# if defined 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. */ + yy_state_t *yyss1 = yyss; + YYSTYPE *yyvs1 = yyvs; + 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 * YYSIZEOF (*yyssp), + &yyvs1, yysize * YYSIZEOF (*yyvsp), + &yyls1, yysize * YYSIZEOF (*yylsp), + &yystacksize); + yyss = yyss1; + yyvs = yyvs1; + yyls = yyls1; + } +# else /* defined YYSTACK_RELOCATE */ + /* Extend the stack our own way. */ + if (YYMAXDEPTH <= yystacksize) + goto yyexhaustedlab; + yystacksize *= 2; + if (YYMAXDEPTH < yystacksize) + yystacksize = YYMAXDEPTH; + + { + yy_state_t *yyss1 = yyss; + union yyalloc *yyptr = + YY_CAST (union yyalloc *, + YYSTACK_ALLOC (YY_CAST (YYSIZE_T, YYSTACK_BYTES (yystacksize)))); + if (! yyptr) + goto yyexhaustedlab; + YYSTACK_RELOCATE (yyss_alloc, yyss); + YYSTACK_RELOCATE (yyvs_alloc, yyvs); + YYSTACK_RELOCATE (yyls_alloc, yyls); +# undef YYSTACK_RELOCATE + if (yyss1 != yyssa) + YYSTACK_FREE (yyss1); + } +# endif + + yyssp = yyss + yysize - 1; + yyvsp = yyvs + yysize - 1; + yylsp = yyls + yysize - 1; + + YY_IGNORE_USELESS_CAST_BEGIN + YYDPRINTF ((stderr, "Stack size increased to %ld\n", + YY_CAST (long, yystacksize))); + YY_IGNORE_USELESS_CAST_END + + if (yyss + yystacksize - 1 <= yyssp) + YYABORT; + } +#endif /* !defined yyoverflow && !defined YYSTACK_RELOCATE */ + + if (yystate == YYFINAL) + YYACCEPT; + + goto yybackup; + + +/*-----------. +| yybackup. | +`-----------*/ +yybackup: + /* Do appropriate processing given the current state. Read a + lookahead token if we need one and don't already have one. */ + + /* First try to decide what to do without reference to lookahead token. */ + yyn = yypact[yystate]; + if (yypact_value_is_default (yyn)) + goto yydefault; + + /* Not known => get a lookahead token if don't already have one. */ + + /* YYCHAR is either YYEMPTY or YYEOF or a valid lookahead symbol. */ + if (yychar == YYEMPTY) + { + YYDPRINTF ((stderr, "Reading a token: ")); + yychar = yylex (&yylval, &yylloc, scanner); + } + + 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 (yytable_value_is_error (yyn)) + goto yyerrlab; + yyn = -yyn; + goto yyreduce; + } + + /* Count tokens shifted since error; after three, turn off error + status. */ + if (yyerrstatus) + yyerrstatus--; + + /* Shift the lookahead token. */ + YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc); + yystate = yyn; + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + *++yyvsp = yylval; + YY_IGNORE_MAYBE_UNINITIALIZED_END + *++yylsp = yylloc; + + /* Discard the shifted token. */ + yychar = YYEMPTY; + 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); + yyerror_range[1] = yyloc; + YY_REDUCE_PRINT (yyn); + switch (yyn) + { + case 5: +#line 98 "src/core/io/ncol-parser.y" + { + igraph_vector_push_back(context->vector, (yyvsp[-2].edgenum)); + igraph_vector_push_back(context->vector, (yyvsp[-1].edgenum)); + igraph_vector_push_back(context->weights, 0); + } +#line 1475 "yy.tab.c" + break; + + case 6: +#line 103 "src/core/io/ncol-parser.y" + { + igraph_vector_push_back(context->vector, (yyvsp[-3].edgenum)); + igraph_vector_push_back(context->vector, (yyvsp[-2].edgenum)); + igraph_vector_push_back(context->weights, (yyvsp[-1].weightnum)); + context->has_weights = 1; + } +#line 1486 "yy.tab.c" + break; + + case 7: +#line 111 "src/core/io/ncol-parser.y" + { igraph_trie_get2(context->trie, + igraph_ncol_yyget_text(scanner), + igraph_ncol_yyget_leng(scanner), + &(yyval.edgenum)); } +#line 1495 "yy.tab.c" + break; + + case 8: +#line 116 "src/core/io/ncol-parser.y" + { (yyval.weightnum)=igraph_ncol_get_number(igraph_ncol_yyget_text(scanner), + igraph_ncol_yyget_leng(scanner)); } +#line 1502 "yy.tab.c" + break; + + +#line 1506 "yy.tab.c" + + default: break; + } + /* User semantic actions sometimes alter yychar, and that requires + that yytoken be updated with the new translation. We take the + approach of translating immediately before every use of yytoken. + One alternative is translating here after every semantic action, + but that translation would be missed if the semantic action invokes + YYABORT, YYACCEPT, or YYERROR immediately after altering yychar or + if it invokes YYBACKUP. In the case of YYABORT or YYACCEPT, an + incorrect destructor might then be invoked immediately. In the + case of YYERROR or YYBACKUP, subsequent parser actions might lead + to an incorrect destructor call or verbose syntax error message + before the lookahead is translated. */ + 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. */ + { + const int yylhs = yyr1[yyn] - YYNTOKENS; + const int yyi = yypgoto[yylhs] + *yyssp; + yystate = (0 <= yyi && yyi <= YYLAST && yycheck[yyi] == *yyssp + ? yytable[yyi] + : yydefgoto[yylhs]); + } + + goto yynewstate; + + +/*--------------------------------------. +| yyerrlab -- here on detecting error. | +`--------------------------------------*/ +yyerrlab: + /* Make sure we have latest lookahead translation. See comments at + user semantic actions for why this is necessary. */ + yytoken = yychar == YYEMPTY ? YYEMPTY : YYTRANSLATE (yychar); + + /* If not already recovering from an error, report this error. */ + if (!yyerrstatus) + { + ++yynerrs; +#if ! YYERROR_VERBOSE + yyerror (&yylloc, context, YY_("syntax error")); +#else +# define YYSYNTAX_ERROR yysyntax_error (&yymsg_alloc, &yymsg, \ + yyssp, yytoken) + { + char const *yymsgp = YY_("syntax error"); + int yysyntax_error_status; + yysyntax_error_status = YYSYNTAX_ERROR; + if (yysyntax_error_status == 0) + yymsgp = yymsg; + else if (yysyntax_error_status == 1) + { + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); + yymsg = YY_CAST (char *, YYSTACK_ALLOC (YY_CAST (YYSIZE_T, yymsg_alloc))); + if (!yymsg) + { + yymsg = yymsgbuf; + yymsg_alloc = sizeof yymsgbuf; + yysyntax_error_status = 2; + } + else + { + yysyntax_error_status = YYSYNTAX_ERROR; + yymsgp = yymsg; + } + } + yyerror (&yylloc, context, yymsgp); + if (yysyntax_error_status == 2) + goto yyexhaustedlab; + } +# undef YYSYNTAX_ERROR +#endif + } + + yyerror_range[1] = yylloc; + + if (yyerrstatus == 3) + { + /* If just tried and failed to reuse lookahead 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 lookahead token after shifting the error + token. */ + goto yyerrlab1; + + +/*---------------------------------------------------. +| yyerrorlab -- error raised explicitly by YYERROR. | +`---------------------------------------------------*/ +yyerrorlab: + /* Pacify compilers when the user code never invokes YYERROR and the + label yyerrorlab therefore never appears in user code. */ + if (0) + YYERROR; + + /* Do not reclaim the symbols of the rule whose 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 (!yypact_value_is_default (yyn)) + { + 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[1] = *yylsp; + yydestruct ("Error: popping", + yystos[yystate], yyvsp, yylsp, context); + YYPOPSTACK (1); + yystate = *yyssp; + YY_STACK_PRINT (yyss, yyssp); + } + + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + *++yyvsp = yylval; + YY_IGNORE_MAYBE_UNINITIALIZED_END + + yyerror_range[2] = yylloc; + /* Using YYLLOC is tempting, but would change the location of + the lookahead. YYLOC is available though. */ + YYLLOC_DEFAULT (yyloc, yyerror_range, 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; + + +#if !defined yyoverflow || YYERROR_VERBOSE +/*-------------------------------------------------. +| yyexhaustedlab -- memory exhaustion comes here. | +`-------------------------------------------------*/ +yyexhaustedlab: + yyerror (&yylloc, context, YY_("memory exhausted")); + yyresult = 2; + /* Fall through. */ +#endif + + +/*-----------------------------------------------------. +| yyreturn -- parsing is finished, return the result. | +`-----------------------------------------------------*/ +yyreturn: + if (yychar != YYEMPTY) + { + /* Make sure we have latest lookahead translation. See comments at + user semantic actions for why this is necessary. */ + yytoken = YYTRANSLATE (yychar); + yydestruct ("Cleanup: discarding lookahead", + yytoken, &yylval, &yylloc, context); + } + /* Do not reclaim the symbols of the rule whose 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 + return yyresult; +} +#line 119 "src/core/io/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/src/rigraph/core/io/ncol-parser.h b/src/rigraph/core/io/ncol-parser.h new file mode 100644 index 0000000..91c660a --- /dev/null +++ b/src/rigraph/core/io/ncol-parser.h @@ -0,0 +1,93 @@ +/* A Bison parser, made by GNU Bison 3.5.1. */ + +/* Bison interface for Yacc-like parsers in C + + Copyright (C) 1984, 1989-1990, 2000-2015, 2018-2020 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 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 . */ + +/* 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. */ + +/* Undocumented macros, especially those whose name start with YY_, + are private implementation details. Do not rely on them. */ + +#ifndef YY_IGRAPH_NCOL_YY_YY_TAB_H_INCLUDED +# define YY_IGRAPH_NCOL_YY_YY_TAB_H_INCLUDED +/* Debug traces. */ +#ifndef YYDEBUG +# define YYDEBUG 0 +#endif +#if YYDEBUG +extern int igraph_ncol_yydebug; +#endif + +/* Token type. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + enum yytokentype + { + ALNUM = 258, + NEWLINE = 259, + ERROR = 260 + }; +#endif + +/* Value type. */ +#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED +union YYSTYPE +{ +#line 79 "src/core/io/ncol-parser.y" + + long int edgenum; + double weightnum; + +#line 68 "yy.tab.h" + +}; +typedef union YYSTYPE YYSTYPE; +# define YYSTYPE_IS_TRIVIAL 1 +# define YYSTYPE_IS_DECLARED 1 +#endif + +/* Location type. */ +#if ! defined YYLTYPE && ! defined YYLTYPE_IS_DECLARED +typedef struct YYLTYPE YYLTYPE; +struct YYLTYPE +{ + int first_line; + int first_column; + int last_line; + int last_column; +}; +# define YYLTYPE_IS_DECLARED 1 +# define YYLTYPE_IS_TRIVIAL 1 +#endif + + + +int igraph_ncol_yyparse (igraph_i_ncol_parsedata_t* context); + +#endif /* !YY_IGRAPH_NCOL_YY_YY_TAB_H_INCLUDED */ diff --git a/src/rigraph/core/io/ncol-parser.y b/src/rigraph/core/io/ncol-parser.y new file mode 100644 index 0000000..db833a8 --- /dev/null +++ b/src/rigraph/core/io/ncol-parser.y @@ -0,0 +1,139 @@ +/* + IGraph library. + Copyright (C) 2006-2012 Gabor Csardi + 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 + +*/ + +%{ + +/* + IGraph library. + Copyright (C) 2006-2012 Gabor Csardi + 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 +#include + +#include "igraph_types.h" +#include "igraph_memory.h" +#include "igraph_error.h" +#include "config.h" + +#include "core/math.h" +#include "io/ncol-header.h" +#include "io/parsers/ncol-parser.h" +#include "io/parsers/ncol-lexer.h" +#include "internal/hacks.h" + +int igraph_ncol_yyerror(YYLTYPE* locp, + igraph_i_ncol_parsedata_t *context, + const char *s); +igraph_real_t igraph_ncol_get_number(const char *str, long int len); + +#define scanner context->scanner +%} + +%pure-parser +/* bison: do not remove the equals sign; macOS XCode ships with bison 2.3, which + * needs the equals sign */ +%name-prefix="igraph_ncol_yy" +%defines +%locations +%error-verbose +%parse-param { igraph_i_ncol_parsedata_t* context } +%lex-param { void *scanner } + +%union { + long int edgenum; + double weightnum; +} + +%type edgeid +%type weight + +%token ALNUM +%token NEWLINE +%token ERROR + +%% + +input : /* empty */ + | input NEWLINE + | input edge +; + +edge : edgeid edgeid NEWLINE { + igraph_vector_push_back(context->vector, $1); + igraph_vector_push_back(context->vector, $2); + igraph_vector_push_back(context->weights, 0); + } + | edgeid edgeid weight NEWLINE { + igraph_vector_push_back(context->vector, $1); + igraph_vector_push_back(context->vector, $2); + igraph_vector_push_back(context->weights, $3); + context->has_weights = 1; + } +; + +edgeid : ALNUM { igraph_trie_get2(context->trie, + igraph_ncol_yyget_text(scanner), + igraph_ncol_yyget_leng(scanner), + &$$); }; + +weight : ALNUM { $$=igraph_ncol_get_number(igraph_ncol_yyget_text(scanner), + igraph_ncol_yyget_leng(scanner)); } ; + +%% + +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/src/rigraph/core/io/ncol.c b/src/rigraph/core/io/ncol.c new file mode 100644 index 0000000..9d6aa16 --- /dev/null +++ b/src/rigraph/core/io/ncol.c @@ -0,0 +1,383 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2005-2020 The igraph development 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., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_foreign.h" + +#include "igraph_attributes.h" +#include "igraph_interface.h" + +#include "graph/attributes.h" + +#include "ncol-header.h" + +int igraph_ncol_yylex_init_extra (igraph_i_ncol_parsedata_t* user_defined, + void* scanner); +void 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 an .ncol file used by LGL. + * + * Also useful for creating graphs from \quote named\endquote (and + * optionally weighted) edge lists. + * + * + * 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 vertex names themselves cannot contain + * whitespace. They may be followed by an optional number, + * 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. + * + * + * 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 .ncol file. If it is not \c NULL and some unknown + * vertex names are found in the .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, + const 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 = ≜ + 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; +} + +/** + * \ingroup loadsave + * \function igraph_write_graph_ncol + * \brief Writes the graph to a file in .ncol format. + * + * + * .ncol is a format used by LGL, see \ref + * igraph_read_graph_ncol() for details. + * + * + * Note that having multiple or loop edges in an + * .ncol file breaks the LGL software but + * \a igraph does not check for this condition. + * + * + * This format cannot represent zero-degree vertices. + * + * \param graph The graph to write. + * \param outstream The stream object to write to, it should be + * writable. + * \param names The name of a string vertex attribute, if symbolic names + * are to be written to the file. Supply \c NULL to write vertex + * ids instead. + * \param weights The name of a numerical edge attribute, which will be + * written as weights to the file. Supply \c NULL to skip writing + * edge weights. + * \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 (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 (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; +} diff --git a/src/rigraph/core/io/pajek-header.h b/src/rigraph/core/io/pajek-header.h new file mode 100644 index 0000000..92caae4 --- /dev/null +++ b/src/rigraph/core/io/pajek-header.h @@ -0,0 +1,46 @@ +/* + IGraph library. + Copyright (C) 2011-2012 Gabor Csardi + 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_error.h" +#include "igraph_vector.h" +#include "igraph_vector_ptr.h" + +#include "core/trie.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/src/rigraph/core/io/pajek-lexer.c b/src/rigraph/core/io/pajek-lexer.c new file mode 100644 index 0000000..1e9d6dd --- /dev/null +++ b/src/rigraph/core/io/pajek-lexer.c @@ -0,0 +1,2752 @@ +#line 2 "src/core/io/pajek-lexer.c" + +#line 4 "src/core/io/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 6 +#define YY_FLEX_SUBMINOR_VERSION 4 +#if YY_FLEX_SUBMINOR_VERSION > 0 +#define FLEX_BETA +#endif + +#ifdef yy_create_buffer +#define igraph_pajek_yy_create_buffer_ALREADY_DEFINED +#else +#define yy_create_buffer igraph_pajek_yy_create_buffer +#endif + +#ifdef yy_delete_buffer +#define igraph_pajek_yy_delete_buffer_ALREADY_DEFINED +#else +#define yy_delete_buffer igraph_pajek_yy_delete_buffer +#endif + +#ifdef yy_scan_buffer +#define igraph_pajek_yy_scan_buffer_ALREADY_DEFINED +#else +#define yy_scan_buffer igraph_pajek_yy_scan_buffer +#endif + +#ifdef yy_scan_string +#define igraph_pajek_yy_scan_string_ALREADY_DEFINED +#else +#define yy_scan_string igraph_pajek_yy_scan_string +#endif + +#ifdef yy_scan_bytes +#define igraph_pajek_yy_scan_bytes_ALREADY_DEFINED +#else +#define yy_scan_bytes igraph_pajek_yy_scan_bytes +#endif + +#ifdef yy_init_buffer +#define igraph_pajek_yy_init_buffer_ALREADY_DEFINED +#else +#define yy_init_buffer igraph_pajek_yy_init_buffer +#endif + +#ifdef yy_flush_buffer +#define igraph_pajek_yy_flush_buffer_ALREADY_DEFINED +#else +#define yy_flush_buffer igraph_pajek_yy_flush_buffer +#endif + +#ifdef yy_load_buffer_state +#define igraph_pajek_yy_load_buffer_state_ALREADY_DEFINED +#else +#define yy_load_buffer_state igraph_pajek_yy_load_buffer_state +#endif + +#ifdef yy_switch_to_buffer +#define igraph_pajek_yy_switch_to_buffer_ALREADY_DEFINED +#else +#define yy_switch_to_buffer igraph_pajek_yy_switch_to_buffer +#endif + +#ifdef yypush_buffer_state +#define igraph_pajek_yypush_buffer_state_ALREADY_DEFINED +#else +#define yypush_buffer_state igraph_pajek_yypush_buffer_state +#endif + +#ifdef yypop_buffer_state +#define igraph_pajek_yypop_buffer_state_ALREADY_DEFINED +#else +#define yypop_buffer_state igraph_pajek_yypop_buffer_state +#endif + +#ifdef yyensure_buffer_stack +#define igraph_pajek_yyensure_buffer_stack_ALREADY_DEFINED +#else +#define yyensure_buffer_stack igraph_pajek_yyensure_buffer_stack +#endif + +#ifdef yylex +#define igraph_pajek_yylex_ALREADY_DEFINED +#else +#define yylex igraph_pajek_yylex +#endif + +#ifdef yyrestart +#define igraph_pajek_yyrestart_ALREADY_DEFINED +#else +#define yyrestart igraph_pajek_yyrestart +#endif + +#ifdef yylex_init +#define igraph_pajek_yylex_init_ALREADY_DEFINED +#else +#define yylex_init igraph_pajek_yylex_init +#endif + +#ifdef yylex_init_extra +#define igraph_pajek_yylex_init_extra_ALREADY_DEFINED +#else +#define yylex_init_extra igraph_pajek_yylex_init_extra +#endif + +#ifdef yylex_destroy +#define igraph_pajek_yylex_destroy_ALREADY_DEFINED +#else +#define yylex_destroy igraph_pajek_yylex_destroy +#endif + +#ifdef yyget_debug +#define igraph_pajek_yyget_debug_ALREADY_DEFINED +#else +#define yyget_debug igraph_pajek_yyget_debug +#endif + +#ifdef yyset_debug +#define igraph_pajek_yyset_debug_ALREADY_DEFINED +#else +#define yyset_debug igraph_pajek_yyset_debug +#endif + +#ifdef yyget_extra +#define igraph_pajek_yyget_extra_ALREADY_DEFINED +#else +#define yyget_extra igraph_pajek_yyget_extra +#endif + +#ifdef yyset_extra +#define igraph_pajek_yyset_extra_ALREADY_DEFINED +#else +#define yyset_extra igraph_pajek_yyset_extra +#endif + +#ifdef yyget_in +#define igraph_pajek_yyget_in_ALREADY_DEFINED +#else +#define yyget_in igraph_pajek_yyget_in +#endif + +#ifdef yyset_in +#define igraph_pajek_yyset_in_ALREADY_DEFINED +#else +#define yyset_in igraph_pajek_yyset_in +#endif + +#ifdef yyget_out +#define igraph_pajek_yyget_out_ALREADY_DEFINED +#else +#define yyget_out igraph_pajek_yyget_out +#endif + +#ifdef yyset_out +#define igraph_pajek_yyset_out_ALREADY_DEFINED +#else +#define yyset_out igraph_pajek_yyset_out +#endif + +#ifdef yyget_leng +#define igraph_pajek_yyget_leng_ALREADY_DEFINED +#else +#define yyget_leng igraph_pajek_yyget_leng +#endif + +#ifdef yyget_text +#define igraph_pajek_yyget_text_ALREADY_DEFINED +#else +#define yyget_text igraph_pajek_yyget_text +#endif + +#ifdef yyget_lineno +#define igraph_pajek_yyget_lineno_ALREADY_DEFINED +#else +#define yyget_lineno igraph_pajek_yyget_lineno +#endif + +#ifdef yyset_lineno +#define igraph_pajek_yyset_lineno_ALREADY_DEFINED +#else +#define yyset_lineno igraph_pajek_yyset_lineno +#endif + +#ifdef yyget_column +#define igraph_pajek_yyget_column_ALREADY_DEFINED +#else +#define yyget_column igraph_pajek_yyget_column +#endif + +#ifdef yyset_column +#define igraph_pajek_yyset_column_ALREADY_DEFINED +#else +#define yyset_column igraph_pajek_yyset_column +#endif + +#ifdef yywrap +#define igraph_pajek_yywrap_ALREADY_DEFINED +#else +#define yywrap igraph_pajek_yywrap +#endif + +#ifdef yyget_lval +#define igraph_pajek_yyget_lval_ALREADY_DEFINED +#else +#define yyget_lval igraph_pajek_yyget_lval +#endif + +#ifdef yyset_lval +#define igraph_pajek_yyset_lval_ALREADY_DEFINED +#else +#define yyset_lval igraph_pajek_yyset_lval +#endif + +#ifdef yyget_lloc +#define igraph_pajek_yyget_lloc_ALREADY_DEFINED +#else +#define yyget_lloc igraph_pajek_yyget_lloc +#endif + +#ifdef yyset_lloc +#define igraph_pajek_yyset_lloc_ALREADY_DEFINED +#else +#define yyset_lloc igraph_pajek_yyset_lloc +#endif + +#ifdef yyalloc +#define igraph_pajek_yyalloc_ALREADY_DEFINED +#else +#define yyalloc igraph_pajek_yyalloc +#endif + +#ifdef yyrealloc +#define igraph_pajek_yyrealloc_ALREADY_DEFINED +#else +#define yyrealloc igraph_pajek_yyrealloc +#endif + +#ifdef yyfree +#define igraph_pajek_yyfree_ALREADY_DEFINED +#else +#define yyfree igraph_pajek_yyfree +#endif + +/* First, we deal with platform-specific or compiler-specific issues. */ + +/* begin standard C headers. */ +#include +#include +#include +#include + +/* end standard C headers. */ + +/* flex integer type definitions */ + +#ifndef FLEXINT_H +#define FLEXINT_H + +/* C99 systems have . 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 +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; +#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; + +/* 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 + +#ifndef SIZE_MAX +#define SIZE_MAX (~(size_t)0) +#endif + +#endif /* ! C99 */ + +#endif /* ! FLEXINT_H */ + +/* begin standard C++ headers. */ + +/* TODO: this is always defined, so inline it */ +#define yyconst const + +#if defined(__GNUC__) && __GNUC__ >= 3 +#define yynoreturn __attribute__((__noreturn__)) +#else +#define yynoreturn +#endif + +/* Returned upon end-of-file. */ +#define YY_NULL 0 + +/* Promotes a possibly negative, possibly signed char to an + * integer in range [0..255] for use as an array index. + */ +#define YY_SC_TO_UI(c) ((YY_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 yyrestart( yyin , yyscanner ) +#define YY_END_OF_BUFFER_CHAR 0 + +/* Size of default input buffer. */ +#ifndef YY_BUF_SIZE +#ifdef __ia64__ +/* On IA-64, the buffer size is 16k, not 8k. + * Moreover, YY_BUF_SIZE is 2*YY_READ_BUF_SIZE in the general case. + * Ditto for the __ia64__ case accordingly. + */ +#define YY_BUF_SIZE 32768 +#else +#define YY_BUF_SIZE 16384 +#endif /* __ia64__ */ +#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) + #define YY_LINENO_REWIND_TO(ptr) + +/* 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. + */ + int yy_buf_size; + + /* Number of characters read into yy_ch_buf, not including EOB + * characters. + */ + int 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 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 yyrestart ( FILE *input_file , yyscan_t yyscanner ); +void yy_switch_to_buffer ( YY_BUFFER_STATE new_buffer , yyscan_t yyscanner ); +YY_BUFFER_STATE yy_create_buffer ( FILE *file, int size , yyscan_t yyscanner ); +void yy_delete_buffer ( YY_BUFFER_STATE b , yyscan_t yyscanner ); +void yy_flush_buffer ( YY_BUFFER_STATE b , yyscan_t yyscanner ); +void yypush_buffer_state ( YY_BUFFER_STATE new_buffer , yyscan_t yyscanner ); +void yypop_buffer_state ( yyscan_t yyscanner ); + +static void yyensure_buffer_stack ( yyscan_t yyscanner ); +static void yy_load_buffer_state ( yyscan_t yyscanner ); +static void yy_init_buffer ( YY_BUFFER_STATE b, FILE *file , yyscan_t yyscanner ); +#define YY_FLUSH_BUFFER yy_flush_buffer( YY_CURRENT_BUFFER , yyscanner) + +YY_BUFFER_STATE yy_scan_buffer ( char *base, yy_size_t size , yyscan_t yyscanner ); +YY_BUFFER_STATE yy_scan_string ( const char *yy_str , yyscan_t yyscanner ); +YY_BUFFER_STATE yy_scan_bytes ( const char *bytes, int len , yyscan_t yyscanner ); + +void *yyalloc ( yy_size_t , yyscan_t yyscanner ); +void *yyrealloc ( void *, yy_size_t , yyscan_t yyscanner ); +void yyfree ( void * , yyscan_t yyscanner ); + +#define yy_new_buffer yy_create_buffer +#define yy_set_interactive(is_interactive) \ + { \ + if ( ! YY_CURRENT_BUFFER ){ \ + yyensure_buffer_stack (yyscanner); \ + YY_CURRENT_BUFFER_LVALUE = \ + 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 ){\ + yyensure_buffer_stack (yyscanner); \ + YY_CURRENT_BUFFER_LVALUE = \ + 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(yyscanner) (/*CONSTCOND*/1) +#define YY_SKIP_YYWRAP +typedef flex_uint8_t 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 __attribute__((unused)) yy_fatal_error ( const 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 = (int) (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 const flex_int16_t yy_accept[161] = + { 0, + 0, 0, 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, 47, + 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 const YY_CHAR 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 const YY_CHAR yy_meta[43] = + { 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, 2 + } ; + +static const flex_int16_t yy_base[168] = + { 0, + 0, 0, 295, 0, 292, 289, 289, 41, 45, 48, + 37, 45, 54, 74, 43, 68, 262, 40, 272, 48, + 103, 89, 82, 92, 96, 257, 99, 247, 246, 296, + 0, 284, 296, 106, 280, 0, 111, 78, 280, 113, + 120, 275, 0, 250, 262, 264, 259, 258, 117, 112, + 128, 296, 142, 147, 152, 155, 161, 296, 119, 164, + 167, 170, 173, 176, 296, 179, 182, 185, 188, 296, + 253, 296, 296, 296, 236, 249, 296, 253, 252, 296, + 268, 157, 296, 253, 248, 235, 234, 235, 180, 183, + 188, 296, 296, 296, 296, 296, 232, 202, 296, 296, + + 296, 296, 296, 296, 296, 296, 241, 296, 207, 244, + 210, 247, 246, 258, 227, 239, 226, 220, 222, 213, + 296, 216, 296, 219, 296, 237, 236, 227, 219, 227, + 221, 225, 296, 296, 296, 214, 213, 222, 219, 206, + 211, 224, 222, 225, 174, 175, 0, 115, 116, 296, + 296, 100, 85, 0, 64, 0, 45, 0, 0, 296, + 74, 229, 231, 233, 235, 237, 239 + } ; + +static const flex_int16_t yy_def[168] = + { 0, + 160, 1, 160, 161, 160, 160, 160, 162, 163, 164, + 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, + 161, 161, 161, 161, 161, 161, 161, 161, 161, 160, + 161, 160, 160, 162, 165, 161, 163, 166, 160, 166, + 164, 167, 161, 161, 161, 161, 161, 161, 161, 161, + 161, 160, 161, 161, 161, 161, 161, 160, 161, 161, + 161, 161, 161, 161, 160, 161, 161, 161, 161, 160, + 161, 160, 160, 160, 161, 161, 160, 161, 161, 160, + 160, 160, 160, 161, 161, 161, 161, 161, 161, 161, + 161, 160, 160, 160, 160, 160, 161, 161, 160, 160, + + 160, 160, 160, 160, 160, 160, 161, 160, 161, 161, + 161, 161, 161, 160, 161, 161, 161, 161, 161, 161, + 160, 161, 160, 161, 160, 161, 161, 161, 161, 161, + 161, 161, 160, 160, 160, 161, 161, 161, 161, 161, + 161, 161, 161, 161, 161, 161, 161, 161, 161, 160, + 160, 161, 161, 161, 161, 161, 161, 161, 161, 0, + 160, 160, 160, 160, 160, 160, 160 + } ; + +static const flex_int16_t yy_nxt[339] = + { 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, 30, 35, 35, 35, 36, 38, 39, 40, 42, + 42, 42, 44, 60, 61, 43, 45, 49, 49, 49, + 56, 63, 64, 46, 47, 50, 49, 49, 49, 58, + 58, 58, 48, 51, 31, 52, 52, 52, 159, 57, + 39, 40, 35, 72, 72, 72, 38, 53, 54, 42, + 70, 70, 70, 73, 73, 73, 158, 74, 74, 74, + + 77, 77, 77, 55, 65, 65, 65, 35, 35, 35, + 36, 71, 38, 39, 40, 82, 40, 157, 66, 75, + 67, 42, 42, 42, 89, 89, 89, 43, 50, 49, + 49, 49, 68, 156, 69, 155, 51, 90, 90, 154, + 91, 91, 91, 92, 92, 92, 97, 35, 93, 93, + 93, 98, 38, 94, 94, 94, 95, 95, 95, 114, + 81, 42, 96, 96, 96, 99, 99, 99, 100, 100, + 100, 101, 101, 101, 102, 102, 102, 103, 103, 103, + 104, 104, 104, 105, 105, 105, 106, 106, 106, 108, + 108, 108, 89, 89, 89, 91, 91, 91, 153, 51, + + 91, 91, 91, 121, 121, 121, 152, 107, 123, 123, + 123, 125, 125, 125, 133, 133, 133, 134, 134, 134, + 135, 135, 135, 150, 150, 150, 151, 151, 151, 34, + 34, 37, 37, 41, 41, 35, 35, 38, 38, 42, + 42, 149, 148, 147, 146, 145, 144, 143, 142, 141, + 140, 139, 138, 137, 136, 132, 131, 130, 129, 128, + 114, 127, 126, 124, 122, 120, 119, 118, 117, 116, + 115, 81, 113, 112, 111, 110, 109, 88, 87, 86, + 85, 84, 83, 81, 80, 32, 79, 78, 76, 62, + 59, 33, 33, 32, 160, 3, 160, 160, 160, 160, + + 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, + 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, + 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, + 160, 160, 160, 160, 160, 160, 160, 160 + } ; + +static const flex_int16_t yy_chk[339] = + { 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, 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, 16, + 16, 16, 11, 13, 161, 14, 14, 14, 157, 15, + 38, 38, 8, 23, 23, 23, 9, 14, 14, 10, + 22, 22, 22, 24, 24, 24, 155, 25, 25, 25, + + 27, 27, 27, 14, 21, 21, 21, 34, 34, 34, + 34, 22, 37, 37, 37, 40, 40, 153, 21, 25, + 21, 41, 41, 41, 50, 50, 50, 41, 49, 49, + 49, 49, 21, 152, 21, 149, 49, 51, 51, 148, + 51, 51, 51, 53, 53, 53, 59, 34, 54, 54, + 54, 59, 37, 55, 55, 55, 56, 56, 56, 82, + 82, 41, 57, 57, 57, 60, 60, 60, 61, 61, + 61, 62, 62, 62, 63, 63, 63, 64, 64, 64, + 66, 66, 66, 67, 67, 67, 68, 68, 68, 69, + 69, 69, 89, 89, 89, 90, 90, 90, 146, 89, + + 91, 91, 91, 98, 98, 98, 145, 68, 109, 109, + 109, 111, 111, 111, 120, 120, 120, 122, 122, 122, + 124, 124, 124, 143, 143, 143, 144, 144, 144, 162, + 162, 163, 163, 164, 164, 165, 165, 166, 166, 167, + 167, 142, 141, 140, 139, 138, 137, 136, 132, 131, + 130, 129, 128, 127, 126, 119, 118, 117, 116, 115, + 114, 113, 112, 110, 107, 97, 88, 87, 86, 85, + 84, 81, 79, 78, 76, 75, 71, 48, 47, 46, + 45, 44, 42, 39, 35, 32, 29, 28, 26, 19, + 17, 7, 6, 5, 3, 160, 160, 160, 160, 160, + + 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, + 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, + 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, + 160, 160, 160, 160, 160, 160, 160, 160 + } ; + +/* 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/core/io/pajek-lexer.l" +/* + IGraph library. + Copyright (C) 2006-2012 Gabor Csardi + 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/core/io/pajek-lexer.l" + +/* + IGraph library. + Copyright (C) 2006-2012 Gabor Csardi + 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 + +#include "io/pajek-header.h" +#include "io/pajek-parser.h" + +#define YY_EXTRA_TYPE igraph_i_pajek_parsedata_t* +#define YY_USER_ACTION yylloc->first_line = yylineno; +#define YY_FATAL_ERROR(msg) IGRAPH_FATAL("Error in Pajek parser: " # msg) +#ifdef USING_R +#define fprintf(file, msg, ...) (1) +#ifdef stdout +# undef stdout +#endif +#define stdout 0 +#endif +#line 843 "src/core/io/pajek-lexer.c" +#define YY_NO_INPUT 1 +#line 845 "src/core/io/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 +#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; + int yy_n_chars; + int 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 yylex_init (yyscan_t* scanner); + +int 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 yylex_destroy ( yyscan_t yyscanner ); + +int yyget_debug ( yyscan_t yyscanner ); + +void yyset_debug ( int debug_flag , yyscan_t yyscanner ); + +YY_EXTRA_TYPE yyget_extra ( yyscan_t yyscanner ); + +void yyset_extra ( YY_EXTRA_TYPE user_defined , yyscan_t yyscanner ); + +FILE *yyget_in ( yyscan_t yyscanner ); + +void yyset_in ( FILE * _in_str , yyscan_t yyscanner ); + +FILE *yyget_out ( yyscan_t yyscanner ); + +void yyset_out ( FILE * _out_str , yyscan_t yyscanner ); + + yy_size_t yyget_leng ( yyscan_t yyscanner ); + +char *yyget_text ( yyscan_t yyscanner ); + +int yyget_lineno ( yyscan_t yyscanner ); + +void yyset_lineno ( int _line_number , yyscan_t yyscanner ); + +int yyget_column ( yyscan_t yyscanner ); + +void yyset_column ( int _column_no , yyscan_t yyscanner ); + +YYSTYPE * yyget_lval ( yyscan_t yyscanner ); + +void yyset_lval ( YYSTYPE * yylval_param , yyscan_t yyscanner ); + + YYLTYPE *yyget_lloc ( yyscan_t yyscanner ); + + void 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 yywrap ( yyscan_t yyscanner ); +#else +extern int yywrap ( yyscan_t yyscanner ); +#endif +#endif + +#ifndef YY_NO_UNPUT + +#endif + +#ifndef yytext_ptr +static void yy_flex_strncpy ( char *, const char *, int , yyscan_t yyscanner); +#endif + +#ifdef YY_NEED_STRLEN +static int yy_flex_strlen ( const 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 +#ifdef __ia64__ +/* On IA-64, the buffer size is 16k, not 8k */ +#define YY_READ_BUF_SIZE 16384 +#else +#define YY_READ_BUF_SIZE 8192 +#endif /* __ia64__ */ +#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 do { if (fwrite( yytext, (size_t) yyleng, 1, yyout )) {} } while (0) +#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 = '*'; \ + int 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 = (int) fread(buf, 1, (yy_size_t) 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 yylex \ + (YYSTYPE * yylval_param, YYLTYPE * yylloc_param , yyscan_t yyscanner); + +#define YY_DECL int 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 /*LINTED*/break; +#endif + +#define YY_RULE_SETUP \ + YY_USER_ACTION + +/** The main scanner function which does all the work. + */ +YY_DECL +{ + yy_state_type yy_current_state; + char *yy_cp, *yy_bp; + int yy_act; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + 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 ) { + yyensure_buffer_stack (yyscanner); + YY_CURRENT_BUFFER_LVALUE = + yy_create_buffer( yyin, YY_BUF_SIZE , yyscanner); + } + + yy_load_buffer_state( yyscanner ); + } + + { +#line 77 "src/core/io/pajek-lexer.l" + + +#line 1130 "src/core/io/pajek-lexer.c" + + while ( /*CONSTCOND*/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 + { + 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 >= 161 ) + yy_c = yy_meta[yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c]; + ++yy_cp; + } + while ( yy_base[yy_current_state] != 296 ); + +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 79 "src/core/io/pajek-lexer.l" +{ } + YY_BREAK +case 2: +/* rule 2 can match eol */ +YY_RULE_SETUP +#line 80 "src/core/io/pajek-lexer.l" +{ } + YY_BREAK +case 3: +/* rule 3 can match eol */ +YY_RULE_SETUP +#line 81 "src/core/io/pajek-lexer.l" +{ } + YY_BREAK +case 4: +YY_RULE_SETUP +#line 82 "src/core/io/pajek-lexer.l" +{ return NETWORKLINE; } + YY_BREAK +case 5: +YY_RULE_SETUP +#line 83 "src/core/io/pajek-lexer.l" +{ return NETWORKLINE; } + YY_BREAK +case 6: +YY_RULE_SETUP +#line 84 "src/core/io/pajek-lexer.l" +{ return VERTICESLINE; } + YY_BREAK +case 7: +YY_RULE_SETUP +#line 85 "src/core/io/pajek-lexer.l" +{ return ARCSLINE; } + YY_BREAK +case 8: +YY_RULE_SETUP +#line 86 "src/core/io/pajek-lexer.l" +{ return EDGESLINE; } + YY_BREAK +case 9: +YY_RULE_SETUP +#line 87 "src/core/io/pajek-lexer.l" +{ return ARCSLISTLINE; } + YY_BREAK +case 10: +YY_RULE_SETUP +#line 88 "src/core/io/pajek-lexer.l" +{ return EDGESLISTLINE; } + YY_BREAK +case 11: +YY_RULE_SETUP +#line 89 "src/core/io/pajek-lexer.l" +{ return MATRIXLINE; } + YY_BREAK +case 12: +/* rule 12 can match eol */ +YY_RULE_SETUP +#line 90 "src/core/io/pajek-lexer.l" +{ yyextra->mode=0; return NEWLINE; } + YY_BREAK +case 13: +/* rule 13 can match eol */ +YY_RULE_SETUP +#line 91 "src/core/io/pajek-lexer.l" +{ return QSTR; } + YY_BREAK +case 14: +/* rule 14 can match eol */ +YY_RULE_SETUP +#line 92 "src/core/io/pajek-lexer.l" +{ return PSTR; } + YY_BREAK +case 15: +YY_RULE_SETUP +#line 93 "src/core/io/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 */ +YY_LINENO_REWIND_TO(yy_bp + 6); +yyg->yy_c_buf_p = yy_cp = yy_bp + 6; +YY_DO_BEFORE_ACTION; /* set up yytext again */ +YY_RULE_SETUP +#line 96 "src/core/io/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 */ +YY_LINENO_REWIND_TO(yy_bp + 6); +yyg->yy_c_buf_p = yy_cp = yy_bp + 6; +YY_DO_BEFORE_ACTION; /* set up yytext again */ +YY_RULE_SETUP +#line 97 "src/core/io/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 */ +YY_LINENO_REWIND_TO(yy_bp + 2); +yyg->yy_c_buf_p = yy_cp = yy_bp + 2; +YY_DO_BEFORE_ACTION; /* set up yytext again */ +YY_RULE_SETUP +#line 98 "src/core/io/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 */ +YY_LINENO_REWIND_TO(yy_bp + 2); +yyg->yy_c_buf_p = yy_cp = yy_bp + 2; +YY_DO_BEFORE_ACTION; /* set up yytext again */ +YY_RULE_SETUP +#line 99 "src/core/io/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 */ +YY_LINENO_REWIND_TO(yy_bp + 2); +yyg->yy_c_buf_p = yy_cp = yy_bp + 2; +YY_DO_BEFORE_ACTION; /* set up yytext again */ +YY_RULE_SETUP +#line 100 "src/core/io/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 */ +YY_LINENO_REWIND_TO(yy_bp + 3); +yyg->yy_c_buf_p = yy_cp = yy_bp + 3; +YY_DO_BEFORE_ACTION; /* set up yytext again */ +YY_RULE_SETUP +#line 101 "src/core/io/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 */ +YY_LINENO_REWIND_TO(yy_bp + 1); +yyg->yy_c_buf_p = yy_cp = yy_bp + 1; +YY_DO_BEFORE_ACTION; /* set up yytext again */ +YY_RULE_SETUP +#line 102 "src/core/io/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 */ +YY_LINENO_REWIND_TO(yy_bp + 1); +yyg->yy_c_buf_p = yy_cp = yy_bp + 1; +YY_DO_BEFORE_ACTION; /* set up yytext again */ +YY_RULE_SETUP +#line 103 "src/core/io/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 */ +YY_LINENO_REWIND_TO(yy_bp + 4); +yyg->yy_c_buf_p = yy_cp = yy_bp + 4; +YY_DO_BEFORE_ACTION; /* set up yytext again */ +YY_RULE_SETUP +#line 104 "src/core/io/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 */ +YY_LINENO_REWIND_TO(yy_bp + 3); +yyg->yy_c_buf_p = yy_cp = yy_bp + 3; +YY_DO_BEFORE_ACTION; /* set up yytext again */ +YY_RULE_SETUP +#line 105 "src/core/io/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 */ +YY_LINENO_REWIND_TO(yy_bp + 1); +yyg->yy_c_buf_p = yy_cp = yy_bp + 1; +YY_DO_BEFORE_ACTION; /* set up yytext again */ +YY_RULE_SETUP +#line 107 "src/core/io/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 */ +YY_LINENO_REWIND_TO(yy_bp + 1); +yyg->yy_c_buf_p = yy_cp = yy_bp + 1; +YY_DO_BEFORE_ACTION; /* set up yytext again */ +YY_RULE_SETUP +#line 108 "src/core/io/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 */ +YY_LINENO_REWIND_TO(yy_bp + 1); +yyg->yy_c_buf_p = yy_cp = yy_bp + 1; +YY_DO_BEFORE_ACTION; /* set up yytext again */ +YY_RULE_SETUP +#line 109 "src/core/io/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 */ +YY_LINENO_REWIND_TO(yy_bp + 1); +yyg->yy_c_buf_p = yy_cp = yy_bp + 1; +YY_DO_BEFORE_ACTION; /* set up yytext again */ +YY_RULE_SETUP +#line 110 "src/core/io/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 */ +YY_LINENO_REWIND_TO(yy_bp + 1); +yyg->yy_c_buf_p = yy_cp = yy_bp + 1; +YY_DO_BEFORE_ACTION; /* set up yytext again */ +YY_RULE_SETUP +#line 111 "src/core/io/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 */ +YY_LINENO_REWIND_TO(yy_bp + 2); +yyg->yy_c_buf_p = yy_cp = yy_bp + 2; +YY_DO_BEFORE_ACTION; /* set up yytext again */ +YY_RULE_SETUP +#line 112 "src/core/io/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 */ +YY_LINENO_REWIND_TO(yy_bp + 2); +yyg->yy_c_buf_p = yy_cp = yy_bp + 2; +YY_DO_BEFORE_ACTION; /* set up yytext again */ +YY_RULE_SETUP +#line 113 "src/core/io/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 */ +YY_LINENO_REWIND_TO(yy_bp + 2); +yyg->yy_c_buf_p = yy_cp = yy_bp + 2; +YY_DO_BEFORE_ACTION; /* set up yytext again */ +YY_RULE_SETUP +#line 114 "src/core/io/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 */ +YY_LINENO_REWIND_TO(yy_bp + 2); +yyg->yy_c_buf_p = yy_cp = yy_bp + 2; +YY_DO_BEFORE_ACTION; /* set up yytext again */ +YY_RULE_SETUP +#line 115 "src/core/io/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 */ +YY_LINENO_REWIND_TO(yy_bp + 2); +yyg->yy_c_buf_p = yy_cp = yy_bp + 2; +YY_DO_BEFORE_ACTION; /* set up yytext again */ +YY_RULE_SETUP +#line 116 "src/core/io/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 */ +YY_LINENO_REWIND_TO(yy_bp + 2); +yyg->yy_c_buf_p = yy_cp = yy_bp + 2; +YY_DO_BEFORE_ACTION; /* set up yytext again */ +YY_RULE_SETUP +#line 117 "src/core/io/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 */ +YY_LINENO_REWIND_TO(yy_bp + 2); +yyg->yy_c_buf_p = yy_cp = yy_bp + 2; +YY_DO_BEFORE_ACTION; /* set up yytext again */ +YY_RULE_SETUP +#line 118 "src/core/io/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 */ +YY_LINENO_REWIND_TO(yy_bp + 1); +yyg->yy_c_buf_p = yy_cp = yy_bp + 1; +YY_DO_BEFORE_ACTION; /* set up yytext again */ +YY_RULE_SETUP +#line 119 "src/core/io/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 */ +YY_LINENO_REWIND_TO(yy_bp + 2); +yyg->yy_c_buf_p = yy_cp = yy_bp + 2; +YY_DO_BEFORE_ACTION; /* set up yytext again */ +YY_RULE_SETUP +#line 120 "src/core/io/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 */ +YY_LINENO_REWIND_TO(yy_bp + 4); +yyg->yy_c_buf_p = yy_cp = yy_bp + 4; +YY_DO_BEFORE_ACTION; /* set up yytext again */ +YY_RULE_SETUP +#line 122 "src/core/io/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 */ +YY_LINENO_REWIND_TO(yy_bp + 2); +yyg->yy_c_buf_p = yy_cp = yy_bp + 2; +YY_DO_BEFORE_ACTION; /* set up yytext again */ +YY_RULE_SETUP +#line 124 "src/core/io/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 */ +YY_LINENO_REWIND_TO(yy_bp + 2); +yyg->yy_c_buf_p = yy_cp = yy_bp + 2; +YY_DO_BEFORE_ACTION; /* set up yytext again */ +YY_RULE_SETUP +#line 126 "src/core/io/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 */ +YY_LINENO_REWIND_TO(yy_bp + 2); +yyg->yy_c_buf_p = yy_cp = yy_bp + 2; +YY_DO_BEFORE_ACTION; /* set up yytext again */ +YY_RULE_SETUP +#line 128 "src/core/io/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 */ +YY_LINENO_REWIND_TO(yy_bp + 4); +yyg->yy_c_buf_p = yy_cp = yy_bp + 4; +YY_DO_BEFORE_ACTION; /* set up yytext again */ +YY_RULE_SETUP +#line 130 "src/core/io/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 */ +YY_LINENO_REWIND_TO(yy_bp + 3); +yyg->yy_c_buf_p = yy_cp = yy_bp + 3; +YY_DO_BEFORE_ACTION; /* set up yytext again */ +YY_RULE_SETUP +#line 132 "src/core/io/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 135 "src/core/io/pajek-lexer.l" +{ return ALNUM; } + YY_BREAK +case YY_STATE_EOF(INITIAL): +#line 137 "src/core/io/pajek-lexer.l" +{ if (yyextra->eof) { + yyterminate(); + } else { + yyextra->eof=1; + return NEWLINE; + } + } + YY_BREAK +case 47: +YY_RULE_SETUP +#line 145 "src/core/io/pajek-lexer.l" +{ return ERROR; } + YY_BREAK +case 48: +YY_RULE_SETUP +#line 147 "src/core/io/pajek-lexer.l" +YY_FATAL_ERROR( "flex scanner jammed" ); + YY_BREAK +#line 1599 "src/core/io/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 + * 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 ( 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 user's declarations */ +} /* end of 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; + char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf; + char *source = yyg->yytext_ptr; + 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 + { + int 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_LVALUE; + + int yy_c_buf_p_offset = + (int) (yyg->yy_c_buf_p - b->yy_ch_buf); + + if ( b->yy_is_our_buffer ) + { + int 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. */ + yyrealloc( (void *) b->yy_ch_buf, + (yy_size_t) (b->yy_buf_size + 2) , yyscanner ); + } + else + /* Can't grow it, we don't own it. */ + b->yy_ch_buf = NULL; + + 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; + 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 ((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. */ + int new_size = yyg->yy_n_chars + number_to_move + (yyg->yy_n_chars >> 1); + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) yyrealloc( + (void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf, (yy_size_t) new_size , yyscanner ); + if ( ! YY_CURRENT_BUFFER_LVALUE->yy_ch_buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_get_next_buffer()" ); + /* "- 2" to take care of EOB's */ + YY_CURRENT_BUFFER_LVALUE->yy_buf_size = (int) (new_size - 2); + } + + 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) +{ + yy_state_type yy_current_state; + 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 ) + { + YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 42); + 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 >= 161 ) + yy_c = yy_meta[yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + 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) +{ + int yy_is_jam; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* This var may be unused depending upon options. */ + char *yy_cp = yyg->yy_c_buf_p; + + YY_CHAR yy_c = 42; + 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 >= 161 ) + yy_c = yy_meta[yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c]; + yy_is_jam = (yy_current_state == 160); + + (void)yyg; + return yy_is_jam ? 0 : yy_current_state; +} + +#ifndef YY_NO_UNPUT + +#endif + +#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 */ + int offset = (int) (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. */ + yyrestart( yyin , yyscanner); + + /*FALLTHROUGH*/ + + case EOB_ACT_END_OF_FILE: + { + if ( 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 yyrestart (FILE * input_file , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + if ( ! YY_CURRENT_BUFFER ){ + yyensure_buffer_stack (yyscanner); + YY_CURRENT_BUFFER_LVALUE = + yy_create_buffer( yyin, YY_BUF_SIZE , yyscanner); + } + + yy_init_buffer( YY_CURRENT_BUFFER, input_file , yyscanner); + yy_load_buffer_state( yyscanner ); +} + +/** Switch to a different input buffer. + * @param new_buffer The new input buffer. + * @param yyscanner The scanner object. + */ + void 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 + * yypop_buffer_state(); + * yypush_buffer_state(new_buffer); + */ + 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; + yy_load_buffer_state( yyscanner ); + + /* We don't actually know whether we did this switch during + * EOF (yywrap()) processing, but the only time this flag + * is looked at is after yywrap() is called, so it's safe + * to go ahead and always set it. + */ + yyg->yy_did_buffer_switch_on_eof = 1; +} + +static void 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 yy_create_buffer (FILE * file, int size , yyscan_t yyscanner) +{ + YY_BUFFER_STATE b; + + b = (YY_BUFFER_STATE) yyalloc( sizeof( struct yy_buffer_state ) , yyscanner ); + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in 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 *) yyalloc( (yy_size_t) (b->yy_buf_size + 2) , yyscanner ); + if ( ! b->yy_ch_buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); + + b->yy_is_our_buffer = 1; + + yy_init_buffer( b, file , yyscanner); + + return b; +} + +/** Destroy the buffer. + * @param b a buffer created with yy_create_buffer() + * @param yyscanner The scanner object. + */ + void 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 ) + yyfree( (void *) b->yy_ch_buf , yyscanner ); + + yyfree( (void *) b , yyscanner ); +} + +/* Initializes or reinitializes a buffer. + * This function is sometimes called more than once on the same buffer, + * such as during a yyrestart() or at EOF. + */ + static void yy_init_buffer (YY_BUFFER_STATE b, FILE * file , yyscan_t yyscanner) + +{ + int oerrno = errno; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + yy_flush_buffer( b , yyscanner); + + b->yy_input_file = file; + b->yy_fill_buffer = 1; + + /* If b is the current buffer, then yy_init_buffer was _probably_ + * called from 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 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 ) + 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 yypush_buffer_state (YY_BUFFER_STATE new_buffer , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + if (new_buffer == NULL) + return; + + yyensure_buffer_stack(yyscanner); + + /* This block is copied from 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 yy_switch_to_buffer. */ + 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 yypop_buffer_state (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + if (!YY_CURRENT_BUFFER) + return; + + 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) { + 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 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; /* After all that talk, this was set to 1 anyways... */ + yyg->yy_buffer_stack = (struct yy_buffer_state**)yyalloc + (num_to_alloc * sizeof(struct yy_buffer_state*) + , yyscanner); + if ( ! yyg->yy_buffer_stack ) + YY_FATAL_ERROR( "out of dynamic memory in 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. */ + yy_size_t grow_size = 8 /* arbitrary grow size */; + + num_to_alloc = yyg->yy_buffer_stack_max + grow_size; + yyg->yy_buffer_stack = (struct yy_buffer_state**)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 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 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 NULL; + + b = (YY_BUFFER_STATE) yyalloc( sizeof( struct yy_buffer_state ) , yyscanner ); + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in yy_scan_buffer()" ); + + b->yy_buf_size = (int) (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 = NULL; + 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; + + yy_switch_to_buffer( b , yyscanner ); + + return b; +} + +/** Setup the input buffer state to scan a string. The next call to 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 + * yy_scan_bytes() instead. + */ +YY_BUFFER_STATE yy_scan_string (const char * yystr , yyscan_t yyscanner) +{ + + return yy_scan_bytes( yystr, (int) strlen(yystr) , yyscanner); +} + +/** Setup the input buffer state to scan the given bytes. The next call to yylex() will + * scan from a @e copy of @a bytes. + * @param yybytes the byte buffer to scan + * @param _yybytes_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 yy_scan_bytes (const char * yybytes, int _yybytes_len , yyscan_t yyscanner) +{ + YY_BUFFER_STATE b; + char *buf; + yy_size_t n; + int i; + + /* Get memory for full buffer, including space for trailing EOB's. */ + n = (yy_size_t) (_yybytes_len + 2); + buf = (char *) yyalloc( n , yyscanner ); + if ( ! buf ) + YY_FATAL_ERROR( "out of dynamic memory in 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 = yy_scan_buffer( buf, n , yyscanner); + if ( ! b ) + YY_FATAL_ERROR( "bad buffer in 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 __attribute__((unused)) yy_fatal_error (const char* msg , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + (void)yyg; + fprintf( stderr, "%s\n", msg ); +} + +/* 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 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 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 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 *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 *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 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 *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 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 line number + * @param yyscanner The scanner object. + */ +void 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( "yyset_lineno called with no buffer" ); + + yylineno = _line_number; +} + +/** Set the current column. + * @param _column_no column number + * @param yyscanner The scanner object. + */ +void 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( "yyset_column called with no buffer" ); + + 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 yy_switch_to_buffer + */ +void yyset_in (FILE * _in_str , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yyin = _in_str ; +} + +void yyset_out (FILE * _out_str , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yyout = _out_str ; +} + +int yyget_debug (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yy_flex_debug; +} + +void 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 * yyget_lval (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yylval; +} + +void yyset_lval (YYSTYPE * yylval_param , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yylval = yylval_param; +} + +YYLTYPE *yyget_lloc (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yylloc; +} + +void yyset_lloc (YYLTYPE * yylloc_param , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yylloc = yylloc_param; +} + +/* User-visible API */ + +/* 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 yylex_init(yyscan_t* ptr_yy_globals) +{ + if (ptr_yy_globals == NULL){ + errno = EINVAL; + return 1; + } + + *ptr_yy_globals = (yyscan_t) 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 ); +} + +/* yylex_init_extra has the same functionality as 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 yyalloc in + * the yyextra field. + */ +int yylex_init_extra( YY_EXTRA_TYPE yy_user_defined, yyscan_t* ptr_yy_globals ) +{ + struct yyguts_t dummy_yyguts; + + yyset_extra (yy_user_defined, &dummy_yyguts); + + if (ptr_yy_globals == NULL){ + errno = EINVAL; + return 1; + } + + *ptr_yy_globals = (yyscan_t) 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)); + + 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 yylex_destroy(), so don't allocate here. + */ + + yyg->yy_buffer_stack = NULL; + yyg->yy_buffer_stack_top = 0; + yyg->yy_buffer_stack_max = 0; + yyg->yy_c_buf_p = NULL; + 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 = NULL; + yyout = NULL; +#endif + + /* For future reference: Set errno on error, since we are called by + * yylex_init() + */ + return 0; +} + +/* yylex_destroy is for both reentrant and non-reentrant scanners. */ +int yylex_destroy (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + /* Pop the buffer stack, destroying each element. */ + while(YY_CURRENT_BUFFER){ + yy_delete_buffer( YY_CURRENT_BUFFER , yyscanner ); + YY_CURRENT_BUFFER_LVALUE = NULL; + yypop_buffer_state(yyscanner); + } + + /* Destroy the stack itself. */ + yyfree(yyg->yy_buffer_stack , yyscanner); + yyg->yy_buffer_stack = NULL; + + /* Destroy the start condition stack. */ + 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 + * yylex() is called, initialization will occur. */ + yy_init_globals( yyscanner); + + /* Destroy the main struct (reentrant only). */ + yyfree ( yyscanner , yyscanner ); + yyscanner = NULL; + return 0; +} + +/* + * Internal utility routines. + */ + +#ifndef yytext_ptr +static void yy_flex_strncpy (char* s1, const char * s2, int n , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + (void)yyg; + + int i; + for ( i = 0; i < n; ++i ) + s1[i] = s2[i]; +} +#endif + +#ifdef YY_NEED_STRLEN +static int yy_flex_strlen (const char * s , yyscan_t yyscanner) +{ + int n; + for ( n = 0; s[n]; ++n ) + ; + + return n; +} +#endif + +void *yyalloc (yy_size_t size , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + (void)yyg; + return malloc(size); +} + +void *yyrealloc (void * ptr, yy_size_t size , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + (void)yyg; + + /* 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 realloc(ptr, size); +} + +void yyfree (void * ptr , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + (void)yyg; + free( (char *) ptr ); /* see yyrealloc() for (char *) cast */ +} + +#define YYTABLES_NAME "yytables" + +#line 147 "src/core/io/pajek-lexer.l" + + diff --git a/src/rigraph/core/io/pajek-lexer.h b/src/rigraph/core/io/pajek-lexer.h new file mode 100644 index 0000000..647317b --- /dev/null +++ b/src/rigraph/core/io/pajek-lexer.h @@ -0,0 +1,736 @@ +#ifndef igraph_pajek_yyHEADER_H +#define igraph_pajek_yyHEADER_H 1 +#define igraph_pajek_yyIN_HEADER 1 + +#line 6 "src/core/io/pajek-lexer.h" + +#line 8 "src/core/io/pajek-lexer.h" + +#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 6 +#define YY_FLEX_SUBMINOR_VERSION 4 +#if YY_FLEX_SUBMINOR_VERSION > 0 +#define FLEX_BETA +#endif + +#ifdef yy_create_buffer +#define igraph_pajek_yy_create_buffer_ALREADY_DEFINED +#else +#define yy_create_buffer igraph_pajek_yy_create_buffer +#endif + +#ifdef yy_delete_buffer +#define igraph_pajek_yy_delete_buffer_ALREADY_DEFINED +#else +#define yy_delete_buffer igraph_pajek_yy_delete_buffer +#endif + +#ifdef yy_scan_buffer +#define igraph_pajek_yy_scan_buffer_ALREADY_DEFINED +#else +#define yy_scan_buffer igraph_pajek_yy_scan_buffer +#endif + +#ifdef yy_scan_string +#define igraph_pajek_yy_scan_string_ALREADY_DEFINED +#else +#define yy_scan_string igraph_pajek_yy_scan_string +#endif + +#ifdef yy_scan_bytes +#define igraph_pajek_yy_scan_bytes_ALREADY_DEFINED +#else +#define yy_scan_bytes igraph_pajek_yy_scan_bytes +#endif + +#ifdef yy_init_buffer +#define igraph_pajek_yy_init_buffer_ALREADY_DEFINED +#else +#define yy_init_buffer igraph_pajek_yy_init_buffer +#endif + +#ifdef yy_flush_buffer +#define igraph_pajek_yy_flush_buffer_ALREADY_DEFINED +#else +#define yy_flush_buffer igraph_pajek_yy_flush_buffer +#endif + +#ifdef yy_load_buffer_state +#define igraph_pajek_yy_load_buffer_state_ALREADY_DEFINED +#else +#define yy_load_buffer_state igraph_pajek_yy_load_buffer_state +#endif + +#ifdef yy_switch_to_buffer +#define igraph_pajek_yy_switch_to_buffer_ALREADY_DEFINED +#else +#define yy_switch_to_buffer igraph_pajek_yy_switch_to_buffer +#endif + +#ifdef yypush_buffer_state +#define igraph_pajek_yypush_buffer_state_ALREADY_DEFINED +#else +#define yypush_buffer_state igraph_pajek_yypush_buffer_state +#endif + +#ifdef yypop_buffer_state +#define igraph_pajek_yypop_buffer_state_ALREADY_DEFINED +#else +#define yypop_buffer_state igraph_pajek_yypop_buffer_state +#endif + +#ifdef yyensure_buffer_stack +#define igraph_pajek_yyensure_buffer_stack_ALREADY_DEFINED +#else +#define yyensure_buffer_stack igraph_pajek_yyensure_buffer_stack +#endif + +#ifdef yylex +#define igraph_pajek_yylex_ALREADY_DEFINED +#else +#define yylex igraph_pajek_yylex +#endif + +#ifdef yyrestart +#define igraph_pajek_yyrestart_ALREADY_DEFINED +#else +#define yyrestart igraph_pajek_yyrestart +#endif + +#ifdef yylex_init +#define igraph_pajek_yylex_init_ALREADY_DEFINED +#else +#define yylex_init igraph_pajek_yylex_init +#endif + +#ifdef yylex_init_extra +#define igraph_pajek_yylex_init_extra_ALREADY_DEFINED +#else +#define yylex_init_extra igraph_pajek_yylex_init_extra +#endif + +#ifdef yylex_destroy +#define igraph_pajek_yylex_destroy_ALREADY_DEFINED +#else +#define yylex_destroy igraph_pajek_yylex_destroy +#endif + +#ifdef yyget_debug +#define igraph_pajek_yyget_debug_ALREADY_DEFINED +#else +#define yyget_debug igraph_pajek_yyget_debug +#endif + +#ifdef yyset_debug +#define igraph_pajek_yyset_debug_ALREADY_DEFINED +#else +#define yyset_debug igraph_pajek_yyset_debug +#endif + +#ifdef yyget_extra +#define igraph_pajek_yyget_extra_ALREADY_DEFINED +#else +#define yyget_extra igraph_pajek_yyget_extra +#endif + +#ifdef yyset_extra +#define igraph_pajek_yyset_extra_ALREADY_DEFINED +#else +#define yyset_extra igraph_pajek_yyset_extra +#endif + +#ifdef yyget_in +#define igraph_pajek_yyget_in_ALREADY_DEFINED +#else +#define yyget_in igraph_pajek_yyget_in +#endif + +#ifdef yyset_in +#define igraph_pajek_yyset_in_ALREADY_DEFINED +#else +#define yyset_in igraph_pajek_yyset_in +#endif + +#ifdef yyget_out +#define igraph_pajek_yyget_out_ALREADY_DEFINED +#else +#define yyget_out igraph_pajek_yyget_out +#endif + +#ifdef yyset_out +#define igraph_pajek_yyset_out_ALREADY_DEFINED +#else +#define yyset_out igraph_pajek_yyset_out +#endif + +#ifdef yyget_leng +#define igraph_pajek_yyget_leng_ALREADY_DEFINED +#else +#define yyget_leng igraph_pajek_yyget_leng +#endif + +#ifdef yyget_text +#define igraph_pajek_yyget_text_ALREADY_DEFINED +#else +#define yyget_text igraph_pajek_yyget_text +#endif + +#ifdef yyget_lineno +#define igraph_pajek_yyget_lineno_ALREADY_DEFINED +#else +#define yyget_lineno igraph_pajek_yyget_lineno +#endif + +#ifdef yyset_lineno +#define igraph_pajek_yyset_lineno_ALREADY_DEFINED +#else +#define yyset_lineno igraph_pajek_yyset_lineno +#endif + +#ifdef yyget_column +#define igraph_pajek_yyget_column_ALREADY_DEFINED +#else +#define yyget_column igraph_pajek_yyget_column +#endif + +#ifdef yyset_column +#define igraph_pajek_yyset_column_ALREADY_DEFINED +#else +#define yyset_column igraph_pajek_yyset_column +#endif + +#ifdef yywrap +#define igraph_pajek_yywrap_ALREADY_DEFINED +#else +#define yywrap igraph_pajek_yywrap +#endif + +#ifdef yyget_lval +#define igraph_pajek_yyget_lval_ALREADY_DEFINED +#else +#define yyget_lval igraph_pajek_yyget_lval +#endif + +#ifdef yyset_lval +#define igraph_pajek_yyset_lval_ALREADY_DEFINED +#else +#define yyset_lval igraph_pajek_yyset_lval +#endif + +#ifdef yyget_lloc +#define igraph_pajek_yyget_lloc_ALREADY_DEFINED +#else +#define yyget_lloc igraph_pajek_yyget_lloc +#endif + +#ifdef yyset_lloc +#define igraph_pajek_yyset_lloc_ALREADY_DEFINED +#else +#define yyset_lloc igraph_pajek_yyset_lloc +#endif + +#ifdef yyalloc +#define igraph_pajek_yyalloc_ALREADY_DEFINED +#else +#define yyalloc igraph_pajek_yyalloc +#endif + +#ifdef yyrealloc +#define igraph_pajek_yyrealloc_ALREADY_DEFINED +#else +#define yyrealloc igraph_pajek_yyrealloc +#endif + +#ifdef yyfree +#define igraph_pajek_yyfree_ALREADY_DEFINED +#else +#define yyfree igraph_pajek_yyfree +#endif + +/* First, we deal with platform-specific or compiler-specific issues. */ + +/* begin standard C headers. */ +#include +#include +#include +#include + +/* end standard C headers. */ + +/* flex integer type definitions */ + +#ifndef FLEXINT_H +#define FLEXINT_H + +/* C99 systems have . 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 +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; +#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; + +/* 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 + +#ifndef SIZE_MAX +#define SIZE_MAX (~(size_t)0) +#endif + +#endif /* ! C99 */ + +#endif /* ! FLEXINT_H */ + +/* begin standard C++ headers. */ + +/* TODO: this is always defined, so inline it */ +#define yyconst const + +#if defined(__GNUC__) && __GNUC__ >= 3 +#define yynoreturn __attribute__((__noreturn__)) +#else +#define yynoreturn +#endif + +/* 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 + +/* Size of default input buffer. */ +#ifndef YY_BUF_SIZE +#ifdef __ia64__ +/* On IA-64, the buffer size is 16k, not 8k. + * Moreover, YY_BUF_SIZE is 2*YY_READ_BUF_SIZE in the general case. + * Ditto for the __ia64__ case accordingly. + */ +#define YY_BUF_SIZE 32768 +#else +#define YY_BUF_SIZE 16384 +#endif /* __ia64__ */ +#endif + +#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 + +#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. + */ + int yy_buf_size; + + /* Number of characters read into yy_ch_buf, not including EOB + * characters. + */ + int 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; + + }; +#endif /* !YY_STRUCT_YY_BUFFER_STATE */ + +void yyrestart ( FILE *input_file , yyscan_t yyscanner ); +void yy_switch_to_buffer ( YY_BUFFER_STATE new_buffer , yyscan_t yyscanner ); +YY_BUFFER_STATE yy_create_buffer ( FILE *file, int size , yyscan_t yyscanner ); +void yy_delete_buffer ( YY_BUFFER_STATE b , yyscan_t yyscanner ); +void yy_flush_buffer ( YY_BUFFER_STATE b , yyscan_t yyscanner ); +void yypush_buffer_state ( YY_BUFFER_STATE new_buffer , yyscan_t yyscanner ); +void yypop_buffer_state ( yyscan_t yyscanner ); + +YY_BUFFER_STATE yy_scan_buffer ( char *base, yy_size_t size , yyscan_t yyscanner ); +YY_BUFFER_STATE yy_scan_string ( const char *yy_str , yyscan_t yyscanner ); +YY_BUFFER_STATE yy_scan_bytes ( const char *bytes, int len , yyscan_t yyscanner ); + +void *yyalloc ( yy_size_t , yyscan_t yyscanner ); +void *yyrealloc ( void *, yy_size_t , yyscan_t yyscanner ); +void yyfree ( void * , yyscan_t yyscanner ); + +/* Begin user sect3 */ + +#define igraph_pajek_yywrap(yyscanner) (/*CONSTCOND*/1) +#define YY_SKIP_YYWRAP + +#define yytext_ptr yytext_r + +#ifdef YY_HEADER_EXPORT_START_CONDITIONS +#define INITIAL 0 + +#endif + +#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 +#endif + +#ifndef YY_EXTRA_TYPE +#define YY_EXTRA_TYPE void * +#endif + +int yylex_init (yyscan_t* scanner); + +int 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 yylex_destroy ( yyscan_t yyscanner ); + +int yyget_debug ( yyscan_t yyscanner ); + +void yyset_debug ( int debug_flag , yyscan_t yyscanner ); + +YY_EXTRA_TYPE yyget_extra ( yyscan_t yyscanner ); + +void yyset_extra ( YY_EXTRA_TYPE user_defined , yyscan_t yyscanner ); + +FILE *yyget_in ( yyscan_t yyscanner ); + +void yyset_in ( FILE * _in_str , yyscan_t yyscanner ); + +FILE *yyget_out ( yyscan_t yyscanner ); + +void yyset_out ( FILE * _out_str , yyscan_t yyscanner ); + + int yyget_leng ( yyscan_t yyscanner ); + +char *yyget_text ( yyscan_t yyscanner ); + +int yyget_lineno ( yyscan_t yyscanner ); + +void yyset_lineno ( int _line_number , yyscan_t yyscanner ); + +int yyget_column ( yyscan_t yyscanner ); + +void yyset_column ( int _column_no , yyscan_t yyscanner ); + +YYSTYPE * yyget_lval ( yyscan_t yyscanner ); + +void yyset_lval ( YYSTYPE * yylval_param , yyscan_t yyscanner ); + + YYLTYPE *yyget_lloc ( yyscan_t yyscanner ); + + void 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 yywrap ( yyscan_t yyscanner ); +#else +extern int yywrap ( yyscan_t yyscanner ); +#endif +#endif + +#ifndef yytext_ptr +static void yy_flex_strncpy ( char *, const char *, int , yyscan_t yyscanner); +#endif + +#ifdef YY_NEED_STRLEN +static int yy_flex_strlen ( const char * , yyscan_t yyscanner); +#endif + +#ifndef YY_NO_INPUT + +#endif + +/* Amount of stuff to slurp up with each read. */ +#ifndef YY_READ_BUF_SIZE +#ifdef __ia64__ +/* On IA-64, the buffer size is 16k, not 8k */ +#define YY_READ_BUF_SIZE 16384 +#else +#define YY_READ_BUF_SIZE 8192 +#endif /* __ia64__ */ +#endif + +/* Number of entries by which start-condition stack grows. */ +#ifndef YY_START_STACK_INCR +#define YY_START_STACK_INCR 25 +#endif + +/* 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 yylex \ + (YYSTYPE * yylval_param, YYLTYPE * yylloc_param , yyscan_t yyscanner); + +#define YY_DECL int yylex \ + (YYSTYPE * yylval_param, YYLTYPE * yylloc_param , yyscan_t yyscanner) +#endif /* !YY_DECL */ + +/* yy_get_previous_state - get the state just before the EOB char was reached */ + +#undef YY_NEW_FILE +#undef YY_FLUSH_BUFFER +#undef yy_set_bol +#undef yy_new_buffer +#undef yy_set_interactive +#undef YY_DO_BEFORE_ACTION + +#ifdef YY_DECL_IS_OURS +#undef YY_DECL_IS_OURS +#undef YY_DECL +#endif + +#ifndef igraph_pajek_yy_create_buffer_ALREADY_DEFINED +#undef yy_create_buffer +#endif +#ifndef igraph_pajek_yy_delete_buffer_ALREADY_DEFINED +#undef yy_delete_buffer +#endif +#ifndef igraph_pajek_yy_scan_buffer_ALREADY_DEFINED +#undef yy_scan_buffer +#endif +#ifndef igraph_pajek_yy_scan_string_ALREADY_DEFINED +#undef yy_scan_string +#endif +#ifndef igraph_pajek_yy_scan_bytes_ALREADY_DEFINED +#undef yy_scan_bytes +#endif +#ifndef igraph_pajek_yy_init_buffer_ALREADY_DEFINED +#undef yy_init_buffer +#endif +#ifndef igraph_pajek_yy_flush_buffer_ALREADY_DEFINED +#undef yy_flush_buffer +#endif +#ifndef igraph_pajek_yy_load_buffer_state_ALREADY_DEFINED +#undef yy_load_buffer_state +#endif +#ifndef igraph_pajek_yy_switch_to_buffer_ALREADY_DEFINED +#undef yy_switch_to_buffer +#endif +#ifndef igraph_pajek_yypush_buffer_state_ALREADY_DEFINED +#undef yypush_buffer_state +#endif +#ifndef igraph_pajek_yypop_buffer_state_ALREADY_DEFINED +#undef yypop_buffer_state +#endif +#ifndef igraph_pajek_yyensure_buffer_stack_ALREADY_DEFINED +#undef yyensure_buffer_stack +#endif +#ifndef igraph_pajek_yylex_ALREADY_DEFINED +#undef yylex +#endif +#ifndef igraph_pajek_yyrestart_ALREADY_DEFINED +#undef yyrestart +#endif +#ifndef igraph_pajek_yylex_init_ALREADY_DEFINED +#undef yylex_init +#endif +#ifndef igraph_pajek_yylex_init_extra_ALREADY_DEFINED +#undef yylex_init_extra +#endif +#ifndef igraph_pajek_yylex_destroy_ALREADY_DEFINED +#undef yylex_destroy +#endif +#ifndef igraph_pajek_yyget_debug_ALREADY_DEFINED +#undef yyget_debug +#endif +#ifndef igraph_pajek_yyset_debug_ALREADY_DEFINED +#undef yyset_debug +#endif +#ifndef igraph_pajek_yyget_extra_ALREADY_DEFINED +#undef yyget_extra +#endif +#ifndef igraph_pajek_yyset_extra_ALREADY_DEFINED +#undef yyset_extra +#endif +#ifndef igraph_pajek_yyget_in_ALREADY_DEFINED +#undef yyget_in +#endif +#ifndef igraph_pajek_yyset_in_ALREADY_DEFINED +#undef yyset_in +#endif +#ifndef igraph_pajek_yyget_out_ALREADY_DEFINED +#undef yyget_out +#endif +#ifndef igraph_pajek_yyset_out_ALREADY_DEFINED +#undef yyset_out +#endif +#ifndef igraph_pajek_yyget_leng_ALREADY_DEFINED +#undef yyget_leng +#endif +#ifndef igraph_pajek_yyget_text_ALREADY_DEFINED +#undef yyget_text +#endif +#ifndef igraph_pajek_yyget_lineno_ALREADY_DEFINED +#undef yyget_lineno +#endif +#ifndef igraph_pajek_yyset_lineno_ALREADY_DEFINED +#undef yyset_lineno +#endif +#ifndef igraph_pajek_yyget_column_ALREADY_DEFINED +#undef yyget_column +#endif +#ifndef igraph_pajek_yyset_column_ALREADY_DEFINED +#undef yyset_column +#endif +#ifndef igraph_pajek_yywrap_ALREADY_DEFINED +#undef yywrap +#endif +#ifndef igraph_pajek_yyget_lval_ALREADY_DEFINED +#undef yyget_lval +#endif +#ifndef igraph_pajek_yyset_lval_ALREADY_DEFINED +#undef yyset_lval +#endif +#ifndef igraph_pajek_yyget_lloc_ALREADY_DEFINED +#undef yyget_lloc +#endif +#ifndef igraph_pajek_yyset_lloc_ALREADY_DEFINED +#undef yyset_lloc +#endif +#ifndef igraph_pajek_yyalloc_ALREADY_DEFINED +#undef yyalloc +#endif +#ifndef igraph_pajek_yyrealloc_ALREADY_DEFINED +#undef yyrealloc +#endif +#ifndef igraph_pajek_yyfree_ALREADY_DEFINED +#undef yyfree +#endif +#ifndef igraph_pajek_yytext_ALREADY_DEFINED +#undef yytext +#endif +#ifndef igraph_pajek_yyleng_ALREADY_DEFINED +#undef yyleng +#endif +#ifndef igraph_pajek_yyin_ALREADY_DEFINED +#undef yyin +#endif +#ifndef igraph_pajek_yyout_ALREADY_DEFINED +#undef yyout +#endif +#ifndef igraph_pajek_yy_flex_debug_ALREADY_DEFINED +#undef yy_flex_debug +#endif +#ifndef igraph_pajek_yylineno_ALREADY_DEFINED +#undef yylineno +#endif +#ifndef igraph_pajek_yytables_fload_ALREADY_DEFINED +#undef yytables_fload +#endif +#ifndef igraph_pajek_yytables_destroy_ALREADY_DEFINED +#undef yytables_destroy +#endif +#ifndef igraph_pajek_yyTABLES_NAME_ALREADY_DEFINED +#undef yyTABLES_NAME +#endif + +#line 147 "src/core/io/pajek-lexer.l" + + +#line 735 "src/core/io/pajek-lexer.h" +#undef igraph_pajek_yyIN_HEADER +#endif /* igraph_pajek_yyHEADER_H */ diff --git a/src/rigraph/core/io/pajek-lexer.l b/src/rigraph/core/io/pajek-lexer.l new file mode 100644 index 0000000..4ba29dc --- /dev/null +++ b/src/rigraph/core/io/pajek-lexer.l @@ -0,0 +1,147 @@ +/* + IGraph library. + Copyright (C) 2006-2012 Gabor Csardi + 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 + +*/ + +%{ + +/* + IGraph library. + Copyright (C) 2006-2012 Gabor Csardi + 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 + +#include "io/pajek-header.h" +#include "io/parsers/pajek-parser.h" + +#define YY_EXTRA_TYPE igraph_i_pajek_parsedata_t* +#define YY_USER_ACTION yylloc->first_line = yylineno; +#define YY_FATAL_ERROR(msg) IGRAPH_FATAL("Error in Pajek parser: " # msg) +#ifdef USING_R +#define fprintf(file, msg, ...) (1) +#ifdef stdout +# undef stdout +#endif +#define stdout 0 +#endif +%} + +%option noyywrap +%option prefix="igraph_pajek_yy" +%option nounput +%option noinput +%option nodefault +%option reentrant +%option bison-bridge +%option bison-locations + +digit [0-9] +word [^ \t\r\n\0] + +%% + +[ \t]+ { } +%[^\n]*\n[\r]* { } +%[^\n]*\r[\n]* { } +\*[Nn][eE][Tt] { return NETWORKLINE; } +\*[Nn][Ee][Tt][Ww][Oo][Rr][Kk] { return NETWORKLINE; } +\*[Vv][Ee][Rr][Tt][Ii][Cc][Ee][Ss] { return VERTICESLINE; } +\*[Aa][Rr][Cc][Ss] { return ARCSLINE; } +\*[Ee][Dd][Gg][Ee][Ss] { return EDGESLINE; } +\*[Aa][Rr][Cc][Ss][Ll][Ii][Ss][Tt] { return ARCSLISTLINE; } +\*[Ee][Dd][Gg][Ee][Ss][Ll][Ii][Ss][Tt] { return EDGESLISTLINE; } +\*[Mm][Aa][Tt][Rr][Ii][Xx] { return MATRIXLINE; } +\n\r|\r\n|\n|\r { yyextra->mode=0; return NEWLINE; } +\"[^\"]*\" { return QSTR; } +\([^\)]*\) { return PSTR; } +\-?{digit}+(\.{digit}+)?([eE](\+|\-)?{digit}+)? { + return NUM; } + +[Xx]_[Ff][Aa][Cc][Tt]/[ \t\n\r] { if (yyextra->mode==1) { return VP_X_FACT; } else { return ALNUM; } } +[Yy]_[Ff][Aa][Cc][Tt]/[ \t\n\r] { if (yyextra->mode==1) { return VP_Y_FACT; } else { return ALNUM; } } +[Ii][Cc]/[ \t\n\r] { if (yyextra->mode==1) { return VP_IC; } else { return ALNUM; } } +[Bb][Cc]/[ \t\n\r] { if (yyextra->mode==1) { return VP_BC; } else { return ALNUM; } } +[Bb][Ww]/[ \t\n\r] { if (yyextra->mode==1) { return VP_BW; } else { return ALNUM; } } +[Pp][Hh][Ii]/[ \t\n\r] { if (yyextra->mode==1) { return VP_PHI; } else { return ALNUM; } } +[Rr]/[ \t\n\r] { if (yyextra->mode==1) { return VP_R; } else { return ALNUM; } } +[Qq]/[ \t\n\r] { if (yyextra->mode==1) { return VP_Q; } else { return ALNUM; } } +[Ff][Oo][Nn][Tt]/[ \t\n\r] { if (yyextra->mode==1) { return VP_FONT; } else { return ALNUM; } } +[Uu][Rr][Ll]/[ \t\n\r] { if (yyextra->mode==1) { return VP_URL; } else { return ALNUM; } } + +[Cc]/[ \t\n\r] { if (yyextra->mode==2) { return EP_C; } else { return ALNUM; } } +[Pp]/[ \t\n\r] { if (yyextra->mode==2) { return EP_P; } else { return ALNUM; } } +[Ss]/[ \t\n\r] { if (yyextra->mode==2) { return EP_S; } else { return ALNUM; } } +[Aa]/[ \t\n\r] { if (yyextra->mode==2) { return EP_A; } else { return ALNUM; } } +[Ww]/[ \t\n\r] { if (yyextra->mode==2) { return EP_W; } else { return ALNUM; } } +[Hh]1/[ \t\n\r] { if (yyextra->mode==2) { return EP_H1; } else { return ALNUM; } } +[Hh]2/[ \t\n\r] { if (yyextra->mode==2) { return EP_H2; } else { return ALNUM; } } +[Aa]1/[ \t\n\r] { if (yyextra->mode==2) { return EP_A1; } else { return ALNUM; } } +[Aa]2/[ \t\n\r] { if (yyextra->mode==2) { return EP_A2; } else { return ALNUM; } } +[Kk]1/[ \t\n\r] { if (yyextra->mode==2) { return EP_K1; } else { return ALNUM; } } +[Kk]2/[ \t\n\r] { if (yyextra->mode==2) { return EP_K2; } else { return ALNUM; } } +[Aa][Pp]/[ \t\n\r] { if (yyextra->mode==2) { return EP_AP; } else { return ALNUM; } } +[Ll]/[ \t\n\r] { if (yyextra->mode==2) { return EP_L; } else { return ALNUM; } } +[Ll][Pp]/[ \t\n\r] { if (yyextra->mode==2) { return EP_LP; } else { return ALNUM; } } + +[Ll][Pp][Hh][Ii]/[ \t\n\r] { if (yyextra->mode==1) { return VP_LPHI; } else + if (yyextra->mode==2) { return EP_LPHI; } else { return ALNUM; } } +[Ll][Cc]/[ \t\n\r] { if (yyextra->mode==1) { return VP_LC; } else + if (yyextra->mode==2) { return EP_LC; } else { return ALNUM; } } +[Ll][Rr]/[ \t\n\r] { if (yyextra->mode==1) { return VP_LR; } else + if (yyextra->mode==2) { return EP_LR; } else { return ALNUM; } } +[Ll][Aa]/[ \t\n\r] { if (yyextra->mode==1) { return VP_LA; } else + if (yyextra->mode==2) { return EP_LA; } else { return ALNUM; } } +[Ss][Ii][Zz][Ee]/[ \t\n\r] { if (yyextra->mode==1) { return VP_SIZE; } else + if (yyextra->mode==2) { return EP_SIZE; } else { return ALNUM; } } +[Ff][Oo][Ss]/[ \t\n\r] { if (yyextra->mode==1) { return VP_FOS; } else + if (yyextra->mode==2) { return EP_FOS; } else { return ALNUM; } } + +{word}+ { return ALNUM; } + +<> { if (yyextra->eof) { + yyterminate(); + } else { + yyextra->eof=1; + return NEWLINE; + } + } + +. { return ERROR; } + +%% diff --git a/src/rigraph/core/io/pajek-parser.c b/src/rigraph/core/io/pajek-parser.c new file mode 100644 index 0000000..960009b --- /dev/null +++ b/src/rigraph/core/io/pajek-parser.c @@ -0,0 +1,2882 @@ +/* A Bison parser, made by GNU Bison 3.5.1. */ + +/* Bison implementation for Yacc-like parsers in C + + Copyright (C) 1984, 1989-1990, 2000-2015, 2018-2020 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 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 . */ + +/* 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. */ + +/* Undocumented macros, especially those whose name start with YY_, + are private implementation details. Do not rely on them. */ + +/* Identify Bison output. */ +#define YYBISON 1 + +/* Bison version. */ +#define YYBISON_VERSION "3.5.1" + +/* Skeleton name. */ +#define YYSKELETON_NAME "yacc.c" + +/* Pure parsers. */ +#define YYPURE 1 + +/* Push parsers. */ +#define YYPUSH 0 + +/* Pull parsers. */ +#define YYPULL 1 + + +/* Substitute the variable and function names. */ +#define yyparse igraph_pajek_yyparse +#define yylex igraph_pajek_yylex +#define yyerror igraph_pajek_yyerror +#define yydebug igraph_pajek_yydebug +#define yynerrs igraph_pajek_yynerrs + +/* First part of user prologue. */ +#line 23 "src/core/io/pajek-parser.y" + + +/* + IGraph library. + Copyright (C) 2006-2012 Gabor Csardi + 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 +#include +#include + +#include "igraph_types.h" +#include "igraph_memory.h" +#include "igraph_error.h" +#include "igraph_attributes.h" +#include "config.h" + +#include "core/math.h" +#include "io/pajek-header.h" +#include "io/pajek-parser.h" /* it must come first because of YYSTYPE */ +#include "io/pajek-lexer.h" +#include "internal/hacks.h" + +int igraph_pajek_yyerror(YYLTYPE* locp, + igraph_i_pajek_parsedata_t *context, + const char *s); + +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 + + +#line 157 "yy.tab.c" + +# ifndef YY_CAST +# ifdef __cplusplus +# define YY_CAST(Type, Val) static_cast (Val) +# define YY_REINTERPRET_CAST(Type, Val) reinterpret_cast (Val) +# else +# define YY_CAST(Type, Val) ((Type) (Val)) +# define YY_REINTERPRET_CAST(Type, Val) ((Type) (Val)) +# endif +# endif +# ifndef YY_NULLPTR +# if defined __cplusplus +# if 201103L <= __cplusplus +# define YY_NULLPTR nullptr +# else +# define YY_NULLPTR 0 +# endif +# else +# define YY_NULLPTR ((void*)0) +# endif +# endif + +/* Enabling verbose error messages. */ +#ifdef YYERROR_VERBOSE +# undef YYERROR_VERBOSE +# define YYERROR_VERBOSE 1 +#else +# define YYERROR_VERBOSE 1 +#endif + +/* Use api.header.include to #include this header + instead of duplicating it here. */ +#ifndef YY_IGRAPH_PAJEK_YY_YY_TAB_H_INCLUDED +# define YY_IGRAPH_PAJEK_YY_YY_TAB_H_INCLUDED +/* Debug traces. */ +#ifndef YYDEBUG +# define YYDEBUG 0 +#endif +#if YYDEBUG +extern int igraph_pajek_yydebug; +#endif + +/* Token type. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + 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 + +/* Value type. */ +#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED +union YYSTYPE +{ +#line 115 "src/core/io/pajek-parser.y" + + long int intnum; + double realnum; + struct { + char *str; + int len; + } string; + +#line 270 "yy.tab.c" + +}; +typedef union YYSTYPE YYSTYPE; +# define YYSTYPE_IS_TRIVIAL 1 +# define YYSTYPE_IS_DECLARED 1 +#endif + +/* Location type. */ +#if ! defined YYLTYPE && ! defined YYLTYPE_IS_DECLARED +typedef struct YYLTYPE YYLTYPE; +struct YYLTYPE +{ + int first_line; + int first_column; + int last_line; + int last_column; +}; +# define YYLTYPE_IS_DECLARED 1 +# define YYLTYPE_IS_TRIVIAL 1 +#endif + + + +int igraph_pajek_yyparse (igraph_i_pajek_parsedata_t* context); + +#endif /* !YY_IGRAPH_PAJEK_YY_YY_TAB_H_INCLUDED */ + + + +#ifdef short +# undef short +#endif + +/* On compilers that do not define __PTRDIFF_MAX__ etc., make sure + and (if available) are included + so that the code can choose integer types of a good width. */ + +#ifndef __PTRDIFF_MAX__ +# include /* INFRINGES ON USER NAME SPACE */ +# if defined __STDC_VERSION__ && 199901 <= __STDC_VERSION__ +# include /* INFRINGES ON USER NAME SPACE */ +# define YY_STDINT_H +# endif +#endif + +/* Narrow types that promote to a signed type and that can represent a + signed or unsigned integer of at least N bits. In tables they can + save space and decrease cache pressure. Promoting to a signed type + helps avoid bugs in integer arithmetic. */ + +#ifdef __INT_LEAST8_MAX__ +typedef __INT_LEAST8_TYPE__ yytype_int8; +#elif defined YY_STDINT_H +typedef int_least8_t yytype_int8; +#else +typedef signed char yytype_int8; +#endif + +#ifdef __INT_LEAST16_MAX__ +typedef __INT_LEAST16_TYPE__ yytype_int16; +#elif defined YY_STDINT_H +typedef int_least16_t yytype_int16; +#else +typedef short yytype_int16; +#endif + +#if defined __UINT_LEAST8_MAX__ && __UINT_LEAST8_MAX__ <= __INT_MAX__ +typedef __UINT_LEAST8_TYPE__ yytype_uint8; +#elif (!defined __UINT_LEAST8_MAX__ && defined YY_STDINT_H \ + && UINT_LEAST8_MAX <= INT_MAX) +typedef uint_least8_t yytype_uint8; +#elif !defined __UINT_LEAST8_MAX__ && UCHAR_MAX <= INT_MAX +typedef unsigned char yytype_uint8; +#else +typedef short yytype_uint8; +#endif + +#if defined __UINT_LEAST16_MAX__ && __UINT_LEAST16_MAX__ <= __INT_MAX__ +typedef __UINT_LEAST16_TYPE__ yytype_uint16; +#elif (!defined __UINT_LEAST16_MAX__ && defined YY_STDINT_H \ + && UINT_LEAST16_MAX <= INT_MAX) +typedef uint_least16_t yytype_uint16; +#elif !defined __UINT_LEAST16_MAX__ && USHRT_MAX <= INT_MAX +typedef unsigned short yytype_uint16; +#else +typedef int yytype_uint16; +#endif + +#ifndef YYPTRDIFF_T +# if defined __PTRDIFF_TYPE__ && defined __PTRDIFF_MAX__ +# define YYPTRDIFF_T __PTRDIFF_TYPE__ +# define YYPTRDIFF_MAXIMUM __PTRDIFF_MAX__ +# elif defined PTRDIFF_MAX +# ifndef ptrdiff_t +# include /* INFRINGES ON USER NAME SPACE */ +# endif +# define YYPTRDIFF_T ptrdiff_t +# define YYPTRDIFF_MAXIMUM PTRDIFF_MAX +# else +# define YYPTRDIFF_T long +# define YYPTRDIFF_MAXIMUM LONG_MAX +# endif +#endif + +#ifndef YYSIZE_T +# ifdef __SIZE_TYPE__ +# define YYSIZE_T __SIZE_TYPE__ +# elif defined size_t +# define YYSIZE_T size_t +# elif defined __STDC_VERSION__ && 199901 <= __STDC_VERSION__ +# include /* INFRINGES ON USER NAME SPACE */ +# define YYSIZE_T size_t +# else +# define YYSIZE_T unsigned +# endif +#endif + +#define YYSIZE_MAXIMUM \ + YY_CAST (YYPTRDIFF_T, \ + (YYPTRDIFF_MAXIMUM < YY_CAST (YYSIZE_T, -1) \ + ? YYPTRDIFF_MAXIMUM \ + : YY_CAST (YYSIZE_T, -1))) + +#define YYSIZEOF(X) YY_CAST (YYPTRDIFF_T, sizeof (X)) + +/* Stored state numbers (used for stacks). */ +typedef yytype_uint8 yy_state_t; + +/* State numbers in computations. */ +typedef int yy_state_fast_t; + +#ifndef YY_ +# if defined YYENABLE_NLS && YYENABLE_NLS +# if ENABLE_NLS +# include /* INFRINGES ON USER NAME SPACE */ +# define YY_(Msgid) dgettext ("bison-runtime", Msgid) +# endif +# endif +# ifndef YY_ +# define YY_(Msgid) Msgid +# endif +#endif + +#ifndef YY_ATTRIBUTE_PURE +# if defined __GNUC__ && 2 < __GNUC__ + (96 <= __GNUC_MINOR__) +# define YY_ATTRIBUTE_PURE __attribute__ ((__pure__)) +# else +# define YY_ATTRIBUTE_PURE +# endif +#endif + +#ifndef YY_ATTRIBUTE_UNUSED +# if defined __GNUC__ && 2 < __GNUC__ + (7 <= __GNUC_MINOR__) +# define YY_ATTRIBUTE_UNUSED __attribute__ ((__unused__)) +# else +# define YY_ATTRIBUTE_UNUSED +# 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 + +#if defined __GNUC__ && ! defined __ICC && 407 <= __GNUC__ * 100 + __GNUC_MINOR__ +/* Suppress an incorrect diagnostic about yylval being uninitialized. */ +# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN \ + _Pragma ("GCC diagnostic push") \ + _Pragma ("GCC diagnostic ignored \"-Wuninitialized\"") \ + _Pragma ("GCC diagnostic ignored \"-Wmaybe-uninitialized\"") +# define YY_IGNORE_MAYBE_UNINITIALIZED_END \ + _Pragma ("GCC diagnostic pop") +#else +# define YY_INITIAL_VALUE(Value) Value +#endif +#ifndef YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN +# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN +# define YY_IGNORE_MAYBE_UNINITIALIZED_END +#endif +#ifndef YY_INITIAL_VALUE +# define YY_INITIAL_VALUE(Value) /* Nothing. */ +#endif + +#if defined __cplusplus && defined __GNUC__ && ! defined __ICC && 6 <= __GNUC__ +# define YY_IGNORE_USELESS_CAST_BEGIN \ + _Pragma ("GCC diagnostic push") \ + _Pragma ("GCC diagnostic ignored \"-Wuseless-cast\"") +# define YY_IGNORE_USELESS_CAST_END \ + _Pragma ("GCC diagnostic pop") +#endif +#ifndef YY_IGNORE_USELESS_CAST_BEGIN +# define YY_IGNORE_USELESS_CAST_BEGIN +# define YY_IGNORE_USELESS_CAST_END +#endif + + +#define YY_ASSERT(E) ((void) (0 && (E))) + +#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 /* INFRINGES ON USER NAME SPACE */ +# elif defined _AIX +# define YYSTACK_ALLOC __alloca +# elif defined _MSC_VER +# include /* INFRINGES ON USER NAME SPACE */ +# define alloca _alloca +# else +# define YYSTACK_ALLOC alloca +# if ! defined _ALLOCA_H && ! defined EXIT_SUCCESS +# include /* INFRINGES ON USER NAME SPACE */ + /* Use EXIT_SUCCESS as a witness for stdlib.h. */ +# ifndef EXIT_SUCCESS +# define EXIT_SUCCESS 0 +# endif +# endif +# endif +# endif +# endif + +# ifdef YYSTACK_ALLOC + /* Pacify GCC's 'empty if-body' warning. */ +# define YYSTACK_FREE(Ptr) do { /* empty */; } while (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 EXIT_SUCCESS \ + && ! ((defined YYMALLOC || defined malloc) \ + && (defined YYFREE || defined free))) +# include /* INFRINGES ON USER NAME SPACE */ +# ifndef EXIT_SUCCESS +# define EXIT_SUCCESS 0 +# endif +# endif +# ifndef YYMALLOC +# define YYMALLOC malloc +# if ! defined malloc && ! defined EXIT_SUCCESS +void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# ifndef YYFREE +# define YYFREE free +# if ! defined free && ! defined EXIT_SUCCESS +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 +{ + yy_state_t yyss_alloc; + YYSTYPE yyvs_alloc; + YYLTYPE yyls_alloc; +}; + +/* The size of the maximum gap between one aligned stack and the next. */ +# define YYSTACK_GAP_MAXIMUM (YYSIZEOF (union yyalloc) - 1) + +/* The size of an array large to enough to hold all stacks, each with + N elements. */ +# define YYSTACK_BYTES(N) \ + ((N) * (YYSIZEOF (yy_state_t) + YYSIZEOF (YYSTYPE) \ + + YYSIZEOF (YYLTYPE)) \ + + 2 * YYSTACK_GAP_MAXIMUM) + +# define YYCOPY_NEEDED 1 + +/* 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_alloc, Stack) \ + do \ + { \ + YYPTRDIFF_T yynewbytes; \ + YYCOPY (&yyptr->Stack_alloc, Stack, yysize); \ + Stack = &yyptr->Stack_alloc; \ + yynewbytes = yystacksize * YYSIZEOF (*Stack) + YYSTACK_GAP_MAXIMUM; \ + yyptr += yynewbytes / YYSIZEOF (*yyptr); \ + } \ + while (0) + +#endif + +#if defined YYCOPY_NEEDED && YYCOPY_NEEDED +/* Copy COUNT objects from SRC to DST. The source and destination do + not overlap. */ +# ifndef YYCOPY +# if defined __GNUC__ && 1 < __GNUC__ +# define YYCOPY(Dst, Src, Count) \ + __builtin_memcpy (Dst, Src, YY_CAST (YYSIZE_T, (Count)) * sizeof (*(Src))) +# else +# define YYCOPY(Dst, Src, Count) \ + do \ + { \ + YYPTRDIFF_T yyi; \ + for (yyi = 0; yyi < (Count); yyi++) \ + (Dst)[yyi] = (Src)[yyi]; \ + } \ + while (0) +# endif +# endif +#endif /* !YYCOPY_NEEDED */ + +/* 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 +/* YYNSTATES -- Number of states. */ +#define YYNSTATES 207 + +#define YYUNDEFTOK 2 +#define YYMAXUTOK 306 + + +/* YYTRANSLATE(TOKEN-NUM) -- Symbol number corresponding to TOKEN-NUM + as returned by yylex, with out-of-bounds checking. */ +#define YYTRANSLATE(YYX) \ + (0 <= (YYX) && (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK) + +/* YYTRANSLATE[TOKEN-NUM] -- Symbol number corresponding to TOKEN-NUM + as returned by yylex. */ +static const yytype_int8 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 + /* YYRLINE[YYN] -- Source line where rule number YYN was defined. */ +static const yytype_int16 yyrline[] = +{ + 0, 189, 189, 193, 193, 195, 197, 201, 207, 207, + 209, 210, 211, 211, 214, 216, 221, 222, 226, 232, + 232, 236, 236, 239, 240, 243, 246, 251, 256, 261, + 264, 267, 270, 273, 276, 279, 282, 285, 290, 290, + 294, 294, 298, 298, 302, 302, 307, 307, 314, 316, + 316, 316, 316, 316, 316, 318, 319, 321, 321, 323, + 324, 324, 330, 332, 334, 335, 337, 337, 339, 340, + 340, 346, 348, 350, 350, 354, 354, 357, 358, 363, + 366, 369, 372, 375, 378, 381, 384, 387, 390, 393, + 396, 399, 402, 405, 410, 410, 414, 414, 418, 418, + 422, 422, 426, 426, 432, 434, 436, 436, 438, 438, + 440, 440, 442, 444, 449, 451, 451, 453, 453, 455, + 455, 457, 459, 466, 468, 473, 473, 475, 477, 477, + 479, 499, 502, 505, 505, 507, 509, 511 +}; +#endif + +#if YYDEBUG || YYERROR_VERBOSE || 1 +/* 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", YY_NULLPTR +}; +#endif + +# ifdef YYPRINT +/* YYTOKNUM[NUM] -- (External) token number corresponding to the + (internal) symbol number NUM (which must be that of a token). */ +static const yytype_int16 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 + +#define YYPACT_NINF (-167) + +#define yypact_value_is_default(Yyn) \ + ((Yyn) == YYPACT_NINF) + +#define YYTABLE_NINF (-129) + +#define yytable_value_is_error(Yyn) \ + 0 + + /* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing + STATE-NUM. */ +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 +}; + + /* YYDEFACT[STATE-NUM] -- Default reduction number in state STATE-NUM. + Performed when YYTABLE does not 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 +}; + + /* 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 +}; + + /* 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 +}; + + /* YYTABLE[YYPACT[STATE-NUM]] -- What to do in state STATE-NUM. If + positive, shift that token. If negative, reduce the rule whose + number is the opposite. If YYTABLE_NINF, syntax error. */ +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_int8 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 +}; + + /* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ +static const yytype_int8 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 on the right hand side of rule YYN. */ +static const yytype_int8 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 +}; + + +#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 + + +#define YYRECOVERING() (!!yyerrstatus) + +#define YYBACKUP(Token, Value) \ + do \ + if (yychar == YYEMPTY) \ + { \ + yychar = (Token); \ + yylval = (Value); \ + YYPOPSTACK (yylen); \ + yystate = *yyssp; \ + goto yybackup; \ + } \ + else \ + { \ + yyerror (&yylloc, context, YY_("syntax error: cannot back up")); \ + YYERROR; \ + } \ + while (0) + +/* Error token number */ +#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). */ + +#ifndef YYLLOC_DEFAULT +# define YYLLOC_DEFAULT(Current, Rhs, N) \ + do \ + if (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 (0) +#endif + +#define YYRHSLOC(Rhs, K) ((Rhs)[K]) + + +/* Enable debugging if requested. */ +#if YYDEBUG + +# ifndef YYFPRINTF +# include /* INFRINGES ON USER NAME SPACE */ +# define YYFPRINTF fprintf +# endif + +# define YYDPRINTF(Args) \ +do { \ + if (yydebug) \ + YYFPRINTF Args; \ +} while (0) + + +/* 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 + +/* Print *YYLOCP on YYO. Private, do not rely on its existence. */ + +YY_ATTRIBUTE_UNUSED +static int +yy_location_print_ (FILE *yyo, YYLTYPE const * const yylocp) +{ + int res = 0; + int end_col = 0 != yylocp->last_column ? yylocp->last_column - 1 : 0; + if (0 <= yylocp->first_line) + { + res += YYFPRINTF (yyo, "%d", yylocp->first_line); + if (0 <= yylocp->first_column) + res += YYFPRINTF (yyo, ".%d", yylocp->first_column); + } + if (0 <= yylocp->last_line) + { + if (yylocp->first_line < yylocp->last_line) + { + res += YYFPRINTF (yyo, "-%d", yylocp->last_line); + if (0 <= end_col) + res += YYFPRINTF (yyo, ".%d", end_col); + } + else if (0 <= end_col && yylocp->first_column < end_col) + res += YYFPRINTF (yyo, "-%d", end_col); + } + return res; + } + +# define YY_LOCATION_PRINT(File, Loc) \ + yy_location_print_ (File, &(Loc)) + +# else +# define YY_LOCATION_PRINT(File, Loc) ((void) 0) +# endif +#endif + + +# 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 (0) + + +/*-----------------------------------. +| Print this symbol's value on YYO. | +`-----------------------------------*/ + +static void +yy_symbol_value_print (FILE *yyo, int yytype, YYSTYPE const * const yyvaluep, YYLTYPE const * const yylocationp, igraph_i_pajek_parsedata_t* context) +{ + FILE *yyoutput = yyo; + YYUSE (yyoutput); + YYUSE (yylocationp); + YYUSE (context); + if (!yyvaluep) + return; +# ifdef YYPRINT + if (yytype < YYNTOKENS) + YYPRINT (yyo, yytoknum[yytype], *yyvaluep); +# endif + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + YYUSE (yytype); + YY_IGNORE_MAYBE_UNINITIALIZED_END +} + + +/*---------------------------. +| Print this symbol on YYO. | +`---------------------------*/ + +static void +yy_symbol_print (FILE *yyo, int yytype, YYSTYPE const * const yyvaluep, YYLTYPE const * const yylocationp, igraph_i_pajek_parsedata_t* context) +{ + YYFPRINTF (yyo, "%s %s (", + yytype < YYNTOKENS ? "token" : "nterm", yytname[yytype]); + + YY_LOCATION_PRINT (yyo, *yylocationp); + YYFPRINTF (yyo, ": "); + yy_symbol_value_print (yyo, yytype, yyvaluep, yylocationp, context); + YYFPRINTF (yyo, ")"); +} + +/*------------------------------------------------------------------. +| yy_stack_print -- Print the state stack from its BOTTOM up to its | +| TOP (included). | +`------------------------------------------------------------------*/ + +static void +yy_stack_print (yy_state_t *yybottom, yy_state_t *yytop) +{ + YYFPRINTF (stderr, "Stack now"); + for (; yybottom <= yytop; yybottom++) + { + int yybot = *yybottom; + YYFPRINTF (stderr, " %d", yybot); + } + YYFPRINTF (stderr, "\n"); +} + +# define YY_STACK_PRINT(Bottom, Top) \ +do { \ + if (yydebug) \ + yy_stack_print ((Bottom), (Top)); \ +} while (0) + + +/*------------------------------------------------. +| Report that the YYRULE is going to be reduced. | +`------------------------------------------------*/ + +static void +yy_reduce_print (yy_state_t *yyssp, YYSTYPE *yyvsp, YYLTYPE *yylsp, int yyrule, igraph_i_pajek_parsedata_t* context) +{ + int yylno = yyrline[yyrule]; + int yynrhs = yyr2[yyrule]; + int yyi; + YYFPRINTF (stderr, "Reducing stack by rule %d (line %d):\n", + yyrule - 1, yylno); + /* The symbols being reduced. */ + for (yyi = 0; yyi < yynrhs; yyi++) + { + YYFPRINTF (stderr, " $%d = ", yyi + 1); + yy_symbol_print (stderr, + yystos[+yyssp[yyi + 1 - yynrhs]], + &yyvsp[(yyi + 1) - (yynrhs)] + , &(yylsp[(yyi + 1) - (yynrhs)]) , context); + YYFPRINTF (stderr, "\n"); + } +} + +# define YY_REDUCE_PRINT(Rule) \ +do { \ + if (yydebug) \ + yy_reduce_print (yyssp, yyvsp, yylsp, Rule, context); \ +} while (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(S) (YY_CAST (YYPTRDIFF_T, strlen (S))) +# else +/* Return the length of YYSTR. */ +static YYPTRDIFF_T +yystrlen (const char *yystr) +{ + YYPTRDIFF_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. */ +static char * +yystpcpy (char *yydest, const char *yysrc) +{ + 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 YYPTRDIFF_T +yytnamerr (char *yyres, const char *yystr) +{ + if (*yystr == '"') + { + YYPTRDIFF_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; + else + goto append; + + append: + default: + if (yyres) + yyres[yyn] = *yyp; + yyn++; + break; + + case '"': + if (yyres) + yyres[yyn] = '\0'; + return yyn; + } + do_not_strip_quotes: ; + } + + if (yyres) + return yystpcpy (yyres, yystr) - yyres; + else + return yystrlen (yystr); +} +# endif + +/* Copy into *YYMSG, which is of size *YYMSG_ALLOC, an error message + about the unexpected token YYTOKEN for the state stack whose top is + YYSSP. + + Return 0 if *YYMSG was successfully written. Return 1 if *YYMSG is + not large enough to hold the message. In that case, also set + *YYMSG_ALLOC to the required number of bytes. Return 2 if the + required number of bytes is too large to store. */ +static int +yysyntax_error (YYPTRDIFF_T *yymsg_alloc, char **yymsg, + yy_state_t *yyssp, int yytoken) +{ + enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 }; + /* Internationalized format string. */ + const char *yyformat = YY_NULLPTR; + /* Arguments of yyformat: reported tokens (one for the "unexpected", + one per "expected"). */ + char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM]; + /* Actual size of YYARG. */ + int yycount = 0; + /* Cumulated lengths of YYARG. */ + YYPTRDIFF_T yysize = 0; + + /* There are many possibilities here to consider: + - If this state is a consistent state with a default action, then + the only way this function was invoked is if the default action + is an error action. In that case, don't check for expected + tokens because there are none. + - The only way there can be no lookahead present (in yychar) is if + this state is a consistent state with a default action. Thus, + detecting the absence of a lookahead is sufficient to determine + that there is no unexpected or expected token to report. In that + case, just report a simple "syntax error". + - Don't assume there isn't a lookahead just because this state is a + consistent state with a default action. There might have been a + previous inconsistent state, consistent state with a non-default + action, or user semantic action that manipulated yychar. + - Of course, the expected token list depends on states to have + correct lookahead information, and it depends on the parser not + to perform extra reductions after fetching a lookahead from the + scanner and before detecting a syntax error. Thus, state merging + (from LALR or IELR) and default reductions corrupt the expected + token list. However, the list is correct for canonical LR with + one exception: it will still contain any token that will not be + accepted due to an error action in a later state. + */ + if (yytoken != YYEMPTY) + { + int yyn = yypact[+*yyssp]; + YYPTRDIFF_T yysize0 = yytnamerr (YY_NULLPTR, yytname[yytoken]); + yysize = yysize0; + yyarg[yycount++] = yytname[yytoken]; + if (!yypact_value_is_default (yyn)) + { + /* Start YYX at -YYN if negative to avoid negative indexes in + YYCHECK. In other words, skip the first -YYN actions for + this state because they are default actions. */ + 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 yyx; + + for (yyx = yyxbegin; yyx < yyxend; ++yyx) + if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR + && !yytable_value_is_error (yytable[yyx + yyn])) + { + if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM) + { + yycount = 1; + yysize = yysize0; + break; + } + yyarg[yycount++] = yytname[yyx]; + { + YYPTRDIFF_T yysize1 + = yysize + yytnamerr (YY_NULLPTR, yytname[yyx]); + if (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM) + yysize = yysize1; + else + return 2; + } + } + } + } + + switch (yycount) + { +# define YYCASE_(N, S) \ + case N: \ + yyformat = S; \ + break + default: /* Avoid compiler warnings. */ + YYCASE_(0, YY_("syntax error")); + YYCASE_(1, YY_("syntax error, unexpected %s")); + YYCASE_(2, YY_("syntax error, unexpected %s, expecting %s")); + YYCASE_(3, YY_("syntax error, unexpected %s, expecting %s or %s")); + YYCASE_(4, YY_("syntax error, unexpected %s, expecting %s or %s or %s")); + YYCASE_(5, YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s")); +# undef YYCASE_ + } + + { + /* Don't count the "%s"s in the final size, but reserve room for + the terminator. */ + YYPTRDIFF_T yysize1 = yysize + (yystrlen (yyformat) - 2 * yycount) + 1; + if (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM) + yysize = yysize1; + else + return 2; + } + + if (*yymsg_alloc < yysize) + { + *yymsg_alloc = 2 * yysize; + if (! (yysize <= *yymsg_alloc + && *yymsg_alloc <= YYSTACK_ALLOC_MAXIMUM)) + *yymsg_alloc = YYSTACK_ALLOC_MAXIMUM; + return 1; + } + + /* 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 = *yymsg; + int yyi = 0; + while ((*yyp = *yyformat) != '\0') + if (*yyp == '%' && yyformat[1] == 's' && yyi < yycount) + { + yyp += yytnamerr (yyp, yyarg[yyi++]); + yyformat += 2; + } + else + { + ++yyp; + ++yyformat; + } + } + return 0; +} +#endif /* YYERROR_VERBOSE */ + +/*-----------------------------------------------. +| Release the memory associated to this symbol. | +`-----------------------------------------------*/ + +static void +yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep, YYLTYPE *yylocationp, igraph_i_pajek_parsedata_t* context) +{ + YYUSE (yyvaluep); + YYUSE (yylocationp); + YYUSE (context); + if (!yymsg) + yymsg = "Deleting"; + YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp); + + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + YYUSE (yytype); + YY_IGNORE_MAYBE_UNINITIALIZED_END +} + + + + +/*----------. +| yyparse. | +`----------*/ + +int +yyparse (igraph_i_pajek_parsedata_t* context) +{ +/* The lookahead symbol. */ +int yychar; + + +/* The semantic value of the lookahead symbol. */ +/* Default value used for initialization, for pacifying older GCCs + or non-GCC compilers. */ +YY_INITIAL_VALUE (static YYSTYPE yyval_default;) +YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); + +/* Location data for the lookahead symbol. */ +static YYLTYPE yyloc_default +# if defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL + = { 1, 1, 1, 1 } +# endif +; +YYLTYPE yylloc = yyloc_default; + + /* Number of syntax errors so far. */ + int yynerrs; + + yy_state_fast_t yystate; + /* Number of tokens to shift before error messages enabled. */ + int yyerrstatus; + + /* The stacks and their tools: + 'yyss': related to states. + 'yyvs': related to semantic values. + 'yyls': related to locations. + + Refer to the stacks through separate pointers, to allow yyoverflow + to reallocate them elsewhere. */ + + /* The state stack. */ + yy_state_t yyssa[YYINITDEPTH]; + yy_state_t *yyss; + yy_state_t *yyssp; + + /* The semantic value stack. */ + YYSTYPE yyvsa[YYINITDEPTH]; + YYSTYPE *yyvs; + YYSTYPE *yyvsp; + + /* The location stack. */ + YYLTYPE yylsa[YYINITDEPTH]; + YYLTYPE *yyls; + YYLTYPE *yylsp; + + /* The locations where the error started and ended. */ + YYLTYPE yyerror_range[3]; + + YYPTRDIFF_T yystacksize; + + int yyn; + int yyresult; + /* Lookahead token as an internal (translated) token number. */ + int yytoken = 0; + /* The variables used to return semantic value and location from the + action routines. */ + YYSTYPE yyval; + YYLTYPE yyloc; + +#if YYERROR_VERBOSE + /* Buffer for error messages, and its allocated size. */ + char yymsgbuf[128]; + char *yymsg = yymsgbuf; + YYPTRDIFF_T yymsg_alloc = sizeof yymsgbuf; +#endif + +#define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N), yylsp -= (N)) + + /* The number of symbols on the RHS of the reduced rule. + Keep to zero when no symbol should be popped. */ + int yylen = 0; + + yyssp = yyss = yyssa; + yyvsp = yyvs = yyvsa; + yylsp = yyls = yylsa; + yystacksize = YYINITDEPTH; + + YYDPRINTF ((stderr, "Starting parse\n")); + + yystate = 0; + yyerrstatus = 0; + yynerrs = 0; + yychar = YYEMPTY; /* Cause a token to be read. */ + yylsp[0] = yylloc; + 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 -- set current state (the top of the stack) to yystate. | +`--------------------------------------------------------------------*/ +yysetstate: + YYDPRINTF ((stderr, "Entering state %d\n", yystate)); + YY_ASSERT (0 <= yystate && yystate < YYNSTATES); + YY_IGNORE_USELESS_CAST_BEGIN + *yyssp = YY_CAST (yy_state_t, yystate); + YY_IGNORE_USELESS_CAST_END + + if (yyss + yystacksize - 1 <= yyssp) +#if !defined yyoverflow && !defined YYSTACK_RELOCATE + goto yyexhaustedlab; +#else + { + /* Get the current used size of the three stacks, in elements. */ + YYPTRDIFF_T yysize = yyssp - yyss + 1; + +# if defined 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. */ + yy_state_t *yyss1 = yyss; + YYSTYPE *yyvs1 = yyvs; + 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 * YYSIZEOF (*yyssp), + &yyvs1, yysize * YYSIZEOF (*yyvsp), + &yyls1, yysize * YYSIZEOF (*yylsp), + &yystacksize); + yyss = yyss1; + yyvs = yyvs1; + yyls = yyls1; + } +# else /* defined YYSTACK_RELOCATE */ + /* Extend the stack our own way. */ + if (YYMAXDEPTH <= yystacksize) + goto yyexhaustedlab; + yystacksize *= 2; + if (YYMAXDEPTH < yystacksize) + yystacksize = YYMAXDEPTH; + + { + yy_state_t *yyss1 = yyss; + union yyalloc *yyptr = + YY_CAST (union yyalloc *, + YYSTACK_ALLOC (YY_CAST (YYSIZE_T, YYSTACK_BYTES (yystacksize)))); + if (! yyptr) + goto yyexhaustedlab; + YYSTACK_RELOCATE (yyss_alloc, yyss); + YYSTACK_RELOCATE (yyvs_alloc, yyvs); + YYSTACK_RELOCATE (yyls_alloc, yyls); +# undef YYSTACK_RELOCATE + if (yyss1 != yyssa) + YYSTACK_FREE (yyss1); + } +# endif + + yyssp = yyss + yysize - 1; + yyvsp = yyvs + yysize - 1; + yylsp = yyls + yysize - 1; + + YY_IGNORE_USELESS_CAST_BEGIN + YYDPRINTF ((stderr, "Stack size increased to %ld\n", + YY_CAST (long, yystacksize))); + YY_IGNORE_USELESS_CAST_END + + if (yyss + yystacksize - 1 <= yyssp) + YYABORT; + } +#endif /* !defined yyoverflow && !defined YYSTACK_RELOCATE */ + + if (yystate == YYFINAL) + YYACCEPT; + + goto yybackup; + + +/*-----------. +| yybackup. | +`-----------*/ +yybackup: + /* Do appropriate processing given the current state. Read a + lookahead token if we need one and don't already have one. */ + + /* First try to decide what to do without reference to lookahead token. */ + yyn = yypact[yystate]; + if (yypact_value_is_default (yyn)) + goto yydefault; + + /* Not known => get a lookahead token if don't already have one. */ + + /* YYCHAR is either YYEMPTY or YYEOF or a valid lookahead symbol. */ + if (yychar == YYEMPTY) + { + YYDPRINTF ((stderr, "Reading a token: ")); + yychar = yylex (&yylval, &yylloc, scanner); + } + + 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 (yytable_value_is_error (yyn)) + goto yyerrlab; + yyn = -yyn; + goto yyreduce; + } + + /* Count tokens shifted since error; after three, turn off error + status. */ + if (yyerrstatus) + yyerrstatus--; + + /* Shift the lookahead token. */ + YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc); + yystate = yyn; + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + *++yyvsp = yylval; + YY_IGNORE_MAYBE_UNINITIALIZED_END + *++yylsp = yylloc; + + /* Discard the shifted token. */ + yychar = YYEMPTY; + 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); + yyerror_range[1] = yyloc; + YY_REDUCE_PRINT (yyn); + switch (yyn) + { + case 2: +#line 189 "src/core/io/pajek-parser.y" + { + if (context->vcount2 > 0) { igraph_i_pajek_check_bipartite(context); } + } +#line 1741 "yy.tab.c" + break; + + case 6: +#line 197 "src/core/io/pajek-parser.y" + { + context->vcount=(yyvsp[0].intnum); + context->vcount2=0; + } +#line 1750 "yy.tab.c" + break; + + case 7: +#line 201 "src/core/io/pajek-parser.y" + { + context->vcount=(yyvsp[-1].intnum); + context->vcount2=(yyvsp[0].intnum); + igraph_i_pajek_add_bipartite_type(context); +} +#line 1760 "yy.tab.c" + break; + + case 12: +#line 211 "src/core/io/pajek-parser.y" + { context->actvertex=(yyvsp[0].intnum); } +#line 1766 "yy.tab.c" + break; + + case 13: +#line 211 "src/core/io/pajek-parser.y" + { } +#line 1772 "yy.tab.c" + break; + + case 14: +#line 214 "src/core/io/pajek-parser.y" + { (yyval.intnum)=(yyvsp[0].intnum); context->mode=1; } +#line 1778 "yy.tab.c" + break; + + case 15: +#line 216 "src/core/io/pajek-parser.y" + { + igraph_i_pajek_add_string_vertex_attribute("id", (yyvsp[0].string).str, (yyvsp[0].string).len, context); + igraph_i_pajek_add_string_vertex_attribute("name", (yyvsp[0].string).str, (yyvsp[0].string).len, context); +} +#line 1787 "yy.tab.c" + break; + + case 17: +#line 222 "src/core/io/pajek-parser.y" + { + igraph_i_pajek_add_numeric_vertex_attribute("x", (yyvsp[-1].realnum), context); + igraph_i_pajek_add_numeric_vertex_attribute("y", (yyvsp[0].realnum), context); + } +#line 1796 "yy.tab.c" + break; + + case 18: +#line 226 "src/core/io/pajek-parser.y" + { + igraph_i_pajek_add_numeric_vertex_attribute("x", (yyvsp[-2].realnum), context); + igraph_i_pajek_add_numeric_vertex_attribute("y", (yyvsp[-1].realnum), context); + igraph_i_pajek_add_numeric_vertex_attribute("z", (yyvsp[0].realnum), context); + } +#line 1806 "yy.tab.c" + break; + + case 20: +#line 232 "src/core/io/pajek-parser.y" + { + igraph_i_pajek_add_string_vertex_attribute("shape", (yyvsp[0].string).str, (yyvsp[0].string).len, context); +} +#line 1814 "yy.tab.c" + break; + + case 24: +#line 240 "src/core/io/pajek-parser.y" + { + igraph_i_pajek_add_numeric_vertex_attribute("xfact", (yyvsp[0].realnum), context); + } +#line 1822 "yy.tab.c" + break; + + case 25: +#line 243 "src/core/io/pajek-parser.y" + { + igraph_i_pajek_add_numeric_vertex_attribute("yfact", (yyvsp[0].realnum), context); + } +#line 1830 "yy.tab.c" + break; + + case 26: +#line 246 "src/core/io/pajek-parser.y" + { /* RGB color */ + igraph_i_pajek_add_numeric_vertex_attribute("color-red", (yyvsp[-2].realnum), context); + igraph_i_pajek_add_numeric_vertex_attribute("color-green", (yyvsp[-1].realnum), context); + igraph_i_pajek_add_numeric_vertex_attribute("color-blue", (yyvsp[0].realnum), context); + } +#line 1840 "yy.tab.c" + break; + + case 27: +#line 251 "src/core/io/pajek-parser.y" + { + igraph_i_pajek_add_numeric_vertex_attribute("framecolor-red", (yyvsp[-2].realnum), context); + igraph_i_pajek_add_numeric_vertex_attribute("framecolor-green", (yyvsp[-1].realnum), context); + igraph_i_pajek_add_numeric_vertex_attribute("framecolor-blue", (yyvsp[0].realnum), context); + } +#line 1850 "yy.tab.c" + break; + + case 28: +#line 256 "src/core/io/pajek-parser.y" + { + igraph_i_pajek_add_numeric_vertex_attribute("labelcolor-red", (yyvsp[-2].realnum), context); + igraph_i_pajek_add_numeric_vertex_attribute("labelcolor-green", (yyvsp[-1].realnum), context); + igraph_i_pajek_add_numeric_vertex_attribute("labelcolor-blue", (yyvsp[0].realnum), context); + } +#line 1860 "yy.tab.c" + break; + + case 29: +#line 261 "src/core/io/pajek-parser.y" + { + igraph_i_pajek_add_numeric_vertex_attribute("labeldist", (yyvsp[0].realnum), context); + } +#line 1868 "yy.tab.c" + break; + + case 30: +#line 264 "src/core/io/pajek-parser.y" + { + igraph_i_pajek_add_numeric_vertex_attribute("labeldegree2", (yyvsp[0].realnum), context); + } +#line 1876 "yy.tab.c" + break; + + case 31: +#line 267 "src/core/io/pajek-parser.y" + { + igraph_i_pajek_add_numeric_vertex_attribute("framewidth", (yyvsp[0].realnum), context); + } +#line 1884 "yy.tab.c" + break; + + case 32: +#line 270 "src/core/io/pajek-parser.y" + { + igraph_i_pajek_add_numeric_vertex_attribute("fontsize", (yyvsp[0].realnum), context); + } +#line 1892 "yy.tab.c" + break; + + case 33: +#line 273 "src/core/io/pajek-parser.y" + { + igraph_i_pajek_add_numeric_vertex_attribute("rotation", (yyvsp[0].realnum), context); + } +#line 1900 "yy.tab.c" + break; + + case 34: +#line 276 "src/core/io/pajek-parser.y" + { + igraph_i_pajek_add_numeric_vertex_attribute("radius", (yyvsp[0].realnum), context); + } +#line 1908 "yy.tab.c" + break; + + case 35: +#line 279 "src/core/io/pajek-parser.y" + { + igraph_i_pajek_add_numeric_vertex_attribute("diamondratio", (yyvsp[0].realnum), context); + } +#line 1916 "yy.tab.c" + break; + + case 36: +#line 282 "src/core/io/pajek-parser.y" + { + igraph_i_pajek_add_numeric_vertex_attribute("labeldegree", (yyvsp[0].realnum), context); + } +#line 1924 "yy.tab.c" + break; + + case 37: +#line 285 "src/core/io/pajek-parser.y" + { + igraph_i_pajek_add_numeric_vertex_attribute("vertexsize", (yyvsp[0].realnum), context); + } +#line 1932 "yy.tab.c" + break; + + case 38: +#line 290 "src/core/io/pajek-parser.y" + { context->mode=3; } +#line 1938 "yy.tab.c" + break; + + case 39: +#line 290 "src/core/io/pajek-parser.y" + { + context->mode=1; + igraph_i_pajek_add_string_vertex_attribute("font", (yyvsp[0].string).str, (yyvsp[0].string).len, context); + } +#line 1947 "yy.tab.c" + break; + + case 40: +#line 294 "src/core/io/pajek-parser.y" + { context->mode=3; } +#line 1953 "yy.tab.c" + break; + + case 41: +#line 294 "src/core/io/pajek-parser.y" + { + context->mode=1; + igraph_i_pajek_add_string_vertex_attribute("url", (yyvsp[0].string).str, (yyvsp[0].string).len, context); + } +#line 1962 "yy.tab.c" + break; + + case 42: +#line 298 "src/core/io/pajek-parser.y" + { context->mode=3; } +#line 1968 "yy.tab.c" + break; + + case 43: +#line 298 "src/core/io/pajek-parser.y" + { + context->mode=1; + igraph_i_pajek_add_string_vertex_attribute("color", (yyvsp[0].string).str, (yyvsp[0].string).len, context); + } +#line 1977 "yy.tab.c" + break; + + case 44: +#line 302 "src/core/io/pajek-parser.y" + { context->mode=3; } +#line 1983 "yy.tab.c" + break; + + case 45: +#line 302 "src/core/io/pajek-parser.y" + { + context->mode=1; + igraph_i_pajek_add_string_vertex_attribute("framecolor", + (yyvsp[0].string).str, (yyvsp[0].string).len, context); + } +#line 1993 "yy.tab.c" + break; + + case 46: +#line 307 "src/core/io/pajek-parser.y" + { context->mode=3; } +#line 1999 "yy.tab.c" + break; + + case 47: +#line 307 "src/core/io/pajek-parser.y" + { + context->mode=1; + igraph_i_pajek_add_string_vertex_attribute("labelcolor", + (yyvsp[0].string).str, (yyvsp[0].string).len, context); + } +#line 2009 "yy.tab.c" + break; + + case 48: +#line 314 "src/core/io/pajek-parser.y" + { (yyval.string)=(yyvsp[0].string); } +#line 2015 "yy.tab.c" + break; + + case 55: +#line 318 "src/core/io/pajek-parser.y" + { context->directed=1; } +#line 2021 "yy.tab.c" + break; + + case 56: +#line 319 "src/core/io/pajek-parser.y" + { context->directed=1; } +#line 2027 "yy.tab.c" + break; + + case 60: +#line 324 "src/core/io/pajek-parser.y" + { context->actedge++; + context->mode=2; } +#line 2034 "yy.tab.c" + break; + + case 61: +#line 325 "src/core/io/pajek-parser.y" + { + igraph_vector_push_back(context->vector, (yyvsp[-5].intnum)-1); + igraph_vector_push_back(context->vector, (yyvsp[-4].intnum)-1); } +#line 2042 "yy.tab.c" + break; + + case 64: +#line 334 "src/core/io/pajek-parser.y" + { context->directed=0; } +#line 2048 "yy.tab.c" + break; + + case 65: +#line 335 "src/core/io/pajek-parser.y" + { context->directed=0; } +#line 2054 "yy.tab.c" + break; + + case 69: +#line 340 "src/core/io/pajek-parser.y" + { context->actedge++; + context->mode=2; } +#line 2061 "yy.tab.c" + break; + + case 70: +#line 341 "src/core/io/pajek-parser.y" + { + igraph_vector_push_back(context->vector, (yyvsp[-5].intnum)-1); + igraph_vector_push_back(context->vector, (yyvsp[-4].intnum)-1); } +#line 2069 "yy.tab.c" + break; + + case 74: +#line 350 "src/core/io/pajek-parser.y" + { + igraph_i_pajek_add_numeric_edge_attribute("weight", (yyvsp[0].realnum), context); +} +#line 2077 "yy.tab.c" + break; + + case 78: +#line 358 "src/core/io/pajek-parser.y" + { + igraph_i_pajek_add_numeric_edge_attribute("color-red", (yyvsp[-2].realnum), context); + igraph_i_pajek_add_numeric_edge_attribute("color-green", (yyvsp[-1].realnum), context); + igraph_i_pajek_add_numeric_edge_attribute("color-blue", (yyvsp[0].realnum), context); + } +#line 2087 "yy.tab.c" + break; + + case 79: +#line 363 "src/core/io/pajek-parser.y" + { + igraph_i_pajek_add_numeric_edge_attribute("arrowsize", (yyvsp[0].realnum), context); + } +#line 2095 "yy.tab.c" + break; + + case 80: +#line 366 "src/core/io/pajek-parser.y" + { + igraph_i_pajek_add_numeric_edge_attribute("edgewidth", (yyvsp[0].realnum), context); + } +#line 2103 "yy.tab.c" + break; + + case 81: +#line 369 "src/core/io/pajek-parser.y" + { + igraph_i_pajek_add_numeric_edge_attribute("hook1", (yyvsp[0].realnum), context); + } +#line 2111 "yy.tab.c" + break; + + case 82: +#line 372 "src/core/io/pajek-parser.y" + { + igraph_i_pajek_add_numeric_edge_attribute("hook2", (yyvsp[0].realnum), context); + } +#line 2119 "yy.tab.c" + break; + + case 83: +#line 375 "src/core/io/pajek-parser.y" + { + igraph_i_pajek_add_numeric_edge_attribute("angle1", (yyvsp[0].realnum), context); + } +#line 2127 "yy.tab.c" + break; + + case 84: +#line 378 "src/core/io/pajek-parser.y" + { + igraph_i_pajek_add_numeric_edge_attribute("angle2", (yyvsp[0].realnum), context); + } +#line 2135 "yy.tab.c" + break; + + case 85: +#line 381 "src/core/io/pajek-parser.y" + { + igraph_i_pajek_add_numeric_edge_attribute("velocity1", (yyvsp[0].realnum), context); + } +#line 2143 "yy.tab.c" + break; + + case 86: +#line 384 "src/core/io/pajek-parser.y" + { + igraph_i_pajek_add_numeric_edge_attribute("velocity2", (yyvsp[0].realnum), context); + } +#line 2151 "yy.tab.c" + break; + + case 87: +#line 387 "src/core/io/pajek-parser.y" + { + igraph_i_pajek_add_numeric_edge_attribute("arrowpos", (yyvsp[0].realnum), context); + } +#line 2159 "yy.tab.c" + break; + + case 88: +#line 390 "src/core/io/pajek-parser.y" + { + igraph_i_pajek_add_numeric_edge_attribute("labelpos", (yyvsp[0].realnum), context); + } +#line 2167 "yy.tab.c" + break; + + case 89: +#line 393 "src/core/io/pajek-parser.y" + { + igraph_i_pajek_add_numeric_edge_attribute("labelangle", (yyvsp[0].realnum), context); + } +#line 2175 "yy.tab.c" + break; + + case 90: +#line 396 "src/core/io/pajek-parser.y" + { + igraph_i_pajek_add_numeric_edge_attribute("labelangle2", (yyvsp[0].realnum), context); + } +#line 2183 "yy.tab.c" + break; + + case 91: +#line 399 "src/core/io/pajek-parser.y" + { + igraph_i_pajek_add_numeric_edge_attribute("labeldegree", (yyvsp[0].realnum), context); + } +#line 2191 "yy.tab.c" + break; + + case 92: +#line 402 "src/core/io/pajek-parser.y" + { /* what is this??? */ + igraph_i_pajek_add_numeric_edge_attribute("arrowsize", (yyvsp[0].realnum), context); + } +#line 2199 "yy.tab.c" + break; + + case 93: +#line 405 "src/core/io/pajek-parser.y" + { + igraph_i_pajek_add_numeric_edge_attribute("fontsize", (yyvsp[0].realnum), context); + } +#line 2207 "yy.tab.c" + break; + + case 94: +#line 410 "src/core/io/pajek-parser.y" + { context->mode=4; } +#line 2213 "yy.tab.c" + break; + + case 95: +#line 410 "src/core/io/pajek-parser.y" + { + context->mode=2; + igraph_i_pajek_add_string_edge_attribute("arrowtype", (yyvsp[0].string).str, (yyvsp[0].string).len, context); + } +#line 2222 "yy.tab.c" + break; + + case 96: +#line 414 "src/core/io/pajek-parser.y" + { context->mode=4; } +#line 2228 "yy.tab.c" + break; + + case 97: +#line 414 "src/core/io/pajek-parser.y" + { + context->mode=2; + igraph_i_pajek_add_string_edge_attribute("linepattern", (yyvsp[0].string).str, (yyvsp[0].string).len, context); + } +#line 2237 "yy.tab.c" + break; + + case 98: +#line 418 "src/core/io/pajek-parser.y" + { context->mode=4; } +#line 2243 "yy.tab.c" + break; + + case 99: +#line 418 "src/core/io/pajek-parser.y" + { + context->mode=2; + igraph_i_pajek_add_string_edge_attribute("label", (yyvsp[0].string).str, (yyvsp[0].string).len, context); + } +#line 2252 "yy.tab.c" + break; + + case 100: +#line 422 "src/core/io/pajek-parser.y" + { context->mode=4; } +#line 2258 "yy.tab.c" + break; + + case 101: +#line 422 "src/core/io/pajek-parser.y" + { + context->mode=2; + igraph_i_pajek_add_string_edge_attribute("labelcolor", (yyvsp[0].string).str, (yyvsp[0].string).len, context); + } +#line 2267 "yy.tab.c" + break; + + case 102: +#line 426 "src/core/io/pajek-parser.y" + { context->mode=4; } +#line 2273 "yy.tab.c" + break; + + case 103: +#line 426 "src/core/io/pajek-parser.y" + { + context->mode=2; + igraph_i_pajek_add_string_edge_attribute("color", (yyvsp[0].string).str, (yyvsp[0].string).len, context); + } +#line 2282 "yy.tab.c" + break; + + case 104: +#line 432 "src/core/io/pajek-parser.y" + { context->mode=2; (yyval.string)=(yyvsp[0].string); } +#line 2288 "yy.tab.c" + break; + + case 105: +#line 434 "src/core/io/pajek-parser.y" + { context->directed=1; } +#line 2294 "yy.tab.c" + break; + + case 112: +#line 442 "src/core/io/pajek-parser.y" + { context->mode=0; context->actfrom=labs((yyvsp[0].intnum))-1; } +#line 2300 "yy.tab.c" + break; + + case 113: +#line 444 "src/core/io/pajek-parser.y" + { + igraph_vector_push_back(context->vector, context->actfrom); + igraph_vector_push_back(context->vector, labs((yyvsp[0].intnum))-1); +} +#line 2309 "yy.tab.c" + break; + + case 114: +#line 449 "src/core/io/pajek-parser.y" + { context->directed=0; } +#line 2315 "yy.tab.c" + break; + + case 121: +#line 457 "src/core/io/pajek-parser.y" + { context->mode=0; context->actfrom=labs((yyvsp[0].intnum))-1; } +#line 2321 "yy.tab.c" + break; + + case 122: +#line 459 "src/core/io/pajek-parser.y" + { + igraph_vector_push_back(context->vector, context->actfrom); + igraph_vector_push_back(context->vector, labs((yyvsp[0].intnum))-1); +} +#line 2330 "yy.tab.c" + break; + + case 124: +#line 468 "src/core/io/pajek-parser.y" + { context->actfrom=0; + context->actto=0; + context->directed=(context->vcount2==0); + } +#line 2339 "yy.tab.c" + break; + + case 127: +#line 475 "src/core/io/pajek-parser.y" + { context->actfrom++; context->actto=0; } +#line 2345 "yy.tab.c" + break; + + case 130: +#line 479 "src/core/io/pajek-parser.y" + { + if ((yyvsp[0].realnum) != 0) { + if (context->vcount2==0) { + context->actedge++; + igraph_i_pajek_add_numeric_edge_attribute("weight", (yyvsp[0].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[0].realnum), context); + igraph_vector_push_back(context->vector, context->actfrom); + igraph_vector_push_back(context->vector, + context->vcount2+context->actto); + } + } + context->actto++; +} +#line 2367 "yy.tab.c" + break; + + case 131: +#line 499 "src/core/io/pajek-parser.y" + { (yyval.intnum)=igraph_pajek_get_number(igraph_pajek_yyget_text(scanner), + igraph_pajek_yyget_leng(scanner)); } +#line 2374 "yy.tab.c" + break; + + case 132: +#line 502 "src/core/io/pajek-parser.y" + { (yyval.realnum)=igraph_pajek_get_number(igraph_pajek_yyget_text(scanner), + igraph_pajek_yyget_leng(scanner)); } +#line 2381 "yy.tab.c" + break; + + case 135: +#line 507 "src/core/io/pajek-parser.y" + { (yyval.string).str=igraph_pajek_yyget_text(scanner); + (yyval.string).len=igraph_pajek_yyget_leng(scanner); } +#line 2388 "yy.tab.c" + break; + + case 136: +#line 509 "src/core/io/pajek-parser.y" + { (yyval.string).str=igraph_pajek_yyget_text(scanner); + (yyval.string).len=igraph_pajek_yyget_leng(scanner); } +#line 2395 "yy.tab.c" + break; + + case 137: +#line 511 "src/core/io/pajek-parser.y" + { (yyval.string).str=igraph_pajek_yyget_text(scanner)+1; + (yyval.string).len=igraph_pajek_yyget_leng(scanner)-2; } +#line 2402 "yy.tab.c" + break; + + +#line 2406 "yy.tab.c" + + default: break; + } + /* User semantic actions sometimes alter yychar, and that requires + that yytoken be updated with the new translation. We take the + approach of translating immediately before every use of yytoken. + One alternative is translating here after every semantic action, + but that translation would be missed if the semantic action invokes + YYABORT, YYACCEPT, or YYERROR immediately after altering yychar or + if it invokes YYBACKUP. In the case of YYABORT or YYACCEPT, an + incorrect destructor might then be invoked immediately. In the + case of YYERROR or YYBACKUP, subsequent parser actions might lead + to an incorrect destructor call or verbose syntax error message + before the lookahead is translated. */ + 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. */ + { + const int yylhs = yyr1[yyn] - YYNTOKENS; + const int yyi = yypgoto[yylhs] + *yyssp; + yystate = (0 <= yyi && yyi <= YYLAST && yycheck[yyi] == *yyssp + ? yytable[yyi] + : yydefgoto[yylhs]); + } + + goto yynewstate; + + +/*--------------------------------------. +| yyerrlab -- here on detecting error. | +`--------------------------------------*/ +yyerrlab: + /* Make sure we have latest lookahead translation. See comments at + user semantic actions for why this is necessary. */ + yytoken = yychar == YYEMPTY ? YYEMPTY : YYTRANSLATE (yychar); + + /* If not already recovering from an error, report this error. */ + if (!yyerrstatus) + { + ++yynerrs; +#if ! YYERROR_VERBOSE + yyerror (&yylloc, context, YY_("syntax error")); +#else +# define YYSYNTAX_ERROR yysyntax_error (&yymsg_alloc, &yymsg, \ + yyssp, yytoken) + { + char const *yymsgp = YY_("syntax error"); + int yysyntax_error_status; + yysyntax_error_status = YYSYNTAX_ERROR; + if (yysyntax_error_status == 0) + yymsgp = yymsg; + else if (yysyntax_error_status == 1) + { + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); + yymsg = YY_CAST (char *, YYSTACK_ALLOC (YY_CAST (YYSIZE_T, yymsg_alloc))); + if (!yymsg) + { + yymsg = yymsgbuf; + yymsg_alloc = sizeof yymsgbuf; + yysyntax_error_status = 2; + } + else + { + yysyntax_error_status = YYSYNTAX_ERROR; + yymsgp = yymsg; + } + } + yyerror (&yylloc, context, yymsgp); + if (yysyntax_error_status == 2) + goto yyexhaustedlab; + } +# undef YYSYNTAX_ERROR +#endif + } + + yyerror_range[1] = yylloc; + + if (yyerrstatus == 3) + { + /* If just tried and failed to reuse lookahead 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 lookahead token after shifting the error + token. */ + goto yyerrlab1; + + +/*---------------------------------------------------. +| yyerrorlab -- error raised explicitly by YYERROR. | +`---------------------------------------------------*/ +yyerrorlab: + /* Pacify compilers when the user code never invokes YYERROR and the + label yyerrorlab therefore never appears in user code. */ + if (0) + YYERROR; + + /* Do not reclaim the symbols of the rule whose 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 (!yypact_value_is_default (yyn)) + { + 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[1] = *yylsp; + yydestruct ("Error: popping", + yystos[yystate], yyvsp, yylsp, context); + YYPOPSTACK (1); + yystate = *yyssp; + YY_STACK_PRINT (yyss, yyssp); + } + + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + *++yyvsp = yylval; + YY_IGNORE_MAYBE_UNINITIALIZED_END + + yyerror_range[2] = yylloc; + /* Using YYLLOC is tempting, but would change the location of + the lookahead. YYLOC is available though. */ + YYLLOC_DEFAULT (yyloc, yyerror_range, 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; + + +#if !defined yyoverflow || YYERROR_VERBOSE +/*-------------------------------------------------. +| yyexhaustedlab -- memory exhaustion comes here. | +`-------------------------------------------------*/ +yyexhaustedlab: + yyerror (&yylloc, context, YY_("memory exhausted")); + yyresult = 2; + /* Fall through. */ +#endif + + +/*-----------------------------------------------------. +| yyreturn -- parsing is finished, return the result. | +`-----------------------------------------------------*/ +yyreturn: + if (yychar != YYEMPTY) + { + /* Make sure we have latest lookahead translation. See comments at + user semantic actions for why this is necessary. */ + yytoken = YYTRANSLATE (yychar); + yydestruct ("Cleanup: discarding lookahead", + yytoken, &yylval, &yylloc, context); + } + /* Do not reclaim the symbols of the rule whose 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 + return yyresult; +} +#line 514 "src/core/io/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 (;origsizename=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 (;origsizevertex_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(igraph_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; ivector; + int i, n1=context->vcount2; + int ne=igraph_vector_size(edges); + + for (i=0; i n1 && v2 > n1) ) { + IGRAPH_WARNING("Invalid edge in bipartite graph"); + } + } + + return 0; +} diff --git a/src/rigraph/core/io/pajek-parser.h b/src/rigraph/core/io/pajek-parser.h new file mode 100644 index 0000000..a6c29aa --- /dev/null +++ b/src/rigraph/core/io/pajek-parser.h @@ -0,0 +1,143 @@ +/* A Bison parser, made by GNU Bison 3.5.1. */ + +/* Bison interface for Yacc-like parsers in C + + Copyright (C) 1984, 1989-1990, 2000-2015, 2018-2020 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 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 . */ + +/* 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. */ + +/* Undocumented macros, especially those whose name start with YY_, + are private implementation details. Do not rely on them. */ + +#ifndef YY_IGRAPH_PAJEK_YY_YY_TAB_H_INCLUDED +# define YY_IGRAPH_PAJEK_YY_YY_TAB_H_INCLUDED +/* Debug traces. */ +#ifndef YYDEBUG +# define YYDEBUG 0 +#endif +#if YYDEBUG +extern int igraph_pajek_yydebug; +#endif + +/* Token type. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + 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 + +/* Value type. */ +#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED +union YYSTYPE +{ +#line 115 "src/core/io/pajek-parser.y" + + long int intnum; + double realnum; + struct { + char *str; + int len; + } string; + +#line 118 "yy.tab.h" + +}; +typedef union YYSTYPE YYSTYPE; +# define YYSTYPE_IS_TRIVIAL 1 +# define YYSTYPE_IS_DECLARED 1 +#endif + +/* Location type. */ +#if ! defined YYLTYPE && ! defined YYLTYPE_IS_DECLARED +typedef struct YYLTYPE YYLTYPE; +struct YYLTYPE +{ + int first_line; + int first_column; + int last_line; + int last_column; +}; +# define YYLTYPE_IS_DECLARED 1 +# define YYLTYPE_IS_TRIVIAL 1 +#endif + + + +int igraph_pajek_yyparse (igraph_i_pajek_parsedata_t* context); + +#endif /* !YY_IGRAPH_PAJEK_YY_YY_TAB_H_INCLUDED */ diff --git a/src/rigraph/core/io/pajek-parser.y b/src/rigraph/core/io/pajek-parser.y new file mode 100644 index 0000000..e181ea0 --- /dev/null +++ b/src/rigraph/core/io/pajek-parser.y @@ -0,0 +1,752 @@ +/* + IGraph library. + Copyright (C) 2006-2012 Gabor Csardi + 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 + +*/ + +%{ + +/* + IGraph library. + Copyright (C) 2006-2012 Gabor Csardi + 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 +#include +#include + +#include "igraph_types.h" +#include "igraph_memory.h" +#include "igraph_error.h" +#include "igraph_attributes.h" +#include "config.h" + +#include "core/math.h" +#include "io/pajek-header.h" +#include "io/parsers/pajek-parser.h" /* it must come first because of YYSTYPE */ +#include "io/parsers/pajek-lexer.h" +#include "internal/hacks.h" + +int igraph_pajek_yyerror(YYLTYPE* locp, + igraph_i_pajek_parsedata_t *context, + const char *s); + +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 + +%} + +%pure-parser +/* bison: do not remove the equals sign; macOS XCode ships with bison 2.3, which + * needs the equals sign */ +%name-prefix="igraph_pajek_yy" +%defines +%locations +%error-verbose +%parse-param { igraph_i_pajek_parsedata_t* context } +%lex-param { void *scanner } + +%union { + long int intnum; + double realnum; + struct { + char *str; + int len; + } string; +} + +%type longint; +%type arcfrom; +%type arcto; +%type edgefrom; +%type edgeto; +%type number; +%type word; +%type vpwordpar; +%type epwordpar; +%type vertex; + +%token NEWLINE +%token NUM +%token ALNUM +%token QSTR +%token PSTR +%token NETWORKLINE +%token VERTICESLINE +%token ARCSLINE +%token EDGESLINE +%token ARCSLISTLINE +%token EDGESLISTLINE +%token MATRIXLINE +%token ERROR + +%token VP_X_FACT +%token VP_Y_FACT +%token VP_IC +%token VP_BC +%token VP_LC +%token VP_LR +%token VP_LPHI +%token VP_BW +%token VP_FOS +%token VP_PHI +%token VP_R +%token VP_Q +%token VP_LA +%token VP_FONT +%token VP_URL +%token VP_SIZE + +%token EP_C +%token EP_S +%token EP_A +%token EP_W +%token EP_H1 +%token EP_H2 +%token EP_A1 +%token EP_A2 +%token EP_K1 +%token EP_K2 +%token EP_AP +%token EP_P +%token EP_L +%token EP_LP +%token EP_LR +%token EP_LPHI +%token EP_LC +%token EP_LA +%token EP_SIZE +%token EP_FOS + +%% + +input: nethead vertices edgeblock { + if (context->vcount2 > 0) { igraph_i_pajek_check_bipartite(context); } + }; + +nethead: /* empty */ | NETWORKLINE words NEWLINE; + +vertices: verticeshead NEWLINE vertdefs; + +verticeshead: VERTICESLINE longint { + context->vcount=$2; + context->vcount2=0; + } + | VERTICESLINE longint longint { + context->vcount=$2; + context->vcount2=$3; + igraph_i_pajek_add_bipartite_type(context); +}; + +vertdefs: /* empty */ | vertdefs vertexline; + +vertexline: NEWLINE | + vertex NEWLINE | + vertex { context->actvertex=$1; } vertexid vertexcoords shape params NEWLINE { } +; + +vertex: longint { $$=$1; context->mode=1; }; + +vertexid: word { + igraph_i_pajek_add_string_vertex_attribute("id", $1.str, $1.len, context); + igraph_i_pajek_add_string_vertex_attribute("name", $1.str, $1.len, context); +}; + +vertexcoords: /* empty */ + | number number { + igraph_i_pajek_add_numeric_vertex_attribute("x", $1, context); + igraph_i_pajek_add_numeric_vertex_attribute("y", $2, context); + } + | number number number { + igraph_i_pajek_add_numeric_vertex_attribute("x", $1, context); + igraph_i_pajek_add_numeric_vertex_attribute("y", $2, context); + igraph_i_pajek_add_numeric_vertex_attribute("z", $3, context); + }; + +shape: /* empty */ | word { + igraph_i_pajek_add_string_vertex_attribute("shape", $1.str, $1.len, context); +}; + +params: /* empty */ | params param; + +param: + vpword + | VP_X_FACT number { + igraph_i_pajek_add_numeric_vertex_attribute("xfact", $2, context); + } + | VP_Y_FACT number { + igraph_i_pajek_add_numeric_vertex_attribute("yfact", $2, context); + } + | VP_IC number number number { /* RGB color */ + igraph_i_pajek_add_numeric_vertex_attribute("color-red", $2, context); + igraph_i_pajek_add_numeric_vertex_attribute("color-green", $3, context); + igraph_i_pajek_add_numeric_vertex_attribute("color-blue", $4, context); + } + | VP_BC number number number { + igraph_i_pajek_add_numeric_vertex_attribute("framecolor-red", $2, context); + igraph_i_pajek_add_numeric_vertex_attribute("framecolor-green", $3, context); + igraph_i_pajek_add_numeric_vertex_attribute("framecolor-blue", $4, context); + } + | VP_LC number number number { + igraph_i_pajek_add_numeric_vertex_attribute("labelcolor-red", $2, context); + igraph_i_pajek_add_numeric_vertex_attribute("labelcolor-green", $3, context); + igraph_i_pajek_add_numeric_vertex_attribute("labelcolor-blue", $4, context); + } + | VP_LR number { + igraph_i_pajek_add_numeric_vertex_attribute("labeldist", $2, context); + } + | VP_LPHI number { + igraph_i_pajek_add_numeric_vertex_attribute("labeldegree2", $2, context); + } + | VP_BW number { + igraph_i_pajek_add_numeric_vertex_attribute("framewidth", $2, context); + } + | VP_FOS number { + igraph_i_pajek_add_numeric_vertex_attribute("fontsize", $2, context); + } + | VP_PHI number { + igraph_i_pajek_add_numeric_vertex_attribute("rotation", $2, context); + } + | VP_R number { + igraph_i_pajek_add_numeric_vertex_attribute("radius", $2, context); + } + | VP_Q number { + igraph_i_pajek_add_numeric_vertex_attribute("diamondratio", $2, context); + } + | VP_LA number { + igraph_i_pajek_add_numeric_vertex_attribute("labeldegree", $2, context); + } + | VP_SIZE number { + igraph_i_pajek_add_numeric_vertex_attribute("vertexsize", $2, context); + } +; + +vpword: VP_FONT { context->mode=3; } vpwordpar { + context->mode=1; + igraph_i_pajek_add_string_vertex_attribute("font", $3.str, $3.len, context); + } + | VP_URL { context->mode=3; } vpwordpar { + context->mode=1; + igraph_i_pajek_add_string_vertex_attribute("url", $3.str, $3.len, context); + } + | VP_IC { context->mode=3; } vpwordpar { + context->mode=1; + igraph_i_pajek_add_string_vertex_attribute("color", $3.str, $3.len, context); + } + | VP_BC { context->mode=3; } vpwordpar { + context->mode=1; + igraph_i_pajek_add_string_vertex_attribute("framecolor", + $3.str, $3.len, context); + } + | VP_LC { context->mode=3; } vpwordpar { + context->mode=1; + igraph_i_pajek_add_string_vertex_attribute("labelcolor", + $3.str, $3.len, context); + } +; + +vpwordpar: word { $$=$1; }; + +edgeblock: /* empty */ | edgeblock arcs | edgeblock edges | edgeblock arcslist | edgeblock edgeslist | edgeblock adjmatrix; + +arcs: ARCSLINE NEWLINE arcsdefs { context->directed=1; } + | ARCSLINE number NEWLINE arcsdefs { context->directed=1; }; + +arcsdefs: /* empty */ | arcsdefs arcsline; + +arcsline: NEWLINE | + arcfrom arcto { context->actedge++; + context->mode=2; } weight edgeparams NEWLINE { + igraph_vector_push_back(context->vector, $1-1); + igraph_vector_push_back(context->vector, $2-1); } +; + +arcfrom: longint; + +arcto: longint; + +edges: EDGESLINE NEWLINE edgesdefs { context->directed=0; } + | EDGESLINE number NEWLINE edgesdefs { context->directed=0; } + +edgesdefs: /* empty */ | edgesdefs edgesline; + +edgesline: NEWLINE | + edgefrom edgeto { context->actedge++; + context->mode=2; } weight edgeparams NEWLINE { + igraph_vector_push_back(context->vector, $1-1); + igraph_vector_push_back(context->vector, $2-1); } +; + +edgefrom: longint; + +edgeto: longint; + +weight: /* empty */ | number { + igraph_i_pajek_add_numeric_edge_attribute("weight", $1, context); +}; + +edgeparams: /* empty */ | edgeparams edgeparam; + +edgeparam: + epword + | EP_C number number number { + igraph_i_pajek_add_numeric_edge_attribute("color-red", $2, context); + igraph_i_pajek_add_numeric_edge_attribute("color-green", $3, context); + igraph_i_pajek_add_numeric_edge_attribute("color-blue", $4, context); + } + | EP_S number { + igraph_i_pajek_add_numeric_edge_attribute("arrowsize", $2, context); + } + | EP_W number { + igraph_i_pajek_add_numeric_edge_attribute("edgewidth", $2, context); + } + | EP_H1 number { + igraph_i_pajek_add_numeric_edge_attribute("hook1", $2, context); + } + | EP_H2 number { + igraph_i_pajek_add_numeric_edge_attribute("hook2", $2, context); + } + | EP_A1 number { + igraph_i_pajek_add_numeric_edge_attribute("angle1", $2, context); + } + | EP_A2 number { + igraph_i_pajek_add_numeric_edge_attribute("angle2", $2, context); + } + | EP_K1 number { + igraph_i_pajek_add_numeric_edge_attribute("velocity1", $2, context); + } + | EP_K2 number { + igraph_i_pajek_add_numeric_edge_attribute("velocity2", $2, context); + } + | EP_AP number { + igraph_i_pajek_add_numeric_edge_attribute("arrowpos", $2, context); + } + | EP_LP number { + igraph_i_pajek_add_numeric_edge_attribute("labelpos", $2, context); + } + | EP_LR number { + igraph_i_pajek_add_numeric_edge_attribute("labelangle", $2, context); + } + | EP_LPHI number { + igraph_i_pajek_add_numeric_edge_attribute("labelangle2", $2, context); + } + | EP_LA number { + igraph_i_pajek_add_numeric_edge_attribute("labeldegree", $2, context); + } + | EP_SIZE number { /* what is this??? */ + igraph_i_pajek_add_numeric_edge_attribute("arrowsize", $2, context); + } + | EP_FOS number { + igraph_i_pajek_add_numeric_edge_attribute("fontsize", $2, context); + } +; + +epword: EP_A { context->mode=4; } epwordpar { + context->mode=2; + igraph_i_pajek_add_string_edge_attribute("arrowtype", $3.str, $3.len, context); + } + | EP_P { context->mode=4; } epwordpar { + context->mode=2; + igraph_i_pajek_add_string_edge_attribute("linepattern", $3.str, $3.len, context); + } + | EP_L { context->mode=4; } epwordpar { + context->mode=2; + igraph_i_pajek_add_string_edge_attribute("label", $3.str, $3.len, context); + } + | EP_LC { context->mode=4; } epwordpar { + context->mode=2; + igraph_i_pajek_add_string_edge_attribute("labelcolor", $3.str, $3.len, context); + } + | EP_C { context->mode=4; } epwordpar { + context->mode=2; + igraph_i_pajek_add_string_edge_attribute("color", $3.str, $3.len, context); + } +; + +epwordpar: word { context->mode=2; $$=$1; }; + +arcslist: ARCSLISTLINE NEWLINE arcslistlines { context->directed=1; }; + +arcslistlines: /* empty */ | arcslistlines arclistline; + +arclistline: NEWLINE | arclistfrom arctolist NEWLINE; + +arctolist: /* empty */ | arctolist arclistto; + +arclistfrom: longint { context->mode=0; context->actfrom=labs($1)-1; }; + +arclistto: longint { + igraph_vector_push_back(context->vector, context->actfrom); + igraph_vector_push_back(context->vector, labs($1)-1); +}; + +edgeslist: EDGESLISTLINE NEWLINE edgelistlines { context->directed=0; }; + +edgelistlines: /* empty */ | edgelistlines edgelistline; + +edgelistline: NEWLINE | edgelistfrom edgetolist NEWLINE; + +edgetolist: /* empty */ | edgetolist edgelistto; + +edgelistfrom: longint { context->mode=0; context->actfrom=labs($1)-1; }; + +edgelistto: longint { + igraph_vector_push_back(context->vector, context->actfrom); + igraph_vector_push_back(context->vector, labs($1)-1); +}; + +/* -----------------------------------------------------*/ + +adjmatrix: matrixline NEWLINE adjmatrixlines; + +matrixline: MATRIXLINE { context->actfrom=0; + context->actto=0; + context->directed=(context->vcount2==0); + }; + +adjmatrixlines: /* empty */ | adjmatrixlines adjmatrixline; + +adjmatrixline: adjmatrixnumbers NEWLINE { context->actfrom++; context->actto=0; }; + +adjmatrixnumbers: /* empty */ | adjmatrixentry adjmatrixnumbers; + +adjmatrixentry: number { + if ($1 != 0) { + if (context->vcount2==0) { + context->actedge++; + igraph_i_pajek_add_numeric_edge_attribute("weight", $1, 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", $1, context); + igraph_vector_push_back(context->vector, context->actfrom); + igraph_vector_push_back(context->vector, + context->vcount2+context->actto); + } + } + context->actto++; +}; + +/* -----------------------------------------------------*/ + +longint: NUM { $$=igraph_pajek_get_number(igraph_pajek_yyget_text(scanner), + igraph_pajek_yyget_leng(scanner)); }; + +number: NUM { $$=igraph_pajek_get_number(igraph_pajek_yyget_text(scanner), + igraph_pajek_yyget_leng(scanner)); }; + +words: /* empty */ | words word; + +word: ALNUM { $$.str=igraph_pajek_yyget_text(scanner); + $$.len=igraph_pajek_yyget_leng(scanner); } + | NUM { $$.str=igraph_pajek_yyget_text(scanner); + $$.len=igraph_pajek_yyget_leng(scanner); } + | QSTR { $$.str=igraph_pajek_yyget_text(scanner)+1; + $$.len=igraph_pajek_yyget_leng(scanner)-2; }; + +%% + +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 (;origsizename=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 (;origsizevertex_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(igraph_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; ivector; + int i, n1=context->vcount2; + int ne=igraph_vector_size(edges); + + for (i=0; i n1 && v2 > n1) ) { + IGRAPH_WARNING("Invalid edge in bipartite graph"); + } + } + + return 0; +} diff --git a/src/rigraph/core/io/pajek.c b/src/rigraph/core/io/pajek.c new file mode 100644 index 0000000..2a7240c --- /dev/null +++ b/src/rigraph/core/io/pajek.c @@ -0,0 +1,771 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2005-2020 The igraph development 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., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_foreign.h" + +#include "igraph_attributes.h" +#include "igraph_error.h" +#include "igraph_interface.h" +#include "igraph_memory.h" + +#include "graph/attributes.h" + +#include "pajek-header.h" + +#include +#include + +int igraph_pajek_yylex_init_extra(igraph_i_pajek_parsedata_t* user_defined, + void* scanner); +void 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. + * + * + * Only a subset of the Pajek format is implemented. This is partially + * because this format is not very well documented, but also because + * igraph does not support some Pajek features, like + * multigraphs. + * + * + * 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. + * + * + * The list of the current limitations: + * \olist + * \oli Only .net files are supported, Pajek + * project files (.paj) are not. These might be + * supported in the future if there is need for it. + * \oli Time events networks are not supported. + * \oli Hypergraphs (i.e. 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 + * igraph. + * \oli Only Pajek networks are supported, permutations, hierarchies, + * clusters and vectors are not. + * \oli Graphs with multiple edge sets are not supported. + * \endolist + * + * + * If there are attribute handlers installed, + * igraph 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. + * + * + * 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. + * + * + * 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. + * + * The \c weight edge attribute might be + * added if there are edge weights present. + * + * + * 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. + * + * + * 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; +} + +/* 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 + +static 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++; + *d = *s; + break; + 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. + * + * + * 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(), e.g. the \c color vertex attributes + * determines the color (\c c in Pajek) parameter. + * + * + * 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. + * + * + * Early versions of Pajek supported only Windows-style line endings + * in Pajek files, but recent versions support both Windows and Unix + * line endings. igraph therefore uses the platform-native line endings + * when the input file is opened in text mode, and uses Unix-style + * line endings when the input file is opened in binary mode. If you + * are using an old version of Pajek, you are on Unix and you are having + * problems reading files written by igraph on a Windows machine, convert the + * line endings manually with a text editor or with \c unix2dos or \c iconv + * from the command line). + * + * \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 igraph 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" }; + + /* Newer Pajek versions support both Unix and Windows-style line endings, + * so we just use Unix style. This will get converted to CRLF on Windows + * when the file is opened in text mode */ + const char *newline = "\n"; + + 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; +} diff --git a/src/rigraph/core/isomorphism/bliss.cc b/src/rigraph/core/isomorphism/bliss.cc new file mode 100644 index 0000000..2439f62 --- /dev/null +++ b/src/rigraph/core/isomorphism/bliss.cc @@ -0,0 +1,608 @@ +/* + 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_topology.h" +#include "igraph_conversion.h" +#include "igraph_interface.h" +#include "igraph_interrupt.h" +#include "igraph_memory.h" +#include "igraph_vector.h" +#include "igraph_vector_ptr.h" + +#include "core/exceptions.h" + +using namespace bliss; +using namespace std; + +/** + * \section about_bliss + * + * + * 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. + * + * + * + * 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 https://users.aalto.fi/~tjunttil/bliss/ and the following + * publication: + * + * + * + * Tommi Junttila and Petteri Kaski: "Engineering an Efficient Canonical Labeling + * Tool for Large and Sparse Graphs" In ALENEX 2007, pages 135–149, 2007 + * https://doi.org/10.1137/1.9781611972870.13 + * + * + * + * Tommi Junttila and Petteri Kaski: "Conflict Propagation and Component Recursion + * for Canonical Labeling" in TAPAS 2011, pages 151–162, 2011. + * https://doi.org/10.1007/978-3-642-19754-3_16 + * + * + * + * Bliss works with both directed graphs and undirected graphs. It supports graphs with + * self-loops, but not graphs with multi-edges. + * + * + * + * Bliss version 0.75 is included in igraph. + * + */ + +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(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(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 int bliss_info_to_igraph(igraph_bliss_info_t *info, const Stats &stats) { + if (info) { + size_t group_size_strlen; + + 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(); + + mpz_t group_size; + mpz_init(group_size); + stats.get_group_size().get(group_size); + group_size_strlen = mpz_sizeinbase(group_size, /* base */ 10) + 2; + info->group_size = IGRAPH_CALLOC(group_size_strlen, char); + if (! info->group_size) { + IGRAPH_ERROR("Insufficient memory to retrieve automotphism group size.", IGRAPH_ENOMEM); + } + mpz_get_str(info->group_size, /* base */ 10, group_size); + mpz_clear(group_size); + } + + return IGRAPH_SUCCESS; +} + + +// This is the callback function that can tell Bliss to terminate early. +struct AbortChecker { + bool aborted; + + AbortChecker() : aborted(false) { } + bool operator()() { + if (igraph_allow_interruption(NULL) != IGRAPH_SUCCESS) { + aborted = true; + return true; + } + return false; + } +}; + + +// This is the callback function used with AbstractGraph::find_automorphisms(). +// It collects the automorphism group generators into a pointer vector. +class AutCollector { + igraph_vector_ptr_t *generators; + +public: + AutCollector(igraph_vector_ptr_t *generators_) : generators(generators_) { } + + void operator ()(unsigned int n, const unsigned int *aut) { + int err; + igraph_vector_t *newvector = IGRAPH_CALLOC(1, igraph_vector_t); + if (! newvector) { + throw bad_alloc(); + } + err = igraph_vector_init(newvector, n); + if (err) { + throw bad_alloc(); + } + copy(aut, aut + n, newvector->stor_begin); // takes care of unsigned int -> double conversion + err = igraph_vector_ptr_push_back(generators, newvector); + if (err) { + throw bad_alloc(); + } + } +}; + +} // end unnamed namespace + + +/** + * \function igraph_canonical_permutation + * \brief Canonical permutation using Bliss. + * + * This function computes the vertex permutation which transforms + * the graph into a canonical form, using the Bliss algorithm. + * Two graphs have the same canonical form if and only if they + * are isomorphic. Use \ref igraph_is_same_graph() to compare + * two canonical forms. + * + * \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. The memory used by this structure must to be freed + * when no longer needed, see \ref igraph_bliss_info_t. + * \return Error code. + * + * \sa igraph_is_same_graph() + * + * 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) { + IGRAPH_HANDLE_EXCEPTIONS( + 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; + AbortChecker checker; + const unsigned int *cl = g->canonical_form(stats, /* report */ nullptr, /* terminate */ checker); + if (checker.aborted) { + return IGRAPH_INTERRUPTED; + } + + IGRAPH_CHECK(igraph_vector_resize(labeling, N)); + for (unsigned int i = 0; i < N; i++) { + VECTOR(*labeling)[i] = cl[i]; + } + + IGRAPH_CHECK(bliss_info_to_igraph(info, stats)); + + delete g; + IGRAPH_FINALLY_CLEAN(1); + ); + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_automorphisms + * \brief 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 long double 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. The memory used by this structure must be + * released when no longer needed, see \ref igraph_bliss_info_t. + * \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) { + IGRAPH_HANDLE_EXCEPTIONS( + 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; + AbortChecker checker; + g->find_automorphisms(stats, /* report */ nullptr, /* terminate */ checker); + if (checker.aborted) { + return IGRAPH_INTERRUPTED; + } + + IGRAPH_CHECK(bliss_info_to_igraph(info, stats)); + + delete g; + IGRAPH_FINALLY_CLEAN(1); + ); + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_automorphism_group + * \brief 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. The generators are permutations represented + * using zero-based indexing. + * + * \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. The memory used by this structure must to be freed + * when no longer needed, 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) { + IGRAPH_HANDLE_EXCEPTIONS( + 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); + AutCollector collector(generators); + AbortChecker checker; + g->find_automorphisms(stats, collector, checker); + if (checker.aborted) { + return IGRAPH_INTERRUPTED; + } + IGRAPH_CHECK(bliss_info_to_igraph(info, stats)); + + delete g; + IGRAPH_FINALLY_CLEAN(1); + ); + + return IGRAPH_SUCCESS; +} + + +/* The following license notice applies to the rest of this file */ + +/* + IGraph library. + Copyright (C) 2006-2021 The igraph development 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, see . +*/ + +/** + * \function igraph_isomorphic_bliss + * \brief 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 LGPL. See + * https://users.aalto.fi/~tjunttil/bliss/ for + * details. Currently the 0.75 version of Bliss is included in igraph. + * + * + * Isomorphism testing is implemented by producing the canonical form + * of both graphs using \ref igraph_canonical_permutation() and + * comparing them. + * + * \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. Note that if the two graphs + * have different number of vertices or edges, then this is only + * partially filled. The memory used by this structure should be + * released when no longer needed, see \ref igraph_bliss_info_t + * for details. + * \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 = 0; + 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 = 0; + 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; +} diff --git a/src/rigraph/core/isomorphism/bliss/bignum.hh b/src/rigraph/core/isomorphism/bliss/bignum.hh new file mode 100644 index 0000000..546da5b --- /dev/null +++ b/src/rigraph/core/isomorphism/bliss/bignum.hh @@ -0,0 +1,103 @@ +#ifndef BLISS_BIGNUM_HH +#define BLISS_BIGNUM_HH + +/* + Copyright (c) 2003-2021 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 . +*/ + +#define BLISS_USE_GMP + +#if defined(BLISS_USE_GMP) +#include "internal/gmp_internal.h" +#endif + +#include +#include "defs.hh" + +namespace bliss { + +/** + * \brief A simple wrapper 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: + /** + * \brief Create a new big number and set it to zero. + */ + BigNum() {mpz_init(v); } + + /** + * \brief Destroy the number. + */ + ~BigNum() {mpz_clear(v); } + + /** + * \brief Set the number to \a n. + */ + void assign(const int n) {mpz_set_si(v, n); } + + /** + * \brief Multiply the number with \a n. + */ + void multiply(const int n) {mpz_mul_si(v, v, n); } + + /** + * Get a copy of the internal GNU GMP integer. + * The caller is responsible for calling mpz_init before, + * and mpz_clear afterwards on the \a result variable. + */ + void get(mpz_t& result) const {mpz_set(result, v); } +}; + +#else + +class BigNum +{ + long double v; +public: + /** + * \brief Create a new big number and set it to zero. + */ + BigNum(): v(0.0) {} + + /** + * \brief Set the number to \a n. + */ + void assign(const int n) {v = (long double)n; } + + /** + * \brief Multiply the number with \a n. + */ + void multiply(const int n) {v *= (long double)n; } +}; + +#endif + +} //namespace bliss + +#endif // BLISS_BIGNUM_HH diff --git a/src/rigraph/core/isomorphism/bliss/defs.cc b/src/rigraph/core/isomorphism/bliss/defs.cc new file mode 100644 index 0000000..9760006 --- /dev/null +++ b/src/rigraph/core/isomorphism/bliss/defs.cc @@ -0,0 +1,32 @@ +#include "igraph_error.h" + +#include "defs.hh" + +/* + Copyright (c) 2003-2021 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 . +*/ + +namespace bliss { + +void +fatal_error(const char* reason) +{ + IGRAPH_FATAL(reason); +} + +} diff --git a/src/rigraph/core/isomorphism/bliss/defs.hh b/src/rigraph/core/isomorphism/bliss/defs.hh new file mode 100644 index 0000000..51b8246 --- /dev/null +++ b/src/rigraph/core/isomorphism/bliss/defs.hh @@ -0,0 +1,90 @@ +#ifndef BLISS_DEFS_HH +#define BLISS_DEFS_HH + +#include +#include + +/* + Copyright (c) 2003-2021 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 . +*/ + +/** \file + * \brief Some common definitions. + */ + +namespace bliss { + +/** \brief The version number of bliss. */ +static const char * const version = "0.75"; + +/** + * If a fatal internal error is encountered, + * this function is called. + * There should no return from this function, but an exit or + * a jump/throw to code that deallocates the AbstractGraph instance calling this. + */ +void fatal_error(const char* fmt); + + +#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 Outline + * + * This is the C++ API documentation of bliss, + * produced by running doxygen in + * the source directory. + * + * The algorithms and data structures used in bliss, + * the graph file format, as well as the compilation process + * can be found at the + * bliss web site. + * + * The C++ language API is the main API to bliss. + * It basically consists of the public methods in the classes + * * 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 only a subset of the C++ API, + * so consider using the C++ API whenever possible. + */ + +#endif // BLISS_DEFS_HH diff --git a/src/rigraph/core/isomorphism/bliss/graph.cc b/src/rigraph/core/isomorphism/bliss/graph.cc new file mode 100644 index 0000000..7260423 --- /dev/null +++ b/src/rigraph/core/isomorphism/bliss/graph.cc @@ -0,0 +1,5035 @@ +#include "igraph_error.h" + +#include +#include +#include +#include +#include +// #include +#include +#include + +#include "defs.hh" +#include "graph.hh" +#include "partition.hh" +#include "utils.hh" + +/* Allow using 'and' instead of '&&' with MSVC */ +#if _MSC_VER +#include +#endif + +/* + Copyright (c) 2003-2021 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 . +*/ + + +namespace bliss { + +#define _INTERNAL_ERROR() IGRAPH_FATAL("Bliss internal error") + +/*------------------------------------------------------------------------- + * + * Constructor and destructor routines for the abstract graph class + * + *-------------------------------------------------------------------------*/ + + +AbstractGraph::AbstractGraph() +{ + /* Initialize stuff */ + first_path_labeling = nullptr; + first_path_labeling_inv = nullptr; + best_path_labeling = nullptr; + best_path_labeling_inv = nullptr; + first_path_automorphism = nullptr; + best_path_automorphism = nullptr; + 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; + */ +} + + +AbstractGraph::~AbstractGraph() +{ + delete[] first_path_labeling; first_path_labeling = nullptr; + delete[] first_path_labeling_inv; first_path_labeling_inv = nullptr; + delete[] first_path_automorphism; first_path_automorphism = nullptr; + + delete[] best_path_labeling; best_path_labeling = nullptr; + delete[] best_path_labeling_inv; best_path_labeling_inv = nullptr; + delete[] best_path_automorphism; best_path_automorphism = nullptr; +} + + + +/*------------------------------------------------------------------------- + * + * 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& 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* 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& +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(get_nof_vertices()); + return *long_prune_fixed[i]; +} + +std::vector& +AbstractGraph::long_prune_get_fixed(const unsigned int index) +{ + return *long_prune_fixed[index % long_prune_max_stored_autss]; +} + +std::vector& +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(get_nof_vertices()); + return *long_prune_mcrs[i]; +} + +std::vector& +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& fixed = long_prune_allocget_fixed(long_prune_end-1); + std::vector& 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 + * + *-------------------------------------------------------------------------*/ + +/** \internal \brief Search tree node information. + */ +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 = false; /* igraph-specific patch: initialize to false to silence UBSan */ + unsigned int long_prune_begin; + std::set > long_prune_redundant; + + UintSeqHash eqref_hash; + unsigned int subcertificate_length; +}; + + + + + +void +AbstractGraph::search(const bool canonical, + Stats& stats, + const std::function& report, + const std::function& terminate) +{ + 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 */ + delete[] first_path_labeling; first_path_labeling = nullptr; + delete[] first_path_labeling_inv; first_path_labeling_inv = nullptr; + delete[] first_path_automorphism; first_path_automorphism = nullptr; + + /* Free old best path data structures */ + delete[] best_path_labeling; best_path_labeling = nullptr; + delete[] best_path_labeling_inv; best_path_labeling_inv = nullptr; + delete[] best_path_automorphism; best_path_automorphism = nullptr; + + 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 + */ + delete[] first_path_labeling; + first_path_labeling = new unsigned int[N]; + + delete[] best_path_labeling; + best_path_labeling = new unsigned int[N]; + for(unsigned int i = 0; i < N; i++) best_path_labeling[i] = i; + + /* + * 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; + /* Release component recursion data in partition */ + if(opt_use_comprec) + p.cr_free(); + return; + } + + /* + * Allocate the inverses of the "first path" and "best path" labelings + */ + delete[] first_path_labeling_inv; + first_path_labeling_inv = new unsigned int[N]; + std::fill_n(first_path_labeling_inv, N, 0); + delete[] best_path_labeling_inv; + best_path_labeling_inv = new unsigned int[N]; + std::fill_n(best_path_labeling_inv, N, 0); + + /* + * Allocate space for the automorphisms + */ + delete[] first_path_automorphism; + first_path_automorphism = new unsigned int[N]; + delete[] best_path_automorphism; + best_path_automorphism = new unsigned int[N]; + + /* + * 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 search_stack; + std::vector first_path_info; + std::vector 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 > FailureRecordingSet; + std::vector 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()) + { + if(terminate and terminate()) { + break; + } + 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& 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& 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& 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 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 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) + report(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) + report(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, + const std::function& report, + const std::function& terminate) +{ + search(false, stats, report, terminate); + + delete[] first_path_labeling; first_path_labeling = nullptr; + delete[] best_path_labeling; best_path_labeling = nullptr; +} + + +const unsigned int * +AbstractGraph::canonical_form(Stats& stats, + const std::function& report, + const std::function& terminate) +{ + search(true, stats, report, terminate); + + 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& 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::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::iterator iter = edges_out.begin(); + iter != edges_out.end(); + iter++) + { + tmp[*iter] = false; + } + + for(std::vector::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::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) +{ + if(vertex1 >= vertices.size() or vertex2 >= vertices.size()) + throw std::runtime_error("out of bounds vertex number"); + //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::const_iterator ei1 = v1.edges_in.begin(); + std::vector::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& 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::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::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; +} + + +void +Digraph::remove_duplicate_edges() +{ + std::vector tmp(get_nof_vertices(), false); + + for(std::vector::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::const_iterator ei = v.edges_out.begin(); + ei != v.edges_out.end(); + ei++) + { + h.update(i); + h.update(*ei); + } + } + + return h.get_value(); +} + + +/*------------------------------------------------------------------------- + * + * 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::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::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::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::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 first_count = std::vector(N, 0); + std::vector other_count = std::vector(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::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::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::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::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 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::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 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::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 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::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) const +{ + std::set > edges1; + std::set > 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++) + { + const Vertex& v1 = vertices[i]; + const Vertex& v2 = vertices[perm[i]]; + + edges1.clear(); + for(std::vector::const_iterator ei = v1.edges_in.cbegin(); + ei != v1.edges_in.cend(); + ei++) + edges1.insert(perm[*ei]); + edges2.clear(); + for(std::vector::const_iterator ei = v2.edges_in.cbegin(); + ei != v2.edges_in.cend(); + ei++) + edges2.insert(*ei); + if(!(edges1 == edges2)) + return false; + + edges1.clear(); + for(std::vector::const_iterator ei = v1.edges_out.cbegin(); + ei != v1.edges_out.cend(); + ei++) + edges1.insert(perm[*ei]); + edges2.clear(); + for(std::vector::const_iterator ei = v2.edges_out.cbegin(); + ei != v2.edges_out.cend(); + ei++) + edges2.insert(*ei); + if(!(edges1 == edges2)) + return false; + } + + return true; +} + +bool +Digraph::is_automorphism(const std::vector& perm) const +{ + + if(!(perm.size() == get_nof_vertices() and is_permutation(perm))) + return false; + + std::set > edges1; + std::set > 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::const_iterator ei = v1.edges_in.begin(); + ei != v1.edges_in.end(); + ei++) + edges1.insert(perm[*ei]); + edges2.clear(); + for(std::vector::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::const_iterator ei = v1.edges_out.begin(); + ei != v1.edges_out.end(); + ei++) + edges1.insert(perm[*ei]); + edges2.clear(); + for(std::vector::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 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::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& 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 comp; + KStack 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::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& 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::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::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); + if(vertex1 >= vertices.size() or vertex2 >= vertices.size()) + throw std::runtime_error("out of bounds vertex number"); + 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; +} + + +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::const_iterator ei1 = v1.edges.begin(); + std::vector::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& 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::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::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; +} + + +/*------------------------------------------------------------------------- + * + * 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::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 tmp(vertices.size(), false); + + for(std::vector::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::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::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::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 first_count = std::vector(N, 0); + std::vector other_count = std::vector(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::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::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(°ree_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 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::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 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::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 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::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) const +{ + std::set > edges1; + std::set > 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++) + { + const Vertex& v1 = vertices[i]; + edges1.clear(); + for(std::vector::const_iterator ei = v1.edges.cbegin(); + ei != v1.edges.cend(); + ei++) + edges1.insert(perm[*ei]); + + const Vertex& v2 = vertices[perm[i]]; + edges2.clear(); + for(std::vector::const_iterator ei = v2.edges.cbegin(); + ei != v2.edges.cend(); + ei++) + edges2.insert(*ei); + + if(!(edges1 == edges2)) + return false; + } + + return true; +} + + + + +bool +Graph::is_automorphism(const std::vector& perm) const +{ + + if(!(perm.size() == get_nof_vertices() and is_permutation(perm))) + return false; + + std::set > edges1; + std::set > edges2; + + for(unsigned int i = 0; i < get_nof_vertices(); i++) + { + const Vertex& v1 = vertices[i]; + edges1.clear(); + for(std::vector::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::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 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::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& 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 comp; + KStack 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::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/src/rigraph/core/isomorphism/bliss/graph.hh b/src/rigraph/core/isomorphism/bliss/graph.hh new file mode 100644 index 0000000..c4d30b3 --- /dev/null +++ b/src/rigraph/core/isomorphism/bliss/graph.hh @@ -0,0 +1,872 @@ +#ifndef BLISS_GRAPH_HH +#define BLISS_GRAPH_HH + +/* + Copyright (c) 2003-2021 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 . +*/ + +/** + * \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 +#include +#include +#include "stats.hh" +#include "kstack.hh" +#include "kqueue.hh" +#include "heap.hh" +#include "orbit.hh" +#include "partition.hh" +#include "uintseqhash.hh" + +namespace bliss { + + + + + +/** + * \brief An abstract base class for different types of graphs. + */ +class AbstractGraph +{ + friend class Partition; + +public: + AbstractGraph(); + virtual ~AbstractGraph(); + +#if 0 + /** + * 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); +#endif + + /** + * 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& perm) const = 0; + + + /** 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& perm) const = 0; + + /** + * Find a set of generators for the automorphism group of the graph. + * The function \a report (if non-null) is called each time a new generator + * for the automorphism group is found. + * The first argument \a n for the function + * is the length of the automorphism (equal to get_nof_vertices()), and + * the second 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 \a report function; + * if you want to use the automorphism later, you have to take a copy of it. + * Do not call any member functions from the \a report function. + * + * The search statistics are copied in \a stats. + * + * If the \a terminate function argument is given, + * it is called in each search tree node: if the function returns true, + * then the search is terminated and thus not all the automorphisms + * may have been generated. + * The \a terminate function may be used to limit the time spent in bliss + * in case the graph is too difficult under the available time constraints. + * If used, keep the function simple to evaluate so that + * it does not consume too much time. + */ + void find_automorphisms(Stats& stats, + const std::function& report = nullptr, + const std::function& terminate = nullptr); + + /** + * 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()). + * + * If the \a terminate function argument is given, + * it is called in each search tree node: if the function returns true, + * then the search is terminated and thus (i) not all the automorphisms + * may have been generated and (ii) the returned labeling may not + * be canonical. + * The \a terminate function may be used to limit the time spent in bliss + * in case the graph is too difficult under the available time constraints. + * If used, keep the function simple to evaluate so that + * it does not consume too much time. + */ + const unsigned int* canonical_form(Stats& stats, + const std::function& report = nullptr, + const std::function& terminate = nullptr); + + /** + * 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 = false; + bool refine_equal_to_first = false; + 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 *> long_prune_fixed; + std::vector *> long_prune_mcrs; + std::vector 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& long_prune_get_fixed(const unsigned int index); + std::vector& long_prune_allocget_fixed(const unsigned int index); + std::vector& long_prune_get_mcrs(const unsigned int index); + std::vector& 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 * const) = 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) const = 0; + + std::vector certificate_current_path; + std::vector certificate_first_path; + std::vector 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; + + + /** \struct PathInfo + * + * A structure for holding first, current, and best path information. + */ + typedef struct { + unsigned int splitting_element; + unsigned int certificate_index; + unsigned int subcertificate_length; + UintSeqHash eqref_hash; + } PathInfo; + + void search(const bool canonical, Stats &stats, + const std::function& report_function = nullptr, + const std::function& terminate = nullptr); + + + 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 @class CR_CEP + * 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_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& 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 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& tmp); + void sort_edges(); + + unsigned int color; + std::vector edges; + unsigned int nof_edges() const {return edges.size(); } + }; + std::vector 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 * 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 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) const; + + + bool nucr_find_first_component(const unsigned int level); + bool nucr_find_first_component(const unsigned int level, + std::vector& 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(); + + /** + * \copydoc AbstractGraph::is_automorphism(const std::vector& perm) const + */ + bool is_automorphism(const std::vector& 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& 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& tmp); + void sort_edges(); + unsigned int color; + std::vector edges_out; + std::vector edges_in; + unsigned int nof_edges_in() const {return edges_in.size(); } + unsigned int nof_edges_out() const {return edges_out.size(); } + }; + std::vector 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) const; + + void sort_edges(); + + bool nucr_find_first_component(const unsigned int level); + bool nucr_find_first_component(const unsigned int level, + std::vector& 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(); + + + /** + * \copydoc AbstractGraph::is_automorphism(const std::vector& perm) const + */ + bool is_automorphism(const std::vector& 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& perm) const; +}; + + + + +} // namespace bliss + +#endif // BLISS_GRAPH_HH diff --git a/src/rigraph/core/isomorphism/bliss/heap.cc b/src/rigraph/core/isomorphism/bliss/heap.cc new file mode 100644 index 0000000..0e57893 --- /dev/null +++ b/src/rigraph/core/isomorphism/bliss/heap.cc @@ -0,0 +1,111 @@ +#include "heap.hh" + +#include +#include + +/* Allow using 'and' instead of '&&' with MSVC */ +#if _MSC_VER +#include +#endif + +/* + Copyright (c) 2003-2021 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 . +*/ + +namespace bliss { + +Heap::Heap() { + array = nullptr; + n = 0; + N = 0; +} + +Heap::~Heap() +{ + delete[] array; + array = nullptr; + n = 0; + N = 0; +} + +void Heap::upheap(unsigned int index) +{ + assert(n >= 1); + assert(index >= 1 and index <= n); + 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) +{ + assert(size > 0); + if(size > N) + { + delete[] array; + array = new unsigned int[size + 1]; + N = size; + } + n = 0; +} + +void Heap::insert(const unsigned int v) +{ + assert(n < N); + array[++n] = v; + upheap(n); +} + +unsigned int Heap::smallest() const +{ + assert(n >= 1 and n <= N); + return array[1]; +} + +unsigned int Heap::remove() +{ + assert(n >= 1 and n <= N); + const unsigned int v = array[1]; + array[1] = array[n--]; + downheap(1); + return v; +} + +} // namespace bliss diff --git a/src/rigraph/core/isomorphism/bliss/heap.hh b/src/rigraph/core/isomorphism/bliss/heap.hh new file mode 100644 index 0000000..bb84cd4 --- /dev/null +++ b/src/rigraph/core/isomorphism/bliss/heap.hh @@ -0,0 +1,89 @@ +#ifndef BLISS_HEAP_HH +#define BLISS_HEAP_HH + +/* + Copyright (c) 2003-2021 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 . +*/ + +namespace bliss { + +/** + * \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(); + ~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); + + /** + * Return the smallest element in the heap. + * Time complexity is O(1). + */ + unsigned int smallest() const; + + /** + * 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 // BLISS_HEAP_HH diff --git a/src/rigraph/core/isomorphism/bliss/igraph-changes.md b/src/rigraph/core/isomorphism/bliss/igraph-changes.md new file mode 100644 index 0000000..3c7058b --- /dev/null +++ b/src/rigraph/core/isomorphism/bliss/igraph-changes.md @@ -0,0 +1,34 @@ +This file lists changes that were made to the original Bliss package (version 0.75) to integrate it into igraph. + +Exclude `CMakeLists.txt`, `Doxyfile`, `Makefile-manual`, `readme.txt`. Make sure not to accidentally overwrite igraph's own `bliss/CMakeLists.txt`. + +Removed `bliss.cc`, `bliss_C.cc`, `bliss_C.h`. + +Remove `timer.hh`. Remove references to `timer.hh` and `Timer` class in `graph.cc`. + +Replace `#pragma once` by traditional header guards in all headers. + +### In `bignum.hh`: + +Replace `#include ` by `#include "internal/gmp_internal.h"`. + +At the beginning, add `#define BLISS_USE_GMP`. Verify that this macro is only used in this file. + +### In `defs.cc` and `defs.hh`: + +Remove the `...` argument from `fatal_error` for simplicity, and make the function simply invoke `IGRAPH_FATAL`. + +### In `graph.cc`: + +Define `_INTERNAL_ERROR` in terms of `IGRAPH_FATAL`. + +### MSVC compatibility + +Bliss uses `and`, `or`, etc. instead of `&&`, `||`, etc. These are not supported by MSVC by default. Bliss 0.74 uses the `/permissive` option to enable support in MSVC, but this option is only supported wit VS2019. Instead, in igraph we add the following where relevant: + +``` +/* Allow using 'and' instead of '&&' with MSVC */ +#if _MSC_VER +#include +#endif +``` diff --git a/src/rigraph/core/isomorphism/bliss/kqueue.hh b/src/rigraph/core/isomorphism/bliss/kqueue.hh new file mode 100644 index 0000000..06de3b4 --- /dev/null +++ b/src/rigraph/core/isomorphism/bliss/kqueue.hh @@ -0,0 +1,168 @@ +#ifndef BLISS_KQUEUE_HH +#define BLISS_KQUEUE_HH + +/* + Copyright (c) 2003-2021 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 . +*/ + +#include +#include + +namespace bliss { + +/** + * \brief A simple implementation of queues with fixed maximum capacity. + */ +template +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 +KQueue::KQueue() +{ + entries = nullptr; + end = nullptr; + head = nullptr; + tail = nullptr; +} + +template +KQueue::~KQueue() +{ + delete[] entries; + entries = nullptr; + end = nullptr; + head = nullptr; + tail = nullptr; +} + +template +void KQueue::init(const unsigned int k) +{ + assert(k > 0); + delete[] entries; + entries = new Type[k+1]; + end = entries + k + 1; + head = entries; + tail = head; +} + +template +void KQueue::clear() +{ + head = entries; + tail = head; +} + +template +bool KQueue::is_empty() const +{ + return head == tail; +} + +template +unsigned int KQueue::size() const +{ + if(tail >= head) + return(tail - head); + return (end - head) + (tail - entries); +} + +template +Type KQueue::front() const +{ + assert(head != tail); + return *head; +} + +template +Type KQueue::pop_front() +{ + assert(head != tail); + Type *old_head = head; + head++; + if(head == end) + head = entries; + return *old_head; +} + +template +void KQueue::push_front(Type e) +{ + if(head == entries) + head = end - 1; + else + head--; + assert(head != tail); + *head = e; +} + +template +void KQueue::push_back(Type e) +{ + *tail = e; + tail++; + if(tail == end) + tail = entries; + assert(head != tail); +} + +} // namespace bliss + +#endif // BLISS_KQUEUE_HH diff --git a/src/rigraph/core/isomorphism/bliss/kstack.hh b/src/rigraph/core/isomorphism/bliss/kstack.hh new file mode 100644 index 0000000..ecb9b83 --- /dev/null +++ b/src/rigraph/core/isomorphism/bliss/kstack.hh @@ -0,0 +1,145 @@ +#ifndef BLISS_KSTACK_HH +#define BLISS_KSTACK_HH + +/* + Copyright (c) 2003-2021 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 . +*/ + +#include +#include + +namespace bliss { + +/** + * \brief A simple implementation of a stack with fixed maximum capacity. + */ +template +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 {assert(cursor > entries); return *cursor; } + + /** + * Pop (remove) the top element of the stack. + */ + Type pop() + { + assert(cursor > entries); + return *cursor--; + } + + /** + * Push the element \a e in the stack. + */ + void push(Type e) + { + assert(cursor < entries + kapacity); + *(++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() const {return kapacity; } +private: + int kapacity; + Type *entries; + Type *cursor; +}; + +template +KStack::KStack() +{ + kapacity = 0; + entries = nullptr; + cursor = nullptr; +} + +template +KStack::KStack(int k) +{ + assert(k > 0); + kapacity = k; + entries = new Type[k+1]; + cursor = entries; +} + +template +void KStack::init(int k) +{ + assert(k > 0); + delete[] entries; + kapacity = k; + entries = new Type[k+1]; + cursor = entries; +} + +template +KStack::~KStack() +{ + delete[] entries; + kapacity = 0; + entries = nullptr; + cursor = nullptr; +} + +} // namespace bliss + +#endif // BLISS_KSTACK_HH diff --git a/src/rigraph/core/isomorphism/bliss/orbit.cc b/src/rigraph/core/isomorphism/bliss/orbit.cc new file mode 100644 index 0000000..cb73862 --- /dev/null +++ b/src/rigraph/core/isomorphism/bliss/orbit.cc @@ -0,0 +1,151 @@ +#include +#include "orbit.hh" + +/* + Copyright (c) 2003-2021 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 . +*/ + +namespace bliss { + +Orbit::Orbit() +{ + orbits = 0; + in_orbit = 0; + nof_elements = 0; +} + + +Orbit::~Orbit() +{ + delete[] orbits; + orbits = 0; + /* + if(orbits) + { + free(orbits); + orbits = 0; + } + */ + delete[] in_orbit; + in_orbit = 0; + /* + if(in_orbit) + { + free(in_orbit); + in_orbit = 0; + } + */ + nof_elements = 0; + _nof_orbits = 0; +} + + +void Orbit::init(const unsigned int n) +{ + assert(n > 0); + if(orbits) delete[] orbits; + orbits = new OrbitEntry[n]; + delete[] in_orbit; + in_orbit = new OrbitEntry*[n]; + 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/src/rigraph/core/isomorphism/bliss/orbit.hh b/src/rigraph/core/isomorphism/bliss/orbit.hh new file mode 100644 index 0000000..1d59445 --- /dev/null +++ b/src/rigraph/core/isomorphism/bliss/orbit.hh @@ -0,0 +1,112 @@ +#ifndef BLISS_ORBIT_HH +#define BLISS_ORBIT_HH + +/* + Copyright (c) 2003-2021 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 . +*/ + +namespace bliss { + +/** + * \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 // BLISS_ORBIT_HH diff --git a/src/rigraph/core/isomorphism/bliss/partition.cc b/src/rigraph/core/isomorphism/bliss/partition.cc new file mode 100644 index 0000000..d0b2d52 --- /dev/null +++ b/src/rigraph/core/isomorphism/bliss/partition.cc @@ -0,0 +1,1127 @@ +#include +#include + +#include "graph.hh" +#include "partition.hh" + +#include "igraph_decls.h" + +/* Allow using 'and' instead of '&&' with MSVC */ +#if _MSC_VER +#include +#endif + +/* + Copyright (c) 2003-2021 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 . +*/ + +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() +{ + delete[] elements; elements = nullptr; + delete[] cells; cells = nullptr; + delete[] element_to_cell_map; element_to_cell_map = nullptr; + delete[] in_pos; in_pos = nullptr; + delete[] invariant_values; invariant_values = nullptr; + N = 0; +} + + + +void Partition::init(const unsigned int M) +{ + assert(M > 0); + N = M; + + delete[] elements; + elements = new unsigned int[N]; + for(unsigned int i = 0; i < N; i++) + elements[i] = i; + + delete[] in_pos; + in_pos = new unsigned int*[N]; + for(unsigned int i = 0; i < N; i++) + in_pos[i] = elements + i; + + delete[] invariant_values; + invariant_values = new unsigned int[N]; + for(unsigned int i = 0; i < N; i++) + invariant_values[i] = 0; + + delete[] cells; + cells = new Cell[N]; + + 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; + + delete[] element_to_cell_map; + element_to_cell_map = new Cell*[N]; + 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) +{ + assert(p < bt_stack.size()); + 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]); + assert(prev_cell->length > 1); + 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]); + assert(next_cell->length > 1); + 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) +{ + assert(!cell->is_unit()); + + unsigned int * const pos = in_pos[element]; + assert((unsigned int)(pos - elements) >= cell->first); + assert((unsigned int)(pos - elements) < cell->first + cell->length); + assert(*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); + assert(elements[new_cell->first] == element); + 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; + + assert(0 < first_half_size && first_half_size < cell->length); + + /* (Pseudo)allocate new cell */ + Cell * const new_cell = free_cells; + assert(new_cell != 0); + 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; +} + + + +void +Partition::splitting_queue_add(Cell* const cell) +{ + static const unsigned int smallish_cell_threshold = 1; + assert(!cell->in_splitting_queue); + 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; + assert(new_cell != 0); + 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; + assert(ival <= 1); + assert(element_to_cell_map[element] == cell); + assert(in_pos[element] == ep0); + 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; + } + } + + assert(ep1 != elements + cell->first); + assert(ep0 != elements + cell->first + cell->length); + + /* 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 */ + assert(!new_cell->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) +{ + assert(max <= 255); + 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) +{ + assert(max_ival <= 255); + + 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; + assert(element_to_cell_map[*ep] == cell); + const unsigned int ival = invariant_values[*ep]; + assert(ival <= 255); + 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--) + { + assert(element_to_cell_map[*ep] == cell); + const unsigned int ival2 = invariant_values[*ep]; + assert(ival2 <= 255); + assert(ival2 <= max_ival); + 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); + + //assert(dcs_start[255] + dcs_count[255] == cell->length); + assert(dcs_start[max_ival] + dcs_count[max_ival] == cell->length); + + /* 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; + assert(ival > i); + assert(dcs_count[ival] > 0); + *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); + assert(new_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; + + //assert(cell->first + cell->length <= N); + + 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]; + assert(element_to_cell_map[*ep] == cell); + ep++; + for(unsigned int i = cell->length - 1; i > 0; i--) + { + assert(element_to_cell_map[*ep] == cell); + 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) +{ + assert(cell != 0); + + 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); + IGRAPH_UNUSED(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; + + delete[] cr_cells; + cr_cells = new CRCell[N]; + + delete[] cr_levels; + cr_levels = new CRCell*[N]; + + 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() +{ + delete[] cr_cells; cr_cells = nullptr; + delete[] cr_levels; cr_levels = nullptr; + + 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& 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/src/rigraph/core/isomorphism/bliss/partition.hh b/src/rigraph/core/isomorphism/bliss/partition.hh new file mode 100644 index 0000000..52cefda --- /dev/null +++ b/src/rigraph/core/isomorphism/bliss/partition.hh @@ -0,0 +1,299 @@ +#ifndef BLISS_PARTITION_HH +#define BLISS_PARTITION_HH + +/* + Copyright (c) 2003-2021 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 . +*/ + +namespace bliss { + class Partition; +} + +#include +#include +#include "kstack.hh" +#include "kqueue.hh" +#include "graph.hh" + + +namespace bliss { + +/** + * \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 refinement_stack; + + class BacktrackInfo { + public: + unsigned int refinement_stack_size; + unsigned int cr_backtrack_point; + }; + + /** \internal + * The main stack for enabling backtracking. + */ + std::vector bt_stack; + +public: + AbstractGraph* graph; + + /* Used during equitable partition refinement */ + KQueue 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 { + assert(e < N); + 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); } + + /* + * 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& 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 cr_created_trail; + std::vector cr_splitted_level_trail; + std::vector 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() +{ + assert(!splitting_queue.is_empty()); + Cell* const cell = splitting_queue.pop_front(); + assert(cell->in_splitting_queue); + 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 +{ + assert(cr_enabled); + assert(cell_index < N); + assert(cr_cells[cell_index].level != UINT_MAX); + return(cr_cells[cell_index].level); +} + +} // namespace bliss + +#endif // BLISS_PARTITION_HH diff --git a/src/rigraph/core/isomorphism/bliss/stats.hh b/src/rigraph/core/isomorphism/bliss/stats.hh new file mode 100644 index 0000000..5294581 --- /dev/null +++ b/src/rigraph/core/isomorphism/bliss/stats.hh @@ -0,0 +1,87 @@ +#ifndef BLISS_STATS_HH +#define BLISS_STATS_HH + +/* + Copyright (c) 2003-2021 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 . +*/ + +#include "graph.hh" +#include "bignum.hh" + +namespace bliss { + +/** + * \brief Statistics returned by the bliss search algorithm. + */ +class Stats +{ + friend class AbstractGraph; + /** \internal The size of the automorphism group. */ + BigNum group_size; + /** \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; + /** \internal Reset the statistics. */ + 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(); } + + /** The size of the automorphism group. */ + const BigNum& get_group_size() const {return group_size;} + /** 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;} +}; + +} // namespace bliss + +#endif // BLISS_STATS_HH diff --git a/src/rigraph/core/isomorphism/bliss/uintseqhash.cc b/src/rigraph/core/isomorphism/bliss/uintseqhash.cc new file mode 100644 index 0000000..3e6f2b3 --- /dev/null +++ b/src/rigraph/core/isomorphism/bliss/uintseqhash.cc @@ -0,0 +1,117 @@ +#include "uintseqhash.hh" + +/* + Copyright (c) 2003-2021 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 . +*/ + +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/src/rigraph/core/isomorphism/bliss/uintseqhash.hh b/src/rigraph/core/isomorphism/bliss/uintseqhash.hh new file mode 100644 index 0000000..4ac4e01 --- /dev/null +++ b/src/rigraph/core/isomorphism/bliss/uintseqhash.hh @@ -0,0 +1,63 @@ +#ifndef BLISS_UINTSEQHASH_HH +#define BLISS_UINTSEQHASH_HH + +/* + Copyright (c) 2003-2021 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 . +*/ + +namespace bliss { + +/** + * \brief A updatable 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 // BLISS_UINTSEQHASH_HH diff --git a/src/rigraph/core/isomorphism/bliss/utils.cc b/src/rigraph/core/isomorphism/bliss/utils.cc new file mode 100644 index 0000000..e2913cb --- /dev/null +++ b/src/rigraph/core/isomorphism/bliss/utils.cc @@ -0,0 +1,60 @@ +#include +#include "utils.hh" + +/* Allow using 'and' instead of '&&' with MSVC */ +#if _MSC_VER +#include +#endif + +/* + Copyright (c) 2003-2021 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 . +*/ + +namespace bliss { + +bool +is_permutation(const unsigned int N, const unsigned int* perm) +{ + if(N == 0) + return true; + std::vector 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& perm) +{ + const unsigned int N = perm.size(); + if(N == 0) + return true; + std::vector 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/src/rigraph/core/isomorphism/bliss/utils.hh b/src/rigraph/core/isomorphism/bliss/utils.hh new file mode 100644 index 0000000..4f3514a --- /dev/null +++ b/src/rigraph/core/isomorphism/bliss/utils.hh @@ -0,0 +1,46 @@ +#ifndef BLISS_UTILS_HH +#define BLISS_UTILS_HH + +/* + Copyright (c) 2003-2021 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 . +*/ + +/** + * \file + * \brief Some small utilities. + */ + +#include + +namespace bliss { + +/** + * 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& perm); + +} // namespace bliss + +#endif // BLISS_UTILS_HH diff --git a/src/rigraph/core/isomorphism/isoclasses.c b/src/rigraph/core/isomorphism/isoclasses.c new file mode 100644 index 0000000..d2a4eca --- /dev/null +++ b/src/rigraph/core/isomorphism/isoclasses.c @@ -0,0 +1,2876 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2006-2021 The igraph development 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., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA +*/ + +#include "igraph_topology.h" + +#include "igraph_constructors.h" +#include "igraph_interface.h" + +#include "isomorphism/isoclasses.h" + +/* + * Small labelled graphs are encoded into a compact representation, a "code", + * that fits into a single integer value. Each non-loop edge corresponds to + * a specific bit of the integer. The edge-to-bit mappings are stored in + * the "isoclass_idx" arrays while the bit-to-edge mappings are in the "classedges" + * arrays. + * + * The "isoclass2" array is a mapping from the code of each possible labelled + * graph to its isomorphism class. A canonical representative of each isomorphism + * class is stored in "isographs". + * + * In the names of arrays, the number refers to the vertex count, while "u" + * indicates undirected graphs (the other arrays store directed ones). + * + * Description of each array for graphs of size n: + * + * isosclass_idx represents an n-by-n matrix stored in column-major order. + * Element i,j of the matrix is an integer with a single bit set. This bit, + * if set, represents edge i-j in the graph code. + * + * isoclass2[code] gives the isomorphism class of the graph represented by code. + * Classes are labelled by integers starting at 0, after ordering them by the + * graph code of their smallest-code representative. + * + * isographs[class] is the code of a graph belonging to the given class. For each + * class, the representative with the smallest code is chosen. + * + * classedges[2*i] - classedges[2*i+1] are the endpoints of the edge represented + * by bit i in the code. Bits are numbered from most to least significant, thus + * the most significant one has index i=0. + */ + +const unsigned int igraph_i_isoclass_3_idx[] = { 0, 4, 16, 1, 0, 32, 2, 8, 0 }; + +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_idx[] = { 0, 1, 2, 1, 0, 4, 2, 4, 0 }; + +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_isoclass_5u_idx[] = { + 0, 1, 2, 8, 64, 1, 0, 4, 16, 128, 2, 4, 0, 32, 256, 8, 16, 32, 0, 512, 64, 128, 256, 512, 0 +}; + +const unsigned int igraph_i_isoclass_6u_idx[] = { + 0, 1, 2, 8, 64, 1024, 1, 0, 4, 16, 128, 2048, 2, 4, 0, 32, 256, 4096, + 8, 16, 32, 0, 512, 8192, 64, 128, 256, 512, 0, 16384, 1024, 2048, + 4096, 8192, 16384, 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_isoclass2_5u[] = { + 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, 1, 2, 2, 4, 5, + 6, 6, 7, 2, 4, 4, 11, 12, 13, 13, 14, 5, 6, 12, 13, 12, 13, 15, 16, + 6, 7, 13, 14, 15, 16, 17, 18, 5, 12, 6, 13, 12, 15, 13, 16, 6, 13, 7, + 14, 15, 17, 16, 18, 12, 15, 15, 17, 19, 20, 20, 21, 13, 16, 16, 18, + 20, 21, 21, 22, 1, 2, 5, 6, 2, 4, 6, 7, 5, 6, 12, 13, 12, 13, 15, 16, + 2, 4, 12, 13, 4, 11, 13, 14, 6, 7, 15, 16, 13, 14, 17, 18, 5, 12, 12, + 15, 6, 13, 13, 16, 12, 15, 19, 20, 15, 17, 20, 21, 6, 13, 15, 17, 7, + 14, 16, 18, 13, 16, 20, 21, 16, 18, 21, 22, 2, 3, 6, 7, 6, 7, 8, 9, + 6, 7, 13, 14, 15, 16, 17, 18, 6, 7, 15, 16, 13, 14, 17, 18, 8, 9, 17, + 18, 17, 18, 23, 24, 12, 19, 15, 20, 15, 20, 17, 21, 15, 20, 20, 25, + 26, 27, 27, 28, 15, 20, 26, 27, 20, 25, 27, 28, 17, 21, 27, 28, 27, + 28, 29, 30, 1, 5, 2, 6, 2, 6, 4, 7, 5, 12, 6, 13, 12, 15, 13, 16, 5, + 12, 12, 15, 6, 13, 13, 16, 12, 19, 15, 20, 15, 20, 17, 21, 2, 12, 4, + 13, 4, 13, 11, 14, 6, 15, 7, 16, 13, 17, 14, 18, 6, 15, 13, 17, 7, + 16, 14, 18, 13, 20, 16, 21, 16, 21, 18, 22, 2, 6, 3, 7, 6, 8, 7, 9, + 6, 13, 7, 14, 15, 17, 16, 18, 12, 15, 19, 20, 15, 17, 20, 21, 15, 20, + 20, 25, 26, 27, 27, 28, 6, 15, 7, 16, 13, 17, 14, 18, 8, 17, 9, 18, + 17, 23, 18, 24, 15, 26, 20, 27, 20, 27, 25, 28, 17, 27, 21, 28, 27, + 29, 28, 30, 2, 6, 6, 8, 3, 7, 7, 9, 12, 15, 15, 17, 19, 20, 20, 21, + 6, 13, 15, 17, 7, 14, 16, 18, 15, 20, 26, 27, 20, 25, 27, 28, 6, 15, + 13, 17, 7, 16, 14, 18, 15, 26, 20, 27, 20, 27, 25, 28, 8, 17, 17, 23, + 9, 18, 18, 24, 17, 27, 27, 29, 21, 28, 28, 30, 4, 7, 7, 9, 7, 9, 9, + 10, 13, 16, 16, 18, 20, 21, 21, 22, 13, 16, 20, 21, 16, 18, 21, 22, + 17, 21, 27, 28, 27, 28, 29, 30, 13, 20, 16, 21, 16, 21, 18, 22, 17, + 27, 21, 28, 27, 29, 28, 30, 17, 27, 27, 29, 21, 28, 28, 30, 23, 29, + 29, 31, 29, 31, 31, 32, 1, 5, 5, 12, 5, 12, 12, 19, 2, 6, 6, 13, 12, + 15, 15, 20, 2, 6, 12, 15, 6, 13, 15, 20, 4, 7, 13, 16, 13, 16, 17, + 21, 2, 12, 6, 15, 6, 15, 13, 20, 4, 13, 7, 16, 13, 17, 16, 21, 4, 13, + 13, 17, 7, 16, 16, 21, 11, 14, 14, 18, 14, 18, 18, 22, 2, 6, 6, 13, + 12, 15, 15, 20, 3, 7, 7, 14, 19, 20, 20, 25, 6, 8, 15, 17, 15, 17, + 26, 27, 7, 9, 16, 18, 20, 21, 27, 28, 6, 15, 8, 17, 15, 26, 17, 27, + 7, 16, 9, 18, 20, 27, 21, 28, 13, 17, 17, 23, 20, 27, 27, 29, 14, 18, + 18, 24, 25, 28, 28, 30, 2, 6, 12, 15, 6, 13, 15, 20, 6, 8, 15, 17, + 15, 17, 26, 27, 3, 7, 19, 20, 7, 14, 20, 25, 7, 9, 20, 21, 16, 18, + 27, 28, 6, 15, 15, 26, 8, 17, 17, 27, 13, 17, 20, 27, 17, 23, 27, 29, + 7, 16, 20, 27, 9, 18, 21, 28, 14, 18, 25, 28, 18, 24, 28, 30, 4, 7, + 13, 16, 13, 16, 17, 21, 7, 9, 16, 18, 20, 21, 27, 28, 7, 9, 20, 21, + 16, 18, 27, 28, 9, 10, 21, 22, 21, 22, 29, 30, 13, 20, 17, 27, 17, + 27, 23, 29, 16, 21, 21, 28, 27, 29, 29, 31, 16, 21, 27, 29, 21, 28, + 29, 31, 18, 22, 28, 30, 28, 30, 31, 32, 2, 12, 6, 15, 6, 15, 13, 20, + 6, 15, 8, 17, 15, 26, 17, 27, 6, 15, 15, 26, 8, 17, 17, 27, 13, 20, + 17, 27, 17, 27, 23, 29, 3, 19, 7, 20, 7, 20, 14, 25, 7, 20, 9, 21, + 16, 27, 18, 28, 7, 20, 16, 27, 9, 21, 18, 28, 14, 25, 18, 28, 18, 28, + 24, 30, 4, 13, 7, 16, 13, 17, 16, 21, 7, 16, 9, 18, 20, 27, 21, 28, + 13, 17, 20, 27, 17, 23, 27, 29, 16, 21, 21, 28, 27, 29, 29, 31, 7, + 20, 9, 21, 16, 27, 18, 28, 9, 21, 10, 22, 21, 29, 22, 30, 16, 27, 21, + 29, 21, 29, 28, 31, 18, 28, 22, 30, 28, 31, 30, 32, 4, 13, 13, 17, 7, + 16, 16, 21, 13, 17, 17, 23, 20, 27, 27, 29, 7, 16, 20, 27, 9, 18, 21, + 28, 16, 21, 27, 29, 21, 28, 29, 31, 7, 20, 16, 27, 9, 21, 18, 28, 16, + 27, 21, 29, 21, 29, 28, 31, 9, 21, 21, 29, 10, 22, 22, 30, 18, 28, + 28, 31, 22, 30, 30, 32, 11, 14, 14, 18, 14, 18, 18, 22, 14, 18, 18, + 24, 25, 28, 28, 30, 14, 18, 25, 28, 18, 24, 28, 30, 18, 22, 28, 30, + 28, 30, 31, 32, 14, 25, 18, 28, 18, 28, 24, 30, 18, 28, 22, 30, 28, + 31, 30, 32, 18, 28, 28, 31, 22, 30, 30, 32, 24, 30, 30, 32, 30, 32, + 32, 33 +}; + +const unsigned int igraph_i_isoclass2_6u[] = { + 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, 1, 2, 2, 4, 5, + 6, 6, 7, 2, 4, 4, 11, 12, 13, 13, 14, 5, 6, 12, 13, 12, 13, 15, 16, + 6, 7, 13, 14, 15, 16, 17, 18, 5, 12, 6, 13, 12, 15, 13, 16, 6, 13, 7, + 14, 15, 17, 16, 18, 12, 15, 15, 17, 19, 20, 20, 21, 13, 16, 16, 18, + 20, 21, 21, 22, 1, 2, 5, 6, 2, 4, 6, 7, 5, 6, 12, 13, 12, 13, 15, 16, + 2, 4, 12, 13, 4, 11, 13, 14, 6, 7, 15, 16, 13, 14, 17, 18, 5, 12, 12, + 15, 6, 13, 13, 16, 12, 15, 19, 20, 15, 17, 20, 21, 6, 13, 15, 17, 7, + 14, 16, 18, 13, 16, 20, 21, 16, 18, 21, 22, 2, 3, 6, 7, 6, 7, 8, 9, + 6, 7, 13, 14, 15, 16, 17, 18, 6, 7, 15, 16, 13, 14, 17, 18, 8, 9, 17, + 18, 17, 18, 23, 24, 12, 19, 15, 20, 15, 20, 17, 21, 15, 20, 20, 25, + 26, 27, 27, 28, 15, 20, 26, 27, 20, 25, 27, 28, 17, 21, 27, 28, 27, + 28, 29, 30, 1, 5, 2, 6, 2, 6, 4, 7, 5, 12, 6, 13, 12, 15, 13, 16, 5, + 12, 12, 15, 6, 13, 13, 16, 12, 19, 15, 20, 15, 20, 17, 21, 2, 12, 4, + 13, 4, 13, 11, 14, 6, 15, 7, 16, 13, 17, 14, 18, 6, 15, 13, 17, 7, + 16, 14, 18, 13, 20, 16, 21, 16, 21, 18, 22, 2, 6, 3, 7, 6, 8, 7, 9, + 6, 13, 7, 14, 15, 17, 16, 18, 12, 15, 19, 20, 15, 17, 20, 21, 15, 20, + 20, 25, 26, 27, 27, 28, 6, 15, 7, 16, 13, 17, 14, 18, 8, 17, 9, 18, + 17, 23, 18, 24, 15, 26, 20, 27, 20, 27, 25, 28, 17, 27, 21, 28, 27, + 29, 28, 30, 2, 6, 6, 8, 3, 7, 7, 9, 12, 15, 15, 17, 19, 20, 20, 21, + 6, 13, 15, 17, 7, 14, 16, 18, 15, 20, 26, 27, 20, 25, 27, 28, 6, 15, + 13, 17, 7, 16, 14, 18, 15, 26, 20, 27, 20, 27, 25, 28, 8, 17, 17, 23, + 9, 18, 18, 24, 17, 27, 27, 29, 21, 28, 28, 30, 4, 7, 7, 9, 7, 9, 9, + 10, 13, 16, 16, 18, 20, 21, 21, 22, 13, 16, 20, 21, 16, 18, 21, 22, + 17, 21, 27, 28, 27, 28, 29, 30, 13, 20, 16, 21, 16, 21, 18, 22, 17, + 27, 21, 28, 27, 29, 28, 30, 17, 27, 27, 29, 21, 28, 28, 30, 23, 29, + 29, 31, 29, 31, 31, 32, 1, 5, 5, 12, 5, 12, 12, 19, 2, 6, 6, 13, 12, + 15, 15, 20, 2, 6, 12, 15, 6, 13, 15, 20, 4, 7, 13, 16, 13, 16, 17, + 21, 2, 12, 6, 15, 6, 15, 13, 20, 4, 13, 7, 16, 13, 17, 16, 21, 4, 13, + 13, 17, 7, 16, 16, 21, 11, 14, 14, 18, 14, 18, 18, 22, 2, 6, 6, 13, + 12, 15, 15, 20, 3, 7, 7, 14, 19, 20, 20, 25, 6, 8, 15, 17, 15, 17, + 26, 27, 7, 9, 16, 18, 20, 21, 27, 28, 6, 15, 8, 17, 15, 26, 17, 27, + 7, 16, 9, 18, 20, 27, 21, 28, 13, 17, 17, 23, 20, 27, 27, 29, 14, 18, + 18, 24, 25, 28, 28, 30, 2, 6, 12, 15, 6, 13, 15, 20, 6, 8, 15, 17, + 15, 17, 26, 27, 3, 7, 19, 20, 7, 14, 20, 25, 7, 9, 20, 21, 16, 18, + 27, 28, 6, 15, 15, 26, 8, 17, 17, 27, 13, 17, 20, 27, 17, 23, 27, 29, + 7, 16, 20, 27, 9, 18, 21, 28, 14, 18, 25, 28, 18, 24, 28, 30, 4, 7, + 13, 16, 13, 16, 17, 21, 7, 9, 16, 18, 20, 21, 27, 28, 7, 9, 20, 21, + 16, 18, 27, 28, 9, 10, 21, 22, 21, 22, 29, 30, 13, 20, 17, 27, 17, + 27, 23, 29, 16, 21, 21, 28, 27, 29, 29, 31, 16, 21, 27, 29, 21, 28, + 29, 31, 18, 22, 28, 30, 28, 30, 31, 32, 2, 12, 6, 15, 6, 15, 13, 20, + 6, 15, 8, 17, 15, 26, 17, 27, 6, 15, 15, 26, 8, 17, 17, 27, 13, 20, + 17, 27, 17, 27, 23, 29, 3, 19, 7, 20, 7, 20, 14, 25, 7, 20, 9, 21, + 16, 27, 18, 28, 7, 20, 16, 27, 9, 21, 18, 28, 14, 25, 18, 28, 18, 28, + 24, 30, 4, 13, 7, 16, 13, 17, 16, 21, 7, 16, 9, 18, 20, 27, 21, 28, + 13, 17, 20, 27, 17, 23, 27, 29, 16, 21, 21, 28, 27, 29, 29, 31, 7, + 20, 9, 21, 16, 27, 18, 28, 9, 21, 10, 22, 21, 29, 22, 30, 16, 27, 21, + 29, 21, 29, 28, 31, 18, 28, 22, 30, 28, 31, 30, 32, 4, 13, 13, 17, 7, + 16, 16, 21, 13, 17, 17, 23, 20, 27, 27, 29, 7, 16, 20, 27, 9, 18, 21, + 28, 16, 21, 27, 29, 21, 28, 29, 31, 7, 20, 16, 27, 9, 21, 18, 28, 16, + 27, 21, 29, 21, 29, 28, 31, 9, 21, 21, 29, 10, 22, 22, 30, 18, 28, + 28, 31, 22, 30, 30, 32, 11, 14, 14, 18, 14, 18, 18, 22, 14, 18, 18, + 24, 25, 28, 28, 30, 14, 18, 25, 28, 18, 24, 28, 30, 18, 22, 28, 30, + 28, 30, 31, 32, 14, 25, 18, 28, 18, 28, 24, 30, 18, 28, 22, 30, 28, + 31, 30, 32, 18, 28, 28, 31, 22, 30, 30, 32, 24, 30, 30, 32, 30, 32, + 32, 33, 1, 2, 2, 4, 5, 6, 6, 7, 2, 4, 4, 11, 12, 13, 13, 14, 5, 6, + 12, 13, 12, 13, 15, 16, 6, 7, 13, 14, 15, 16, 17, 18, 5, 12, 6, 13, + 12, 15, 13, 16, 6, 13, 7, 14, 15, 17, 16, 18, 12, 15, 15, 17, 19, 20, + 20, 21, 13, 16, 16, 18, 20, 21, 21, 22, 2, 4, 4, 11, 12, 13, 13, 14, + 4, 11, 11, 34, 35, 36, 36, 37, 12, 13, 35, 36, 38, 39, 40, 41, 13, + 14, 36, 37, 40, 41, 42, 43, 12, 35, 13, 36, 38, 40, 39, 41, 13, 36, + 14, 37, 40, 42, 41, 43, 38, 40, 40, 42, 44, 45, 45, 46, 39, 41, 41, + 43, 45, 46, 46, 47, 5, 6, 12, 13, 12, 13, 15, 16, 12, 13, 35, 36, 38, + 39, 40, 41, 12, 13, 38, 39, 35, 36, 40, 41, 15, 16, 40, 41, 40, 41, + 48, 49, 50, 51, 51, 52, 51, 52, 52, 53, 51, 52, 54, 55, 56, 57, 58, + 59, 51, 52, 56, 57, 54, 55, 58, 59, 52, 53, 58, 59, 58, 59, 60, 61, + 6, 7, 13, 14, 15, 16, 17, 18, 13, 14, 36, 37, 40, 41, 42, 43, 15, 16, + 40, 41, 40, 41, 48, 49, 17, 18, 42, 43, 48, 49, 62, 63, 51, 54, 52, + 55, 56, 58, 57, 59, 52, 55, 55, 64, 65, 66, 66, 67, 56, 58, 65, 66, + 68, 69, 70, 71, 57, 59, 66, 67, 70, 71, 72, 73, 5, 12, 6, 13, 12, 15, + 13, 16, 12, 35, 13, 36, 38, 40, 39, 41, 50, 51, 51, 52, 51, 52, 52, + 53, 51, 54, 52, 55, 56, 58, 57, 59, 12, 38, 13, 39, 35, 40, 36, 41, + 15, 40, 16, 41, 40, 48, 41, 49, 51, 56, 52, 57, 54, 58, 55, 59, 52, + 58, 53, 59, 58, 60, 59, 61, 6, 13, 7, 14, 15, 17, 16, 18, 13, 36, 14, + 37, 40, 42, 41, 43, 51, 52, 54, 55, 56, 57, 58, 59, 52, 55, 55, 64, + 65, 66, 66, 67, 15, 40, 16, 41, 40, 48, 41, 49, 17, 42, 18, 43, 48, + 62, 49, 63, 56, 65, 58, 66, 68, 70, 69, 71, 57, 66, 59, 67, 70, 72, + 71, 73, 12, 15, 15, 17, 19, 20, 20, 21, 38, 40, 40, 42, 44, 45, 45, + 46, 51, 52, 56, 57, 54, 55, 58, 59, 56, 58, 65, 66, 68, 69, 70, 71, + 51, 56, 52, 57, 54, 58, 55, 59, 56, 65, 58, 66, 68, 70, 69, 71, 74, + 75, 75, 76, 77, 78, 78, 79, 75, 80, 80, 81, 82, 83, 83, 84, 13, 16, + 16, 18, 20, 21, 21, 22, 39, 41, 41, 43, 45, 46, 46, 47, 52, 53, 58, + 59, 58, 59, 60, 61, 57, 59, 66, 67, 70, 71, 72, 73, 52, 58, 53, 59, + 58, 60, 59, 61, 57, 66, 59, 67, 70, 72, 71, 73, 75, 80, 80, 81, 82, + 83, 83, 84, 76, 81, 81, 85, 86, 87, 87, 88, 5, 12, 12, 35, 50, 51, + 51, 54, 6, 13, 13, 36, 51, 52, 52, 55, 12, 15, 38, 40, 51, 52, 56, + 58, 13, 16, 39, 41, 52, 53, 57, 59, 12, 38, 15, 40, 51, 56, 52, 58, + 13, 39, 16, 41, 52, 57, 53, 59, 35, 40, 40, 48, 54, 58, 58, 60, 36, + 41, 41, 49, 55, 59, 59, 61, 6, 13, 13, 36, 51, 52, 52, 55, 7, 14, 14, + 37, 54, 55, 55, 64, 15, 17, 40, 42, 56, 57, 65, 66, 16, 18, 41, 43, + 58, 59, 66, 67, 15, 40, 17, 42, 56, 65, 57, 66, 16, 41, 18, 43, 58, + 66, 59, 67, 40, 48, 48, 62, 68, 70, 70, 72, 41, 49, 49, 63, 69, 71, + 71, 73, 12, 15, 38, 40, 51, 52, 56, 58, 15, 17, 40, 42, 56, 57, 65, + 66, 19, 20, 44, 45, 54, 55, 68, 69, 20, 21, 45, 46, 58, 59, 70, 71, + 51, 56, 56, 65, 74, 75, 75, 80, 52, 57, 58, 66, 75, 76, 80, 81, 54, + 58, 68, 70, 77, 78, 82, 83, 55, 59, 69, 71, 78, 79, 83, 84, 13, 16, + 39, 41, 52, 53, 57, 59, 16, 18, 41, 43, 58, 59, 66, 67, 20, 21, 45, + 46, 58, 59, 70, 71, 21, 22, 46, 47, 60, 61, 72, 73, 52, 58, 57, 66, + 75, 80, 76, 81, 53, 59, 59, 67, 80, 81, 81, 85, 58, 60, 70, 72, 82, + 83, 86, 87, 59, 61, 71, 73, 83, 84, 87, 88, 12, 38, 15, 40, 51, 56, + 52, 58, 15, 40, 17, 42, 56, 65, 57, 66, 51, 56, 56, 65, 74, 75, 75, + 80, 52, 58, 57, 66, 75, 80, 76, 81, 19, 44, 20, 45, 54, 68, 55, 69, + 20, 45, 21, 46, 58, 70, 59, 71, 54, 68, 58, 70, 77, 82, 78, 83, 55, + 69, 59, 71, 78, 83, 79, 84, 13, 39, 16, 41, 52, 57, 53, 59, 16, 41, + 18, 43, 58, 66, 59, 67, 52, 57, 58, 66, 75, 76, 80, 81, 53, 59, 59, + 67, 80, 81, 81, 85, 20, 45, 21, 46, 58, 70, 59, 71, 21, 46, 22, 47, + 60, 72, 61, 73, 58, 70, 60, 72, 82, 86, 83, 87, 59, 71, 61, 73, 83, + 87, 84, 88, 35, 40, 40, 48, 54, 58, 58, 60, 40, 48, 48, 62, 68, 70, + 70, 72, 54, 58, 68, 70, 77, 78, 82, 83, 58, 60, 70, 72, 82, 83, 86, + 87, 54, 68, 58, 70, 77, 82, 78, 83, 58, 70, 60, 72, 82, 86, 83, 87, + 77, 82, 82, 86, 89, 90, 90, 91, 78, 83, 83, 87, 90, 91, 91, 92, 36, + 41, 41, 49, 55, 59, 59, 61, 41, 49, 49, 63, 69, 71, 71, 73, 55, 59, + 69, 71, 78, 79, 83, 84, 59, 61, 71, 73, 83, 84, 87, 88, 55, 69, 59, + 71, 78, 83, 79, 84, 59, 71, 61, 73, 83, 87, 84, 88, 78, 83, 83, 87, + 90, 91, 91, 92, 79, 84, 84, 88, 91, 92, 92, 93, 1, 2, 5, 6, 2, 4, 6, + 7, 5, 6, 12, 13, 12, 13, 15, 16, 2, 4, 12, 13, 4, 11, 13, 14, 6, 7, + 15, 16, 13, 14, 17, 18, 5, 12, 12, 15, 6, 13, 13, 16, 12, 15, 19, 20, + 15, 17, 20, 21, 6, 13, 15, 17, 7, 14, 16, 18, 13, 16, 20, 21, 16, 18, + 21, 22, 5, 6, 12, 13, 12, 13, 15, 16, 12, 13, 35, 36, 38, 39, 40, 41, + 12, 13, 38, 39, 35, 36, 40, 41, 15, 16, 40, 41, 40, 41, 48, 49, 50, + 51, 51, 52, 51, 52, 52, 53, 51, 52, 54, 55, 56, 57, 58, 59, 51, 52, + 56, 57, 54, 55, 58, 59, 52, 53, 58, 59, 58, 59, 60, 61, 2, 4, 12, 13, + 4, 11, 13, 14, 12, 13, 38, 39, 35, 36, 40, 41, 4, 11, 35, 36, 11, 34, + 36, 37, 13, 14, 40, 41, 36, 37, 42, 43, 12, 35, 38, 40, 13, 36, 39, + 41, 38, 40, 44, 45, 40, 42, 45, 46, 13, 36, 40, 42, 14, 37, 41, 43, + 39, 41, 45, 46, 41, 43, 46, 47, 6, 7, 15, 16, 13, 14, 17, 18, 15, 16, + 40, 41, 40, 41, 48, 49, 13, 14, 40, 41, 36, 37, 42, 43, 17, 18, 48, + 49, 42, 43, 62, 63, 51, 54, 56, 58, 52, 55, 57, 59, 56, 58, 68, 69, + 65, 66, 70, 71, 52, 55, 65, 66, 55, 64, 66, 67, 57, 59, 70, 71, 66, + 67, 72, 73, 5, 12, 12, 15, 6, 13, 13, 16, 50, 51, 51, 52, 51, 52, 52, + 53, 12, 35, 38, 40, 13, 36, 39, 41, 51, 54, 56, 58, 52, 55, 57, 59, + 12, 38, 35, 40, 13, 39, 36, 41, 51, 56, 54, 58, 52, 57, 55, 59, 15, + 40, 40, 48, 16, 41, 41, 49, 52, 58, 58, 60, 53, 59, 59, 61, 12, 15, + 19, 20, 15, 17, 20, 21, 51, 52, 54, 55, 56, 57, 58, 59, 38, 40, 44, + 45, 40, 42, 45, 46, 56, 58, 68, 69, 65, 66, 70, 71, 51, 56, 54, 58, + 52, 57, 55, 59, 74, 75, 77, 78, 75, 76, 78, 79, 56, 65, 68, 70, 58, + 66, 69, 71, 75, 80, 82, 83, 80, 81, 83, 84, 6, 13, 15, 17, 7, 14, 16, + 18, 51, 52, 56, 57, 54, 55, 58, 59, 13, 36, 40, 42, 14, 37, 41, 43, + 52, 55, 65, 66, 55, 64, 66, 67, 15, 40, 40, 48, 16, 41, 41, 49, 56, + 65, 68, 70, 58, 66, 69, 71, 17, 42, 48, 62, 18, 43, 49, 63, 57, 66, + 70, 72, 59, 67, 71, 73, 13, 16, 20, 21, 16, 18, 21, 22, 52, 53, 58, + 59, 58, 59, 60, 61, 39, 41, 45, 46, 41, 43, 46, 47, 57, 59, 70, 71, + 66, 67, 72, 73, 52, 58, 58, 60, 53, 59, 59, 61, 75, 80, 82, 83, 80, + 81, 83, 84, 57, 66, 70, 72, 59, 67, 71, 73, 76, 81, 86, 87, 81, 85, + 87, 88, 5, 12, 50, 51, 12, 35, 51, 54, 12, 15, 51, 52, 38, 40, 56, + 58, 6, 13, 51, 52, 13, 36, 52, 55, 13, 16, 52, 53, 39, 41, 57, 59, + 12, 38, 51, 56, 15, 40, 52, 58, 35, 40, 54, 58, 40, 48, 58, 60, 13, + 39, 52, 57, 16, 41, 53, 59, 36, 41, 55, 59, 41, 49, 59, 61, 12, 15, + 51, 52, 38, 40, 56, 58, 19, 20, 54, 55, 44, 45, 68, 69, 15, 17, 56, + 57, 40, 42, 65, 66, 20, 21, 58, 59, 45, 46, 70, 71, 51, 56, 74, 75, + 56, 65, 75, 80, 54, 58, 77, 78, 68, 70, 82, 83, 52, 57, 75, 76, 58, + 66, 80, 81, 55, 59, 78, 79, 69, 71, 83, 84, 6, 13, 51, 52, 13, 36, + 52, 55, 15, 17, 56, 57, 40, 42, 65, 66, 7, 14, 54, 55, 14, 37, 55, + 64, 16, 18, 58, 59, 41, 43, 66, 67, 15, 40, 56, 65, 17, 42, 57, 66, + 40, 48, 68, 70, 48, 62, 70, 72, 16, 41, 58, 66, 18, 43, 59, 67, 41, + 49, 69, 71, 49, 63, 71, 73, 13, 16, 52, 53, 39, 41, 57, 59, 20, 21, + 58, 59, 45, 46, 70, 71, 16, 18, 58, 59, 41, 43, 66, 67, 21, 22, 60, + 61, 46, 47, 72, 73, 52, 58, 75, 80, 57, 66, 76, 81, 58, 60, 82, 83, + 70, 72, 86, 87, 53, 59, 80, 81, 59, 67, 81, 85, 59, 61, 83, 84, 71, + 73, 87, 88, 12, 38, 51, 56, 15, 40, 52, 58, 51, 56, 74, 75, 56, 65, + 75, 80, 15, 40, 56, 65, 17, 42, 57, 66, 52, 58, 75, 80, 57, 66, 76, + 81, 19, 44, 54, 68, 20, 45, 55, 69, 54, 68, 77, 82, 58, 70, 78, 83, + 20, 45, 58, 70, 21, 46, 59, 71, 55, 69, 78, 83, 59, 71, 79, 84, 35, + 40, 54, 58, 40, 48, 58, 60, 54, 58, 77, 78, 68, 70, 82, 83, 40, 48, + 68, 70, 48, 62, 70, 72, 58, 60, 82, 83, 70, 72, 86, 87, 54, 68, 77, + 82, 58, 70, 78, 83, 77, 82, 89, 90, 82, 86, 90, 91, 58, 70, 82, 86, + 60, 72, 83, 87, 78, 83, 90, 91, 83, 87, 91, 92, 13, 39, 52, 57, 16, + 41, 53, 59, 52, 57, 75, 76, 58, 66, 80, 81, 16, 41, 58, 66, 18, 43, + 59, 67, 53, 59, 80, 81, 59, 67, 81, 85, 20, 45, 58, 70, 21, 46, 59, + 71, 58, 70, 82, 86, 60, 72, 83, 87, 21, 46, 60, 72, 22, 47, 61, 73, + 59, 71, 83, 87, 61, 73, 84, 88, 36, 41, 55, 59, 41, 49, 59, 61, 55, + 59, 78, 79, 69, 71, 83, 84, 41, 49, 69, 71, 49, 63, 71, 73, 59, 61, + 83, 84, 71, 73, 87, 88, 55, 69, 78, 83, 59, 71, 79, 84, 78, 83, 90, + 91, 83, 87, 91, 92, 59, 71, 83, 87, 61, 73, 84, 88, 79, 84, 91, 92, + 84, 88, 92, 93, 2, 3, 6, 7, 6, 7, 8, 9, 6, 7, 13, 14, 15, 16, 17, 18, + 6, 7, 15, 16, 13, 14, 17, 18, 8, 9, 17, 18, 17, 18, 23, 24, 12, 19, + 15, 20, 15, 20, 17, 21, 15, 20, 20, 25, 26, 27, 27, 28, 15, 20, 26, + 27, 20, 25, 27, 28, 17, 21, 27, 28, 27, 28, 29, 30, 6, 7, 13, 14, 15, + 16, 17, 18, 13, 14, 36, 37, 40, 41, 42, 43, 15, 16, 40, 41, 40, 41, + 48, 49, 17, 18, 42, 43, 48, 49, 62, 63, 51, 54, 52, 55, 56, 58, 57, + 59, 52, 55, 55, 64, 65, 66, 66, 67, 56, 58, 65, 66, 68, 69, 70, 71, + 57, 59, 66, 67, 70, 71, 72, 73, 6, 7, 15, 16, 13, 14, 17, 18, 15, 16, + 40, 41, 40, 41, 48, 49, 13, 14, 40, 41, 36, 37, 42, 43, 17, 18, 48, + 49, 42, 43, 62, 63, 51, 54, 56, 58, 52, 55, 57, 59, 56, 58, 68, 69, + 65, 66, 70, 71, 52, 55, 65, 66, 55, 64, 66, 67, 57, 59, 70, 71, 66, + 67, 72, 73, 8, 9, 17, 18, 17, 18, 23, 24, 17, 18, 42, 43, 48, 49, 62, + 63, 17, 18, 48, 49, 42, 43, 62, 63, 23, 24, 62, 63, 62, 63, 94, 95, + 74, 77, 75, 78, 75, 78, 76, 79, 75, 78, 96, 97, 98, 99, 100, 101, 75, + 78, 98, 99, 96, 97, 100, 101, 76, 79, 100, 101, 100, 101, 102, 103, + 12, 19, 15, 20, 15, 20, 17, 21, 51, 54, 52, 55, 56, 58, 57, 59, 51, + 54, 56, 58, 52, 55, 57, 59, 74, 77, 75, 78, 75, 78, 76, 79, 38, 44, + 40, 45, 40, 45, 42, 46, 56, 68, 58, 69, 65, 70, 66, 71, 56, 68, 65, + 70, 58, 69, 66, 71, 75, 82, 80, 83, 80, 83, 81, 84, 15, 20, 20, 25, + 26, 27, 27, 28, 52, 55, 55, 64, 65, 66, 66, 67, 56, 58, 68, 69, 65, + 66, 70, 71, 75, 78, 96, 97, 98, 99, 100, 101, 56, 68, 58, 69, 65, 70, + 66, 71, 75, 96, 78, 97, 98, 100, 99, 101, 104, 105, 105, 106, 105, + 106, 106, 107, 108, 109, 109, 110, 111, 112, 112, 113, 15, 20, 26, + 27, 20, 25, 27, 28, 56, 58, 65, 66, 68, 69, 70, 71, 52, 55, 65, 66, + 55, 64, 66, 67, 75, 78, 98, 99, 96, 97, 100, 101, 56, 68, 65, 70, 58, + 69, 66, 71, 104, 105, 105, 106, 105, 106, 106, 107, 75, 96, 98, 100, + 78, 97, 99, 101, 108, 109, 111, 112, 109, 110, 112, 113, 17, 21, 27, + 28, 27, 28, 29, 30, 57, 59, 66, 67, 70, 71, 72, 73, 57, 59, 70, 71, + 66, 67, 72, 73, 76, 79, 100, 101, 100, 101, 102, 103, 75, 82, 80, 83, + 80, 83, 81, 84, 108, 109, 109, 110, 111, 112, 112, 113, 108, 109, + 111, 112, 109, 110, 112, 113, 114, 115, 116, 117, 116, 117, 118, 119, + 12, 19, 51, 54, 51, 54, 74, 77, 15, 20, 52, 55, 56, 58, 75, 78, 15, + 20, 56, 58, 52, 55, 75, 78, 17, 21, 57, 59, 57, 59, 76, 79, 38, 44, + 56, 68, 56, 68, 75, 82, 40, 45, 58, 69, 65, 70, 80, 83, 40, 45, 65, + 70, 58, 69, 80, 83, 42, 46, 66, 71, 66, 71, 81, 84, 15, 20, 52, 55, + 56, 58, 75, 78, 20, 25, 55, 64, 68, 69, 96, 97, 26, 27, 65, 66, 65, + 66, 98, 99, 27, 28, 66, 67, 70, 71, 100, 101, 56, 68, 75, 96, 104, + 105, 108, 109, 58, 69, 78, 97, 105, 106, 109, 110, 65, 70, 98, 100, + 105, 106, 111, 112, 66, 71, 99, 101, 106, 107, 112, 113, 15, 20, 56, + 58, 52, 55, 75, 78, 26, 27, 65, 66, 65, 66, 98, 99, 20, 25, 68, 69, + 55, 64, 96, 97, 27, 28, 70, 71, 66, 67, 100, 101, 56, 68, 104, 105, + 75, 96, 108, 109, 65, 70, 105, 106, 98, 100, 111, 112, 58, 69, 105, + 106, 78, 97, 109, 110, 66, 71, 106, 107, 99, 101, 112, 113, 17, 21, + 57, 59, 57, 59, 76, 79, 27, 28, 66, 67, 70, 71, 100, 101, 27, 28, 70, + 71, 66, 67, 100, 101, 29, 30, 72, 73, 72, 73, 102, 103, 75, 82, 108, + 109, 108, 109, 114, 115, 80, 83, 109, 110, 111, 112, 116, 117, 80, + 83, 111, 112, 109, 110, 116, 117, 81, 84, 112, 113, 112, 113, 118, + 119, 38, 44, 56, 68, 56, 68, 75, 82, 56, 68, 75, 96, 104, 105, 108, + 109, 56, 68, 104, 105, 75, 96, 108, 109, 75, 82, 108, 109, 108, 109, + 114, 115, 44, 120, 68, 121, 68, 121, 96, 122, 68, 121, 82, 122, 105, + 123, 109, 124, 68, 121, 105, 123, 82, 122, 109, 124, 96, 122, 109, + 124, 109, 124, 115, 125, 40, 45, 58, 69, 65, 70, 80, 83, 58, 69, 78, + 97, 105, 106, 109, 110, 65, 70, 105, 106, 98, 100, 111, 112, 80, 83, + 109, 110, 111, 112, 116, 117, 68, 121, 82, 122, 105, 123, 109, 124, + 82, 122, 90, 126, 127, 128, 129, 130, 105, 123, 127, 128, 127, 128, + 131, 132, 109, 124, 129, 130, 131, 132, 133, 134, 40, 45, 65, 70, 58, + 69, 80, 83, 65, 70, 98, 100, 105, 106, 111, 112, 58, 69, 105, 106, + 78, 97, 109, 110, 80, 83, 111, 112, 109, 110, 116, 117, 68, 121, 105, + 123, 82, 122, 109, 124, 105, 123, 127, 128, 127, 128, 131, 132, 82, + 122, 127, 128, 90, 126, 129, 130, 109, 124, 131, 132, 129, 130, 133, + 134, 42, 46, 66, 71, 66, 71, 81, 84, 66, 71, 99, 101, 106, 107, 112, + 113, 66, 71, 106, 107, 99, 101, 112, 113, 81, 84, 112, 113, 112, 113, + 118, 119, 96, 122, 109, 124, 109, 124, 115, 125, 109, 124, 129, 130, + 131, 132, 133, 134, 109, 124, 131, 132, 129, 130, 133, 134, 115, 125, + 133, 134, 133, 134, 135, 136, 1, 5, 2, 6, 2, 6, 4, 7, 5, 12, 6, 13, + 12, 15, 13, 16, 5, 12, 12, 15, 6, 13, 13, 16, 12, 19, 15, 20, 15, 20, + 17, 21, 2, 12, 4, 13, 4, 13, 11, 14, 6, 15, 7, 16, 13, 17, 14, 18, 6, + 15, 13, 17, 7, 16, 14, 18, 13, 20, 16, 21, 16, 21, 18, 22, 5, 12, 6, + 13, 12, 15, 13, 16, 12, 35, 13, 36, 38, 40, 39, 41, 50, 51, 51, 52, + 51, 52, 52, 53, 51, 54, 52, 55, 56, 58, 57, 59, 12, 38, 13, 39, 35, + 40, 36, 41, 15, 40, 16, 41, 40, 48, 41, 49, 51, 56, 52, 57, 54, 58, + 55, 59, 52, 58, 53, 59, 58, 60, 59, 61, 5, 12, 12, 15, 6, 13, 13, 16, + 50, 51, 51, 52, 51, 52, 52, 53, 12, 35, 38, 40, 13, 36, 39, 41, 51, + 54, 56, 58, 52, 55, 57, 59, 12, 38, 35, 40, 13, 39, 36, 41, 51, 56, + 54, 58, 52, 57, 55, 59, 15, 40, 40, 48, 16, 41, 41, 49, 52, 58, 58, + 60, 53, 59, 59, 61, 12, 19, 15, 20, 15, 20, 17, 21, 51, 54, 52, 55, + 56, 58, 57, 59, 51, 54, 56, 58, 52, 55, 57, 59, 74, 77, 75, 78, 75, + 78, 76, 79, 38, 44, 40, 45, 40, 45, 42, 46, 56, 68, 58, 69, 65, 70, + 66, 71, 56, 68, 65, 70, 58, 69, 66, 71, 75, 82, 80, 83, 80, 83, 81, + 84, 2, 12, 4, 13, 4, 13, 11, 14, 12, 38, 13, 39, 35, 40, 36, 41, 12, + 38, 35, 40, 13, 39, 36, 41, 38, 44, 40, 45, 40, 45, 42, 46, 4, 35, + 11, 36, 11, 36, 34, 37, 13, 40, 14, 41, 36, 42, 37, 43, 13, 40, 36, + 42, 14, 41, 37, 43, 39, 45, 41, 46, 41, 46, 43, 47, 6, 15, 7, 16, 13, + 17, 14, 18, 15, 40, 16, 41, 40, 48, 41, 49, 51, 56, 54, 58, 52, 57, + 55, 59, 56, 68, 58, 69, 65, 70, 66, 71, 13, 40, 14, 41, 36, 42, 37, + 43, 17, 48, 18, 49, 42, 62, 43, 63, 52, 65, 55, 66, 55, 66, 64, 67, + 57, 70, 59, 71, 66, 72, 67, 73, 6, 15, 13, 17, 7, 16, 14, 18, 51, 56, + 52, 57, 54, 58, 55, 59, 15, 40, 40, 48, 16, 41, 41, 49, 56, 68, 65, + 70, 58, 69, 66, 71, 13, 40, 36, 42, 14, 41, 37, 43, 52, 65, 55, 66, + 55, 66, 64, 67, 17, 48, 42, 62, 18, 49, 43, 63, 57, 70, 66, 72, 59, + 71, 67, 73, 13, 20, 16, 21, 16, 21, 18, 22, 52, 58, 53, 59, 58, 60, + 59, 61, 52, 58, 58, 60, 53, 59, 59, 61, 75, 82, 80, 83, 80, 83, 81, + 84, 39, 45, 41, 46, 41, 46, 43, 47, 57, 70, 59, 71, 66, 72, 67, 73, + 57, 70, 66, 72, 59, 71, 67, 73, 76, 86, 81, 87, 81, 87, 85, 88, 5, + 50, 12, 51, 12, 51, 35, 54, 12, 51, 15, 52, 38, 56, 40, 58, 12, 51, + 38, 56, 15, 52, 40, 58, 35, 54, 40, 58, 40, 58, 48, 60, 6, 51, 13, + 52, 13, 52, 36, 55, 13, 52, 16, 53, 39, 57, 41, 59, 13, 52, 39, 57, + 16, 53, 41, 59, 36, 55, 41, 59, 41, 59, 49, 61, 12, 51, 15, 52, 38, + 56, 40, 58, 19, 54, 20, 55, 44, 68, 45, 69, 51, 74, 56, 75, 56, 75, + 65, 80, 54, 77, 58, 78, 68, 82, 70, 83, 15, 56, 17, 57, 40, 65, 42, + 66, 20, 58, 21, 59, 45, 70, 46, 71, 52, 75, 57, 76, 58, 80, 66, 81, + 55, 78, 59, 79, 69, 83, 71, 84, 12, 51, 38, 56, 15, 52, 40, 58, 51, + 74, 56, 75, 56, 75, 65, 80, 19, 54, 44, 68, 20, 55, 45, 69, 54, 77, + 68, 82, 58, 78, 70, 83, 15, 56, 40, 65, 17, 57, 42, 66, 52, 75, 58, + 80, 57, 76, 66, 81, 20, 58, 45, 70, 21, 59, 46, 71, 55, 78, 69, 83, + 59, 79, 71, 84, 35, 54, 40, 58, 40, 58, 48, 60, 54, 77, 58, 78, 68, + 82, 70, 83, 54, 77, 68, 82, 58, 78, 70, 83, 77, 89, 82, 90, 82, 90, + 86, 91, 40, 68, 48, 70, 48, 70, 62, 72, 58, 82, 60, 83, 70, 86, 72, + 87, 58, 82, 70, 86, 60, 83, 72, 87, 78, 90, 83, 91, 83, 91, 87, 92, + 6, 51, 13, 52, 13, 52, 36, 55, 15, 56, 17, 57, 40, 65, 42, 66, 15, + 56, 40, 65, 17, 57, 42, 66, 40, 68, 48, 70, 48, 70, 62, 72, 7, 54, + 14, 55, 14, 55, 37, 64, 16, 58, 18, 59, 41, 66, 43, 67, 16, 58, 41, + 66, 18, 59, 43, 67, 41, 69, 49, 71, 49, 71, 63, 73, 13, 52, 16, 53, + 39, 57, 41, 59, 20, 58, 21, 59, 45, 70, 46, 71, 52, 75, 58, 80, 57, + 76, 66, 81, 58, 82, 60, 83, 70, 86, 72, 87, 16, 58, 18, 59, 41, 66, + 43, 67, 21, 60, 22, 61, 46, 72, 47, 73, 53, 80, 59, 81, 59, 81, 67, + 85, 59, 83, 61, 84, 71, 87, 73, 88, 13, 52, 39, 57, 16, 53, 41, 59, + 52, 75, 57, 76, 58, 80, 66, 81, 20, 58, 45, 70, 21, 59, 46, 71, 58, + 82, 70, 86, 60, 83, 72, 87, 16, 58, 41, 66, 18, 59, 43, 67, 53, 80, + 59, 81, 59, 81, 67, 85, 21, 60, 46, 72, 22, 61, 47, 73, 59, 83, 71, + 87, 61, 84, 73, 88, 36, 55, 41, 59, 41, 59, 49, 61, 55, 78, 59, 79, + 69, 83, 71, 84, 55, 78, 69, 83, 59, 79, 71, 84, 78, 90, 83, 91, 83, + 91, 87, 92, 41, 69, 49, 71, 49, 71, 63, 73, 59, 83, 61, 84, 71, 87, + 73, 88, 59, 83, 71, 87, 61, 84, 73, 88, 79, 91, 84, 92, 84, 92, 88, + 93, 2, 6, 3, 7, 6, 8, 7, 9, 6, 13, 7, 14, 15, 17, 16, 18, 12, 15, 19, + 20, 15, 17, 20, 21, 15, 20, 20, 25, 26, 27, 27, 28, 6, 15, 7, 16, 13, + 17, 14, 18, 8, 17, 9, 18, 17, 23, 18, 24, 15, 26, 20, 27, 20, 27, 25, + 28, 17, 27, 21, 28, 27, 29, 28, 30, 6, 13, 7, 14, 15, 17, 16, 18, 13, + 36, 14, 37, 40, 42, 41, 43, 51, 52, 54, 55, 56, 57, 58, 59, 52, 55, + 55, 64, 65, 66, 66, 67, 15, 40, 16, 41, 40, 48, 41, 49, 17, 42, 18, + 43, 48, 62, 49, 63, 56, 65, 58, 66, 68, 70, 69, 71, 57, 66, 59, 67, + 70, 72, 71, 73, 12, 15, 19, 20, 15, 17, 20, 21, 51, 52, 54, 55, 56, + 57, 58, 59, 38, 40, 44, 45, 40, 42, 45, 46, 56, 58, 68, 69, 65, 66, + 70, 71, 51, 56, 54, 58, 52, 57, 55, 59, 74, 75, 77, 78, 75, 76, 78, + 79, 56, 65, 68, 70, 58, 66, 69, 71, 75, 80, 82, 83, 80, 81, 83, 84, + 15, 20, 20, 25, 26, 27, 27, 28, 52, 55, 55, 64, 65, 66, 66, 67, 56, + 58, 68, 69, 65, 66, 70, 71, 75, 78, 96, 97, 98, 99, 100, 101, 56, 68, + 58, 69, 65, 70, 66, 71, 75, 96, 78, 97, 98, 100, 99, 101, 104, 105, + 105, 106, 105, 106, 106, 107, 108, 109, 109, 110, 111, 112, 112, 113, + 6, 15, 7, 16, 13, 17, 14, 18, 15, 40, 16, 41, 40, 48, 41, 49, 51, 56, + 54, 58, 52, 57, 55, 59, 56, 68, 58, 69, 65, 70, 66, 71, 13, 40, 14, + 41, 36, 42, 37, 43, 17, 48, 18, 49, 42, 62, 43, 63, 52, 65, 55, 66, + 55, 66, 64, 67, 57, 70, 59, 71, 66, 72, 67, 73, 8, 17, 9, 18, 17, 23, + 18, 24, 17, 42, 18, 43, 48, 62, 49, 63, 74, 75, 77, 78, 75, 76, 78, + 79, 75, 96, 78, 97, 98, 100, 99, 101, 17, 48, 18, 49, 42, 62, 43, 63, + 23, 62, 24, 63, 62, 94, 63, 95, 75, 98, 78, 99, 96, 100, 97, 101, 76, + 100, 79, 101, 100, 102, 101, 103, 15, 26, 20, 27, 20, 27, 25, 28, 56, + 65, 58, 66, 68, 70, 69, 71, 56, 65, 68, 70, 58, 66, 69, 71, 104, 105, + 105, 106, 105, 106, 106, 107, 52, 65, 55, 66, 55, 66, 64, 67, 75, 98, + 78, 99, 96, 100, 97, 101, 75, 98, 96, 100, 78, 99, 97, 101, 108, 111, + 109, 112, 109, 112, 110, 113, 17, 27, 21, 28, 27, 29, 28, 30, 57, 66, + 59, 67, 70, 72, 71, 73, 75, 80, 82, 83, 80, 81, 83, 84, 108, 109, + 109, 110, 111, 112, 112, 113, 57, 70, 59, 71, 66, 72, 67, 73, 76, + 100, 79, 101, 100, 102, 101, 103, 108, 111, 109, 112, 109, 112, 110, + 113, 114, 116, 115, 117, 116, 118, 117, 119, 12, 51, 19, 54, 51, 74, + 54, 77, 15, 52, 20, 55, 56, 75, 58, 78, 38, 56, 44, 68, 56, 75, 68, + 82, 40, 58, 45, 69, 65, 80, 70, 83, 15, 56, 20, 58, 52, 75, 55, 78, + 17, 57, 21, 59, 57, 76, 59, 79, 40, 65, 45, 70, 58, 80, 69, 83, 42, + 66, 46, 71, 66, 81, 71, 84, 15, 52, 20, 55, 56, 75, 58, 78, 20, 55, + 25, 64, 68, 96, 69, 97, 56, 75, 68, 96, 104, 108, 105, 109, 58, 78, + 69, 97, 105, 109, 106, 110, 26, 65, 27, 66, 65, 98, 66, 99, 27, 66, + 28, 67, 70, 100, 71, 101, 65, 98, 70, 100, 105, 111, 106, 112, 66, + 99, 71, 101, 106, 112, 107, 113, 38, 56, 44, 68, 56, 75, 68, 82, 56, + 75, 68, 96, 104, 108, 105, 109, 44, 68, 120, 121, 68, 96, 121, 122, + 68, 82, 121, 122, 105, 109, 123, 124, 56, 104, 68, 105, 75, 108, 96, + 109, 75, 108, 82, 109, 108, 114, 109, 115, 68, 105, 121, 123, 82, + 109, 122, 124, 96, 109, 122, 124, 109, 115, 124, 125, 40, 58, 45, 69, + 65, 80, 70, 83, 58, 78, 69, 97, 105, 109, 106, 110, 68, 82, 121, 122, + 105, 109, 123, 124, 82, 90, 122, 126, 127, 129, 128, 130, 65, 105, + 70, 106, 98, 111, 100, 112, 80, 109, 83, 110, 111, 116, 112, 117, + 105, 127, 123, 128, 127, 131, 128, 132, 109, 129, 124, 130, 131, 133, + 132, 134, 15, 56, 20, 58, 52, 75, 55, 78, 26, 65, 27, 66, 65, 98, 66, + 99, 56, 104, 68, 105, 75, 108, 96, 109, 65, 105, 70, 106, 98, 111, + 100, 112, 20, 68, 25, 69, 55, 96, 64, 97, 27, 70, 28, 71, 66, 100, + 67, 101, 58, 105, 69, 106, 78, 109, 97, 110, 66, 106, 71, 107, 99, + 112, 101, 113, 17, 57, 21, 59, 57, 76, 59, 79, 27, 66, 28, 67, 70, + 100, 71, 101, 75, 108, 82, 109, 108, 114, 109, 115, 80, 109, 83, 110, + 111, 116, 112, 117, 27, 70, 28, 71, 66, 100, 67, 101, 29, 72, 30, 73, + 72, 102, 73, 103, 80, 111, 83, 112, 109, 116, 110, 117, 81, 112, 84, + 113, 112, 118, 113, 119, 40, 65, 45, 70, 58, 80, 69, 83, 65, 98, 70, + 100, 105, 111, 106, 112, 68, 105, 121, 123, 82, 109, 122, 124, 105, + 127, 123, 128, 127, 131, 128, 132, 58, 105, 69, 106, 78, 109, 97, + 110, 80, 111, 83, 112, 109, 116, 110, 117, 82, 127, 122, 128, 90, + 129, 126, 130, 109, 131, 124, 132, 129, 133, 130, 134, 42, 66, 46, + 71, 66, 81, 71, 84, 66, 99, 71, 101, 106, 112, 107, 113, 96, 109, + 122, 124, 109, 115, 124, 125, 109, 129, 124, 130, 131, 133, 132, 134, + 66, 106, 71, 107, 99, 112, 101, 113, 81, 112, 84, 113, 112, 118, 113, + 119, 109, 131, 124, 132, 129, 133, 130, 134, 115, 133, 125, 134, 133, + 135, 134, 136, 2, 6, 6, 8, 3, 7, 7, 9, 12, 15, 15, 17, 19, 20, 20, + 21, 6, 13, 15, 17, 7, 14, 16, 18, 15, 20, 26, 27, 20, 25, 27, 28, 6, + 15, 13, 17, 7, 16, 14, 18, 15, 26, 20, 27, 20, 27, 25, 28, 8, 17, 17, + 23, 9, 18, 18, 24, 17, 27, 27, 29, 21, 28, 28, 30, 12, 15, 15, 17, + 19, 20, 20, 21, 38, 40, 40, 42, 44, 45, 45, 46, 51, 52, 56, 57, 54, + 55, 58, 59, 56, 58, 65, 66, 68, 69, 70, 71, 51, 56, 52, 57, 54, 58, + 55, 59, 56, 65, 58, 66, 68, 70, 69, 71, 74, 75, 75, 76, 77, 78, 78, + 79, 75, 80, 80, 81, 82, 83, 83, 84, 6, 13, 15, 17, 7, 14, 16, 18, 51, + 52, 56, 57, 54, 55, 58, 59, 13, 36, 40, 42, 14, 37, 41, 43, 52, 55, + 65, 66, 55, 64, 66, 67, 15, 40, 40, 48, 16, 41, 41, 49, 56, 65, 68, + 70, 58, 66, 69, 71, 17, 42, 48, 62, 18, 43, 49, 63, 57, 66, 70, 72, + 59, 67, 71, 73, 15, 20, 26, 27, 20, 25, 27, 28, 56, 58, 65, 66, 68, + 69, 70, 71, 52, 55, 65, 66, 55, 64, 66, 67, 75, 78, 98, 99, 96, 97, + 100, 101, 56, 68, 65, 70, 58, 69, 66, 71, 104, 105, 105, 106, 105, + 106, 106, 107, 75, 96, 98, 100, 78, 97, 99, 101, 108, 109, 111, 112, + 109, 110, 112, 113, 6, 15, 13, 17, 7, 16, 14, 18, 51, 56, 52, 57, 54, + 58, 55, 59, 15, 40, 40, 48, 16, 41, 41, 49, 56, 68, 65, 70, 58, 69, + 66, 71, 13, 40, 36, 42, 14, 41, 37, 43, 52, 65, 55, 66, 55, 66, 64, + 67, 17, 48, 42, 62, 18, 49, 43, 63, 57, 70, 66, 72, 59, 71, 67, 73, + 15, 26, 20, 27, 20, 27, 25, 28, 56, 65, 58, 66, 68, 70, 69, 71, 56, + 65, 68, 70, 58, 66, 69, 71, 104, 105, 105, 106, 105, 106, 106, 107, + 52, 65, 55, 66, 55, 66, 64, 67, 75, 98, 78, 99, 96, 100, 97, 101, 75, + 98, 96, 100, 78, 99, 97, 101, 108, 111, 109, 112, 109, 112, 110, 113, + 8, 17, 17, 23, 9, 18, 18, 24, 74, 75, 75, 76, 77, 78, 78, 79, 17, 42, + 48, 62, 18, 43, 49, 63, 75, 96, 98, 100, 78, 97, 99, 101, 17, 48, 42, + 62, 18, 49, 43, 63, 75, 98, 96, 100, 78, 99, 97, 101, 23, 62, 62, 94, + 24, 63, 63, 95, 76, 100, 100, 102, 79, 101, 101, 103, 17, 27, 27, 29, + 21, 28, 28, 30, 75, 80, 80, 81, 82, 83, 83, 84, 57, 66, 70, 72, 59, + 67, 71, 73, 108, 109, 111, 112, 109, 110, 112, 113, 57, 70, 66, 72, + 59, 71, 67, 73, 108, 111, 109, 112, 109, 112, 110, 113, 76, 100, 100, + 102, 79, 101, 101, 103, 114, 116, 116, 118, 115, 117, 117, 119, 12, + 51, 51, 74, 19, 54, 54, 77, 38, 56, 56, 75, 44, 68, 68, 82, 15, 52, + 56, 75, 20, 55, 58, 78, 40, 58, 65, 80, 45, 69, 70, 83, 15, 56, 52, + 75, 20, 58, 55, 78, 40, 65, 58, 80, 45, 70, 69, 83, 17, 57, 57, 76, + 21, 59, 59, 79, 42, 66, 66, 81, 46, 71, 71, 84, 38, 56, 56, 75, 44, + 68, 68, 82, 44, 68, 68, 96, 120, 121, 121, 122, 56, 75, 104, 108, 68, + 96, 105, 109, 68, 82, 105, 109, 121, 122, 123, 124, 56, 104, 75, 108, + 68, 105, 96, 109, 68, 105, 82, 109, 121, 123, 122, 124, 75, 108, 108, + 114, 82, 109, 109, 115, 96, 109, 109, 115, 122, 124, 124, 125, 15, + 52, 56, 75, 20, 55, 58, 78, 56, 75, 104, 108, 68, 96, 105, 109, 20, + 55, 68, 96, 25, 64, 69, 97, 58, 78, 105, 109, 69, 97, 106, 110, 26, + 65, 65, 98, 27, 66, 66, 99, 65, 98, 105, 111, 70, 100, 106, 112, 27, + 66, 70, 100, 28, 67, 71, 101, 66, 99, 106, 112, 71, 101, 107, 113, + 40, 58, 65, 80, 45, 69, 70, 83, 68, 82, 105, 109, 121, 122, 123, 124, + 58, 78, 105, 109, 69, 97, 106, 110, 82, 90, 127, 129, 122, 126, 128, + 130, 65, 105, 98, 111, 70, 106, 100, 112, 105, 127, 127, 131, 123, + 128, 128, 132, 80, 109, 111, 116, 83, 110, 112, 117, 109, 129, 131, + 133, 124, 130, 132, 134, 15, 56, 52, 75, 20, 58, 55, 78, 56, 104, 75, + 108, 68, 105, 96, 109, 26, 65, 65, 98, 27, 66, 66, 99, 65, 105, 98, + 111, 70, 106, 100, 112, 20, 68, 55, 96, 25, 69, 64, 97, 58, 105, 78, + 109, 69, 106, 97, 110, 27, 70, 66, 100, 28, 71, 67, 101, 66, 106, 99, + 112, 71, 107, 101, 113, 40, 65, 58, 80, 45, 70, 69, 83, 68, 105, 82, + 109, 121, 123, 122, 124, 65, 98, 105, 111, 70, 100, 106, 112, 105, + 127, 127, 131, 123, 128, 128, 132, 58, 105, 78, 109, 69, 106, 97, + 110, 82, 127, 90, 129, 122, 128, 126, 130, 80, 111, 109, 116, 83, + 112, 110, 117, 109, 131, 129, 133, 124, 132, 130, 134, 17, 57, 57, + 76, 21, 59, 59, 79, 75, 108, 108, 114, 82, 109, 109, 115, 27, 66, 70, + 100, 28, 67, 71, 101, 80, 109, 111, 116, 83, 110, 112, 117, 27, 70, + 66, 100, 28, 71, 67, 101, 80, 111, 109, 116, 83, 112, 110, 117, 29, + 72, 72, 102, 30, 73, 73, 103, 81, 112, 112, 118, 84, 113, 113, 119, + 42, 66, 66, 81, 46, 71, 71, 84, 96, 109, 109, 115, 122, 124, 124, + 125, 66, 99, 106, 112, 71, 101, 107, 113, 109, 129, 131, 133, 124, + 130, 132, 134, 66, 106, 99, 112, 71, 107, 101, 113, 109, 131, 129, + 133, 124, 132, 130, 134, 81, 112, 112, 118, 84, 113, 113, 119, 115, + 133, 133, 135, 125, 134, 134, 136, 4, 7, 7, 9, 7, 9, 9, 10, 13, 16, + 16, 18, 20, 21, 21, 22, 13, 16, 20, 21, 16, 18, 21, 22, 17, 21, 27, + 28, 27, 28, 29, 30, 13, 20, 16, 21, 16, 21, 18, 22, 17, 27, 21, 28, + 27, 29, 28, 30, 17, 27, 27, 29, 21, 28, 28, 30, 23, 29, 29, 31, 29, + 31, 31, 32, 13, 16, 16, 18, 20, 21, 21, 22, 39, 41, 41, 43, 45, 46, + 46, 47, 52, 53, 58, 59, 58, 59, 60, 61, 57, 59, 66, 67, 70, 71, 72, + 73, 52, 58, 53, 59, 58, 60, 59, 61, 57, 66, 59, 67, 70, 72, 71, 73, + 75, 80, 80, 81, 82, 83, 83, 84, 76, 81, 81, 85, 86, 87, 87, 88, 13, + 16, 20, 21, 16, 18, 21, 22, 52, 53, 58, 59, 58, 59, 60, 61, 39, 41, + 45, 46, 41, 43, 46, 47, 57, 59, 70, 71, 66, 67, 72, 73, 52, 58, 58, + 60, 53, 59, 59, 61, 75, 80, 82, 83, 80, 81, 83, 84, 57, 66, 70, 72, + 59, 67, 71, 73, 76, 81, 86, 87, 81, 85, 87, 88, 17, 21, 27, 28, 27, + 28, 29, 30, 57, 59, 66, 67, 70, 71, 72, 73, 57, 59, 70, 71, 66, 67, + 72, 73, 76, 79, 100, 101, 100, 101, 102, 103, 75, 82, 80, 83, 80, 83, + 81, 84, 108, 109, 109, 110, 111, 112, 112, 113, 108, 109, 111, 112, + 109, 110, 112, 113, 114, 115, 116, 117, 116, 117, 118, 119, 13, 20, + 16, 21, 16, 21, 18, 22, 52, 58, 53, 59, 58, 60, 59, 61, 52, 58, 58, + 60, 53, 59, 59, 61, 75, 82, 80, 83, 80, 83, 81, 84, 39, 45, 41, 46, + 41, 46, 43, 47, 57, 70, 59, 71, 66, 72, 67, 73, 57, 70, 66, 72, 59, + 71, 67, 73, 76, 86, 81, 87, 81, 87, 85, 88, 17, 27, 21, 28, 27, 29, + 28, 30, 57, 66, 59, 67, 70, 72, 71, 73, 75, 80, 82, 83, 80, 81, 83, + 84, 108, 109, 109, 110, 111, 112, 112, 113, 57, 70, 59, 71, 66, 72, + 67, 73, 76, 100, 79, 101, 100, 102, 101, 103, 108, 111, 109, 112, + 109, 112, 110, 113, 114, 116, 115, 117, 116, 118, 117, 119, 17, 27, + 27, 29, 21, 28, 28, 30, 75, 80, 80, 81, 82, 83, 83, 84, 57, 66, 70, + 72, 59, 67, 71, 73, 108, 109, 111, 112, 109, 110, 112, 113, 57, 70, + 66, 72, 59, 71, 67, 73, 108, 111, 109, 112, 109, 112, 110, 113, 76, + 100, 100, 102, 79, 101, 101, 103, 114, 116, 116, 118, 115, 117, 117, + 119, 23, 29, 29, 31, 29, 31, 31, 32, 76, 81, 81, 85, 86, 87, 87, 88, + 76, 81, 86, 87, 81, 85, 87, 88, 114, 115, 116, 117, 116, 117, 118, + 119, 76, 86, 81, 87, 81, 87, 85, 88, 114, 116, 115, 117, 116, 118, + 117, 119, 114, 116, 116, 118, 115, 117, 117, 119, 137, 138, 138, 139, + 138, 139, 139, 140, 35, 54, 54, 77, 54, 77, 77, 89, 40, 58, 58, 78, + 68, 82, 82, 90, 40, 58, 68, 82, 58, 78, 82, 90, 48, 60, 70, 83, 70, + 83, 86, 91, 40, 68, 58, 82, 58, 82, 78, 90, 48, 70, 60, 83, 70, 86, + 83, 91, 48, 70, 70, 86, 60, 83, 83, 91, 62, 72, 72, 87, 72, 87, 87, + 92, 40, 58, 58, 78, 68, 82, 82, 90, 45, 69, 69, 97, 121, 122, 122, + 126, 65, 80, 105, 109, 105, 109, 127, 129, 70, 83, 106, 110, 123, + 124, 128, 130, 65, 105, 80, 109, 105, 127, 109, 129, 70, 106, 83, + 110, 123, 128, 124, 130, 98, 111, 111, 116, 127, 131, 131, 133, 100, + 112, 112, 117, 128, 132, 132, 134, 40, 58, 68, 82, 58, 78, 82, 90, + 65, 80, 105, 109, 105, 109, 127, 129, 45, 69, 121, 122, 69, 97, 122, + 126, 70, 83, 123, 124, 106, 110, 128, 130, 65, 105, 105, 127, 80, + 109, 109, 129, 98, 111, 127, 131, 111, 116, 131, 133, 70, 106, 123, + 128, 83, 110, 124, 130, 100, 112, 128, 132, 112, 117, 132, 134, 48, + 60, 70, 83, 70, 83, 86, 91, 70, 83, 106, 110, 123, 124, 128, 130, 70, + 83, 123, 124, 106, 110, 128, 130, 86, 91, 128, 130, 128, 130, 141, + 142, 98, 127, 111, 131, 111, 131, 116, 133, 111, 131, 131, 143, 144, + 145, 145, 146, 111, 131, 144, 145, 131, 143, 145, 146, 116, 133, 145, + 146, 145, 146, 147, 148, 40, 68, 58, 82, 58, 82, 78, 90, 65, 105, 80, + 109, 105, 127, 109, 129, 65, 105, 105, 127, 80, 109, 109, 129, 98, + 127, 111, 131, 111, 131, 116, 133, 45, 121, 69, 122, 69, 122, 97, + 126, 70, 123, 83, 124, 106, 128, 110, 130, 70, 123, 106, 128, 83, + 124, 110, 130, 100, 128, 112, 132, 112, 132, 117, 134, 48, 70, 60, + 83, 70, 86, 83, 91, 70, 106, 83, 110, 123, 128, 124, 130, 98, 111, + 127, 131, 111, 116, 131, 133, 111, 131, 131, 143, 144, 145, 145, 146, + 70, 123, 83, 124, 106, 128, 110, 130, 86, 128, 91, 130, 128, 141, + 130, 142, 111, 144, 131, 145, 131, 145, 143, 146, 116, 145, 133, 146, + 145, 147, 146, 148, 48, 70, 70, 86, 60, 83, 83, 91, 98, 111, 111, + 116, 127, 131, 131, 133, 70, 106, 123, 128, 83, 110, 124, 130, 111, + 131, 144, 145, 131, 143, 145, 146, 70, 123, 106, 128, 83, 124, 110, + 130, 111, 144, 131, 145, 131, 145, 143, 146, 86, 128, 128, 141, 91, + 130, 130, 142, 116, 145, 145, 147, 133, 146, 146, 148, 62, 72, 72, + 87, 72, 87, 87, 92, 100, 112, 112, 117, 128, 132, 132, 134, 100, 112, + 128, 132, 112, 117, 132, 134, 116, 133, 145, 146, 145, 146, 147, 148, + 100, 128, 112, 132, 112, 132, 117, 134, 116, 145, 133, 146, 145, 147, + 146, 148, 116, 145, 145, 147, 133, 146, 146, 148, 138, 149, 149, 150, + 149, 150, 150, 151, 1, 5, 5, 12, 5, 12, 12, 19, 2, 6, 6, 13, 12, 15, + 15, 20, 2, 6, 12, 15, 6, 13, 15, 20, 4, 7, 13, 16, 13, 16, 17, 21, 2, + 12, 6, 15, 6, 15, 13, 20, 4, 13, 7, 16, 13, 17, 16, 21, 4, 13, 13, + 17, 7, 16, 16, 21, 11, 14, 14, 18, 14, 18, 18, 22, 5, 12, 12, 35, 50, + 51, 51, 54, 6, 13, 13, 36, 51, 52, 52, 55, 12, 15, 38, 40, 51, 52, + 56, 58, 13, 16, 39, 41, 52, 53, 57, 59, 12, 38, 15, 40, 51, 56, 52, + 58, 13, 39, 16, 41, 52, 57, 53, 59, 35, 40, 40, 48, 54, 58, 58, 60, + 36, 41, 41, 49, 55, 59, 59, 61, 5, 12, 50, 51, 12, 35, 51, 54, 12, + 15, 51, 52, 38, 40, 56, 58, 6, 13, 51, 52, 13, 36, 52, 55, 13, 16, + 52, 53, 39, 41, 57, 59, 12, 38, 51, 56, 15, 40, 52, 58, 35, 40, 54, + 58, 40, 48, 58, 60, 13, 39, 52, 57, 16, 41, 53, 59, 36, 41, 55, 59, + 41, 49, 59, 61, 12, 19, 51, 54, 51, 54, 74, 77, 15, 20, 52, 55, 56, + 58, 75, 78, 15, 20, 56, 58, 52, 55, 75, 78, 17, 21, 57, 59, 57, 59, + 76, 79, 38, 44, 56, 68, 56, 68, 75, 82, 40, 45, 58, 69, 65, 70, 80, + 83, 40, 45, 65, 70, 58, 69, 80, 83, 42, 46, 66, 71, 66, 71, 81, 84, + 5, 50, 12, 51, 12, 51, 35, 54, 12, 51, 15, 52, 38, 56, 40, 58, 12, + 51, 38, 56, 15, 52, 40, 58, 35, 54, 40, 58, 40, 58, 48, 60, 6, 51, + 13, 52, 13, 52, 36, 55, 13, 52, 16, 53, 39, 57, 41, 59, 13, 52, 39, + 57, 16, 53, 41, 59, 36, 55, 41, 59, 41, 59, 49, 61, 12, 51, 19, 54, + 51, 74, 54, 77, 15, 52, 20, 55, 56, 75, 58, 78, 38, 56, 44, 68, 56, + 75, 68, 82, 40, 58, 45, 69, 65, 80, 70, 83, 15, 56, 20, 58, 52, 75, + 55, 78, 17, 57, 21, 59, 57, 76, 59, 79, 40, 65, 45, 70, 58, 80, 69, + 83, 42, 66, 46, 71, 66, 81, 71, 84, 12, 51, 51, 74, 19, 54, 54, 77, + 38, 56, 56, 75, 44, 68, 68, 82, 15, 52, 56, 75, 20, 55, 58, 78, 40, + 58, 65, 80, 45, 69, 70, 83, 15, 56, 52, 75, 20, 58, 55, 78, 40, 65, + 58, 80, 45, 70, 69, 83, 17, 57, 57, 76, 21, 59, 59, 79, 42, 66, 66, + 81, 46, 71, 71, 84, 35, 54, 54, 77, 54, 77, 77, 89, 40, 58, 58, 78, + 68, 82, 82, 90, 40, 58, 68, 82, 58, 78, 82, 90, 48, 60, 70, 83, 70, + 83, 86, 91, 40, 68, 58, 82, 58, 82, 78, 90, 48, 70, 60, 83, 70, 86, + 83, 91, 48, 70, 70, 86, 60, 83, 83, 91, 62, 72, 72, 87, 72, 87, 87, + 92, 2, 12, 12, 38, 12, 38, 38, 44, 4, 13, 13, 39, 35, 40, 40, 45, 4, + 13, 35, 40, 13, 39, 40, 45, 11, 14, 36, 41, 36, 41, 42, 46, 4, 35, + 13, 40, 13, 40, 39, 45, 11, 36, 14, 41, 36, 42, 41, 46, 11, 36, 36, + 42, 14, 41, 41, 46, 34, 37, 37, 43, 37, 43, 43, 47, 6, 15, 15, 40, + 51, 56, 56, 68, 7, 16, 16, 41, 54, 58, 58, 69, 13, 17, 40, 48, 52, + 57, 65, 70, 14, 18, 41, 49, 55, 59, 66, 71, 13, 40, 17, 48, 52, 65, + 57, 70, 14, 41, 18, 49, 55, 66, 59, 71, 36, 42, 42, 62, 55, 66, 66, + 72, 37, 43, 43, 63, 64, 67, 67, 73, 6, 15, 51, 56, 15, 40, 56, 68, + 13, 17, 52, 57, 40, 48, 65, 70, 7, 16, 54, 58, 16, 41, 58, 69, 14, + 18, 55, 59, 41, 49, 66, 71, 13, 40, 52, 65, 17, 48, 57, 70, 36, 42, + 55, 66, 42, 62, 66, 72, 14, 41, 55, 66, 18, 49, 59, 71, 37, 43, 64, + 67, 43, 63, 67, 73, 13, 20, 52, 58, 52, 58, 75, 82, 16, 21, 53, 59, + 58, 60, 80, 83, 16, 21, 58, 60, 53, 59, 80, 83, 18, 22, 59, 61, 59, + 61, 81, 84, 39, 45, 57, 70, 57, 70, 76, 86, 41, 46, 59, 71, 66, 72, + 81, 87, 41, 46, 66, 72, 59, 71, 81, 87, 43, 47, 67, 73, 67, 73, 85, + 88, 6, 51, 15, 56, 15, 56, 40, 68, 13, 52, 17, 57, 40, 65, 48, 70, + 13, 52, 40, 65, 17, 57, 48, 70, 36, 55, 42, 66, 42, 66, 62, 72, 7, + 54, 16, 58, 16, 58, 41, 69, 14, 55, 18, 59, 41, 66, 49, 71, 14, 55, + 41, 66, 18, 59, 49, 71, 37, 64, 43, 67, 43, 67, 63, 73, 13, 52, 20, + 58, 52, 75, 58, 82, 16, 53, 21, 59, 58, 80, 60, 83, 39, 57, 45, 70, + 57, 76, 70, 86, 41, 59, 46, 71, 66, 81, 72, 87, 16, 58, 21, 60, 53, + 80, 59, 83, 18, 59, 22, 61, 59, 81, 61, 84, 41, 66, 46, 72, 59, 81, + 71, 87, 43, 67, 47, 73, 67, 85, 73, 88, 13, 52, 52, 75, 20, 58, 58, + 82, 39, 57, 57, 76, 45, 70, 70, 86, 16, 53, 58, 80, 21, 59, 60, 83, + 41, 59, 66, 81, 46, 71, 72, 87, 16, 58, 53, 80, 21, 60, 59, 83, 41, + 66, 59, 81, 46, 72, 71, 87, 18, 59, 59, 81, 22, 61, 61, 84, 43, 67, + 67, 85, 47, 73, 73, 88, 36, 55, 55, 78, 55, 78, 78, 90, 41, 59, 59, + 79, 69, 83, 83, 91, 41, 59, 69, 83, 59, 79, 83, 91, 49, 61, 71, 84, + 71, 84, 87, 92, 41, 69, 59, 83, 59, 83, 79, 91, 49, 71, 61, 84, 71, + 87, 84, 92, 49, 71, 71, 87, 61, 84, 84, 92, 63, 73, 73, 88, 73, 88, + 88, 93, 2, 6, 6, 13, 12, 15, 15, 20, 3, 7, 7, 14, 19, 20, 20, 25, 6, + 8, 15, 17, 15, 17, 26, 27, 7, 9, 16, 18, 20, 21, 27, 28, 6, 15, 8, + 17, 15, 26, 17, 27, 7, 16, 9, 18, 20, 27, 21, 28, 13, 17, 17, 23, 20, + 27, 27, 29, 14, 18, 18, 24, 25, 28, 28, 30, 6, 13, 13, 36, 51, 52, + 52, 55, 7, 14, 14, 37, 54, 55, 55, 64, 15, 17, 40, 42, 56, 57, 65, + 66, 16, 18, 41, 43, 58, 59, 66, 67, 15, 40, 17, 42, 56, 65, 57, 66, + 16, 41, 18, 43, 58, 66, 59, 67, 40, 48, 48, 62, 68, 70, 70, 72, 41, + 49, 49, 63, 69, 71, 71, 73, 12, 15, 51, 52, 38, 40, 56, 58, 19, 20, + 54, 55, 44, 45, 68, 69, 15, 17, 56, 57, 40, 42, 65, 66, 20, 21, 58, + 59, 45, 46, 70, 71, 51, 56, 74, 75, 56, 65, 75, 80, 54, 58, 77, 78, + 68, 70, 82, 83, 52, 57, 75, 76, 58, 66, 80, 81, 55, 59, 78, 79, 69, + 71, 83, 84, 15, 20, 52, 55, 56, 58, 75, 78, 20, 25, 55, 64, 68, 69, + 96, 97, 26, 27, 65, 66, 65, 66, 98, 99, 27, 28, 66, 67, 70, 71, 100, + 101, 56, 68, 75, 96, 104, 105, 108, 109, 58, 69, 78, 97, 105, 106, + 109, 110, 65, 70, 98, 100, 105, 106, 111, 112, 66, 71, 99, 101, 106, + 107, 112, 113, 12, 51, 15, 52, 38, 56, 40, 58, 19, 54, 20, 55, 44, + 68, 45, 69, 51, 74, 56, 75, 56, 75, 65, 80, 54, 77, 58, 78, 68, 82, + 70, 83, 15, 56, 17, 57, 40, 65, 42, 66, 20, 58, 21, 59, 45, 70, 46, + 71, 52, 75, 57, 76, 58, 80, 66, 81, 55, 78, 59, 79, 69, 83, 71, 84, + 15, 52, 20, 55, 56, 75, 58, 78, 20, 55, 25, 64, 68, 96, 69, 97, 56, + 75, 68, 96, 104, 108, 105, 109, 58, 78, 69, 97, 105, 109, 106, 110, + 26, 65, 27, 66, 65, 98, 66, 99, 27, 66, 28, 67, 70, 100, 71, 101, 65, + 98, 70, 100, 105, 111, 106, 112, 66, 99, 71, 101, 106, 112, 107, 113, + 38, 56, 56, 75, 44, 68, 68, 82, 44, 68, 68, 96, 120, 121, 121, 122, + 56, 75, 104, 108, 68, 96, 105, 109, 68, 82, 105, 109, 121, 122, 123, + 124, 56, 104, 75, 108, 68, 105, 96, 109, 68, 105, 82, 109, 121, 123, + 122, 124, 75, 108, 108, 114, 82, 109, 109, 115, 96, 109, 109, 115, + 122, 124, 124, 125, 40, 58, 58, 78, 68, 82, 82, 90, 45, 69, 69, 97, + 121, 122, 122, 126, 65, 80, 105, 109, 105, 109, 127, 129, 70, 83, + 106, 110, 123, 124, 128, 130, 65, 105, 80, 109, 105, 127, 109, 129, + 70, 106, 83, 110, 123, 128, 124, 130, 98, 111, 111, 116, 127, 131, + 131, 133, 100, 112, 112, 117, 128, 132, 132, 134, 6, 15, 15, 40, 51, + 56, 56, 68, 7, 16, 16, 41, 54, 58, 58, 69, 13, 17, 40, 48, 52, 57, + 65, 70, 14, 18, 41, 49, 55, 59, 66, 71, 13, 40, 17, 48, 52, 65, 57, + 70, 14, 41, 18, 49, 55, 66, 59, 71, 36, 42, 42, 62, 55, 66, 66, 72, + 37, 43, 43, 63, 64, 67, 67, 73, 8, 17, 17, 42, 74, 75, 75, 96, 9, 18, + 18, 43, 77, 78, 78, 97, 17, 23, 48, 62, 75, 76, 98, 100, 18, 24, 49, + 63, 78, 79, 99, 101, 17, 48, 23, 62, 75, 98, 76, 100, 18, 49, 24, 63, + 78, 99, 79, 101, 42, 62, 62, 94, 96, 100, 100, 102, 43, 63, 63, 95, + 97, 101, 101, 103, 15, 26, 56, 65, 56, 65, 104, 105, 20, 27, 58, 66, + 68, 70, 105, 106, 20, 27, 68, 70, 58, 66, 105, 106, 25, 28, 69, 71, + 69, 71, 106, 107, 52, 65, 75, 98, 75, 98, 108, 111, 55, 66, 78, 99, + 96, 100, 109, 112, 55, 66, 96, 100, 78, 99, 109, 112, 64, 67, 97, + 101, 97, 101, 110, 113, 17, 27, 57, 66, 75, 80, 108, 109, 21, 28, 59, + 67, 82, 83, 109, 110, 27, 29, 70, 72, 80, 81, 111, 112, 28, 30, 71, + 73, 83, 84, 112, 113, 57, 70, 76, 100, 108, 111, 114, 116, 59, 71, + 79, 101, 109, 112, 115, 117, 66, 72, 100, 102, 109, 112, 116, 118, + 67, 73, 101, 103, 110, 113, 117, 119, 15, 56, 26, 65, 56, 104, 65, + 105, 20, 58, 27, 66, 68, 105, 70, 106, 52, 75, 65, 98, 75, 108, 98, + 111, 55, 78, 66, 99, 96, 109, 100, 112, 20, 68, 27, 70, 58, 105, 66, + 106, 25, 69, 28, 71, 69, 106, 71, 107, 55, 96, 66, 100, 78, 109, 99, + 112, 64, 97, 67, 101, 97, 110, 101, 113, 17, 57, 27, 66, 75, 108, 80, + 109, 21, 59, 28, 67, 82, 109, 83, 110, 57, 76, 70, 100, 108, 114, + 111, 116, 59, 79, 71, 101, 109, 115, 112, 117, 27, 70, 29, 72, 80, + 111, 81, 112, 28, 71, 30, 73, 83, 112, 84, 113, 66, 100, 72, 102, + 109, 116, 112, 118, 67, 101, 73, 103, 110, 117, 113, 119, 40, 65, 65, + 98, 68, 105, 105, 127, 45, 70, 70, 100, 121, 123, 123, 128, 58, 80, + 105, 111, 82, 109, 127, 131, 69, 83, 106, 112, 122, 124, 128, 132, + 58, 105, 80, 111, 82, 127, 109, 131, 69, 106, 83, 112, 122, 128, 124, + 132, 78, 109, 109, 116, 90, 129, 129, 133, 97, 110, 110, 117, 126, + 130, 130, 134, 42, 66, 66, 99, 96, 109, 109, 129, 46, 71, 71, 101, + 122, 124, 124, 130, 66, 81, 106, 112, 109, 115, 131, 133, 71, 84, + 107, 113, 124, 125, 132, 134, 66, 106, 81, 112, 109, 131, 115, 133, + 71, 107, 84, 113, 124, 132, 125, 134, 99, 112, 112, 118, 129, 133, + 133, 135, 101, 113, 113, 119, 130, 134, 134, 136, 2, 6, 12, 15, 6, + 13, 15, 20, 6, 8, 15, 17, 15, 17, 26, 27, 3, 7, 19, 20, 7, 14, 20, + 25, 7, 9, 20, 21, 16, 18, 27, 28, 6, 15, 15, 26, 8, 17, 17, 27, 13, + 17, 20, 27, 17, 23, 27, 29, 7, 16, 20, 27, 9, 18, 21, 28, 14, 18, 25, + 28, 18, 24, 28, 30, 12, 15, 38, 40, 51, 52, 56, 58, 15, 17, 40, 42, + 56, 57, 65, 66, 19, 20, 44, 45, 54, 55, 68, 69, 20, 21, 45, 46, 58, + 59, 70, 71, 51, 56, 56, 65, 74, 75, 75, 80, 52, 57, 58, 66, 75, 76, + 80, 81, 54, 58, 68, 70, 77, 78, 82, 83, 55, 59, 69, 71, 78, 79, 83, + 84, 6, 13, 51, 52, 13, 36, 52, 55, 15, 17, 56, 57, 40, 42, 65, 66, 7, + 14, 54, 55, 14, 37, 55, 64, 16, 18, 58, 59, 41, 43, 66, 67, 15, 40, + 56, 65, 17, 42, 57, 66, 40, 48, 68, 70, 48, 62, 70, 72, 16, 41, 58, + 66, 18, 43, 59, 67, 41, 49, 69, 71, 49, 63, 71, 73, 15, 20, 56, 58, + 52, 55, 75, 78, 26, 27, 65, 66, 65, 66, 98, 99, 20, 25, 68, 69, 55, + 64, 96, 97, 27, 28, 70, 71, 66, 67, 100, 101, 56, 68, 104, 105, 75, + 96, 108, 109, 65, 70, 105, 106, 98, 100, 111, 112, 58, 69, 105, 106, + 78, 97, 109, 110, 66, 71, 106, 107, 99, 101, 112, 113, 12, 51, 38, + 56, 15, 52, 40, 58, 51, 74, 56, 75, 56, 75, 65, 80, 19, 54, 44, 68, + 20, 55, 45, 69, 54, 77, 68, 82, 58, 78, 70, 83, 15, 56, 40, 65, 17, + 57, 42, 66, 52, 75, 58, 80, 57, 76, 66, 81, 20, 58, 45, 70, 21, 59, + 46, 71, 55, 78, 69, 83, 59, 79, 71, 84, 38, 56, 44, 68, 56, 75, 68, + 82, 56, 75, 68, 96, 104, 108, 105, 109, 44, 68, 120, 121, 68, 96, + 121, 122, 68, 82, 121, 122, 105, 109, 123, 124, 56, 104, 68, 105, 75, + 108, 96, 109, 75, 108, 82, 109, 108, 114, 109, 115, 68, 105, 121, + 123, 82, 109, 122, 124, 96, 109, 122, 124, 109, 115, 124, 125, 15, + 52, 56, 75, 20, 55, 58, 78, 56, 75, 104, 108, 68, 96, 105, 109, 20, + 55, 68, 96, 25, 64, 69, 97, 58, 78, 105, 109, 69, 97, 106, 110, 26, + 65, 65, 98, 27, 66, 66, 99, 65, 98, 105, 111, 70, 100, 106, 112, 27, + 66, 70, 100, 28, 67, 71, 101, 66, 99, 106, 112, 71, 101, 107, 113, + 40, 58, 68, 82, 58, 78, 82, 90, 65, 80, 105, 109, 105, 109, 127, 129, + 45, 69, 121, 122, 69, 97, 122, 126, 70, 83, 123, 124, 106, 110, 128, + 130, 65, 105, 105, 127, 80, 109, 109, 129, 98, 111, 127, 131, 111, + 116, 131, 133, 70, 106, 123, 128, 83, 110, 124, 130, 100, 112, 128, + 132, 112, 117, 132, 134, 6, 15, 51, 56, 15, 40, 56, 68, 13, 17, 52, + 57, 40, 48, 65, 70, 7, 16, 54, 58, 16, 41, 58, 69, 14, 18, 55, 59, + 41, 49, 66, 71, 13, 40, 52, 65, 17, 48, 57, 70, 36, 42, 55, 66, 42, + 62, 66, 72, 14, 41, 55, 66, 18, 49, 59, 71, 37, 43, 64, 67, 43, 63, + 67, 73, 15, 26, 56, 65, 56, 65, 104, 105, 20, 27, 58, 66, 68, 70, + 105, 106, 20, 27, 68, 70, 58, 66, 105, 106, 25, 28, 69, 71, 69, 71, + 106, 107, 52, 65, 75, 98, 75, 98, 108, 111, 55, 66, 78, 99, 96, 100, + 109, 112, 55, 66, 96, 100, 78, 99, 109, 112, 64, 67, 97, 101, 97, + 101, 110, 113, 8, 17, 74, 75, 17, 42, 75, 96, 17, 23, 75, 76, 48, 62, + 98, 100, 9, 18, 77, 78, 18, 43, 78, 97, 18, 24, 78, 79, 49, 63, 99, + 101, 17, 48, 75, 98, 23, 62, 76, 100, 42, 62, 96, 100, 62, 94, 100, + 102, 18, 49, 78, 99, 24, 63, 79, 101, 43, 63, 97, 101, 63, 95, 101, + 103, 17, 27, 75, 80, 57, 66, 108, 109, 27, 29, 80, 81, 70, 72, 111, + 112, 21, 28, 82, 83, 59, 67, 109, 110, 28, 30, 83, 84, 71, 73, 112, + 113, 57, 70, 108, 111, 76, 100, 114, 116, 66, 72, 109, 112, 100, 102, + 116, 118, 59, 71, 109, 112, 79, 101, 115, 117, 67, 73, 110, 113, 101, + 103, 117, 119, 15, 56, 56, 104, 26, 65, 65, 105, 52, 75, 75, 108, 65, + 98, 98, 111, 20, 58, 68, 105, 27, 66, 70, 106, 55, 78, 96, 109, 66, + 99, 100, 112, 20, 68, 58, 105, 27, 70, 66, 106, 55, 96, 78, 109, 66, + 100, 99, 112, 25, 69, 69, 106, 28, 71, 71, 107, 64, 97, 97, 110, 67, + 101, 101, 113, 40, 65, 68, 105, 65, 98, 105, 127, 58, 80, 82, 109, + 105, 111, 127, 131, 45, 70, 121, 123, 70, 100, 123, 128, 69, 83, 122, + 124, 106, 112, 128, 132, 58, 105, 82, 127, 80, 111, 109, 131, 78, + 109, 90, 129, 109, 116, 129, 133, 69, 106, 122, 128, 83, 112, 124, + 132, 97, 110, 126, 130, 110, 117, 130, 134, 17, 57, 75, 108, 27, 66, + 80, 109, 57, 76, 108, 114, 70, 100, 111, 116, 21, 59, 82, 109, 28, + 67, 83, 110, 59, 79, 109, 115, 71, 101, 112, 117, 27, 70, 80, 111, + 29, 72, 81, 112, 66, 100, 109, 116, 72, 102, 112, 118, 28, 71, 83, + 112, 30, 73, 84, 113, 67, 101, 110, 117, 73, 103, 113, 119, 42, 66, + 96, 109, 66, 99, 109, 129, 66, 81, 109, 115, 106, 112, 131, 133, 46, + 71, 122, 124, 71, 101, 124, 130, 71, 84, 124, 125, 107, 113, 132, + 134, 66, 106, 109, 131, 81, 112, 115, 133, 99, 112, 129, 133, 112, + 118, 133, 135, 71, 107, 124, 132, 84, 113, 125, 134, 101, 113, 130, + 134, 113, 119, 134, 136, 4, 7, 13, 16, 13, 16, 17, 21, 7, 9, 16, 18, + 20, 21, 27, 28, 7, 9, 20, 21, 16, 18, 27, 28, 9, 10, 21, 22, 21, 22, + 29, 30, 13, 20, 17, 27, 17, 27, 23, 29, 16, 21, 21, 28, 27, 29, 29, + 31, 16, 21, 27, 29, 21, 28, 29, 31, 18, 22, 28, 30, 28, 30, 31, 32, + 13, 16, 39, 41, 52, 53, 57, 59, 16, 18, 41, 43, 58, 59, 66, 67, 20, + 21, 45, 46, 58, 59, 70, 71, 21, 22, 46, 47, 60, 61, 72, 73, 52, 58, + 57, 66, 75, 80, 76, 81, 53, 59, 59, 67, 80, 81, 81, 85, 58, 60, 70, + 72, 82, 83, 86, 87, 59, 61, 71, 73, 83, 84, 87, 88, 13, 16, 52, 53, + 39, 41, 57, 59, 20, 21, 58, 59, 45, 46, 70, 71, 16, 18, 58, 59, 41, + 43, 66, 67, 21, 22, 60, 61, 46, 47, 72, 73, 52, 58, 75, 80, 57, 66, + 76, 81, 58, 60, 82, 83, 70, 72, 86, 87, 53, 59, 80, 81, 59, 67, 81, + 85, 59, 61, 83, 84, 71, 73, 87, 88, 17, 21, 57, 59, 57, 59, 76, 79, + 27, 28, 66, 67, 70, 71, 100, 101, 27, 28, 70, 71, 66, 67, 100, 101, + 29, 30, 72, 73, 72, 73, 102, 103, 75, 82, 108, 109, 108, 109, 114, + 115, 80, 83, 109, 110, 111, 112, 116, 117, 80, 83, 111, 112, 109, + 110, 116, 117, 81, 84, 112, 113, 112, 113, 118, 119, 35, 54, 40, 58, + 40, 58, 48, 60, 54, 77, 58, 78, 68, 82, 70, 83, 54, 77, 68, 82, 58, + 78, 70, 83, 77, 89, 82, 90, 82, 90, 86, 91, 40, 68, 48, 70, 48, 70, + 62, 72, 58, 82, 60, 83, 70, 86, 72, 87, 58, 82, 70, 86, 60, 83, 72, + 87, 78, 90, 83, 91, 83, 91, 87, 92, 40, 58, 45, 69, 65, 80, 70, 83, + 58, 78, 69, 97, 105, 109, 106, 110, 68, 82, 121, 122, 105, 109, 123, + 124, 82, 90, 122, 126, 127, 129, 128, 130, 65, 105, 70, 106, 98, 111, + 100, 112, 80, 109, 83, 110, 111, 116, 112, 117, 105, 127, 123, 128, + 127, 131, 128, 132, 109, 129, 124, 130, 131, 133, 132, 134, 40, 58, + 65, 80, 45, 69, 70, 83, 68, 82, 105, 109, 121, 122, 123, 124, 58, 78, + 105, 109, 69, 97, 106, 110, 82, 90, 127, 129, 122, 126, 128, 130, 65, + 105, 98, 111, 70, 106, 100, 112, 105, 127, 127, 131, 123, 128, 128, + 132, 80, 109, 111, 116, 83, 110, 112, 117, 109, 129, 131, 133, 124, + 130, 132, 134, 48, 60, 70, 83, 70, 83, 86, 91, 70, 83, 106, 110, 123, + 124, 128, 130, 70, 83, 123, 124, 106, 110, 128, 130, 86, 91, 128, + 130, 128, 130, 141, 142, 98, 127, 111, 131, 111, 131, 116, 133, 111, + 131, 131, 143, 144, 145, 145, 146, 111, 131, 144, 145, 131, 143, 145, + 146, 116, 133, 145, 146, 145, 146, 147, 148, 13, 20, 52, 58, 52, 58, + 75, 82, 16, 21, 53, 59, 58, 60, 80, 83, 16, 21, 58, 60, 53, 59, 80, + 83, 18, 22, 59, 61, 59, 61, 81, 84, 39, 45, 57, 70, 57, 70, 76, 86, + 41, 46, 59, 71, 66, 72, 81, 87, 41, 46, 66, 72, 59, 71, 81, 87, 43, + 47, 67, 73, 67, 73, 85, 88, 17, 27, 57, 66, 75, 80, 108, 109, 21, 28, + 59, 67, 82, 83, 109, 110, 27, 29, 70, 72, 80, 81, 111, 112, 28, 30, + 71, 73, 83, 84, 112, 113, 57, 70, 76, 100, 108, 111, 114, 116, 59, + 71, 79, 101, 109, 112, 115, 117, 66, 72, 100, 102, 109, 112, 116, + 118, 67, 73, 101, 103, 110, 113, 117, 119, 17, 27, 75, 80, 57, 66, + 108, 109, 27, 29, 80, 81, 70, 72, 111, 112, 21, 28, 82, 83, 59, 67, + 109, 110, 28, 30, 83, 84, 71, 73, 112, 113, 57, 70, 108, 111, 76, + 100, 114, 116, 66, 72, 109, 112, 100, 102, 116, 118, 59, 71, 109, + 112, 79, 101, 115, 117, 67, 73, 110, 113, 101, 103, 117, 119, 23, 29, + 76, 81, 76, 81, 114, 115, 29, 31, 81, 85, 86, 87, 116, 117, 29, 31, + 86, 87, 81, 85, 116, 117, 31, 32, 87, 88, 87, 88, 118, 119, 76, 86, + 114, 116, 114, 116, 137, 138, 81, 87, 115, 117, 116, 118, 138, 139, + 81, 87, 116, 118, 115, 117, 138, 139, 85, 88, 117, 119, 117, 119, + 139, 140, 40, 68, 65, 105, 65, 105, 98, 127, 58, 82, 80, 109, 105, + 127, 111, 131, 58, 82, 105, 127, 80, 109, 111, 131, 78, 90, 109, 129, + 109, 129, 116, 133, 45, 121, 70, 123, 70, 123, 100, 128, 69, 122, 83, + 124, 106, 128, 112, 132, 69, 122, 106, 128, 83, 124, 112, 132, 97, + 126, 110, 130, 110, 130, 117, 134, 48, 70, 70, 106, 98, 111, 111, + 131, 60, 83, 83, 110, 127, 131, 131, 143, 70, 86, 123, 128, 111, 116, + 144, 145, 83, 91, 124, 130, 131, 133, 145, 146, 70, 123, 86, 128, + 111, 144, 116, 145, 83, 124, 91, 130, 131, 145, 133, 146, 106, 128, + 128, 141, 131, 145, 145, 147, 110, 130, 130, 142, 143, 146, 146, 148, + 48, 70, 98, 111, 70, 106, 111, 131, 70, 86, 111, 116, 123, 128, 144, + 145, 60, 83, 127, 131, 83, 110, 131, 143, 83, 91, 131, 133, 124, 130, + 145, 146, 70, 123, 111, 144, 86, 128, 116, 145, 106, 128, 131, 145, + 128, 141, 145, 147, 83, 124, 131, 145, 91, 130, 133, 146, 110, 130, + 143, 146, 130, 142, 146, 148, 62, 72, 100, 112, 100, 112, 116, 133, + 72, 87, 112, 117, 128, 132, 145, 146, 72, 87, 128, 132, 112, 117, + 145, 146, 87, 92, 132, 134, 132, 134, 147, 148, 100, 128, 116, 145, + 116, 145, 138, 149, 112, 132, 133, 146, 145, 147, 149, 150, 112, 132, + 145, 147, 133, 146, 149, 150, 117, 134, 146, 148, 146, 148, 150, 151, + 2, 12, 6, 15, 6, 15, 13, 20, 6, 15, 8, 17, 15, 26, 17, 27, 6, 15, 15, + 26, 8, 17, 17, 27, 13, 20, 17, 27, 17, 27, 23, 29, 3, 19, 7, 20, 7, + 20, 14, 25, 7, 20, 9, 21, 16, 27, 18, 28, 7, 20, 16, 27, 9, 21, 18, + 28, 14, 25, 18, 28, 18, 28, 24, 30, 12, 38, 15, 40, 51, 56, 52, 58, + 15, 40, 17, 42, 56, 65, 57, 66, 51, 56, 56, 65, 74, 75, 75, 80, 52, + 58, 57, 66, 75, 80, 76, 81, 19, 44, 20, 45, 54, 68, 55, 69, 20, 45, + 21, 46, 58, 70, 59, 71, 54, 68, 58, 70, 77, 82, 78, 83, 55, 69, 59, + 71, 78, 83, 79, 84, 12, 38, 51, 56, 15, 40, 52, 58, 51, 56, 74, 75, + 56, 65, 75, 80, 15, 40, 56, 65, 17, 42, 57, 66, 52, 58, 75, 80, 57, + 66, 76, 81, 19, 44, 54, 68, 20, 45, 55, 69, 54, 68, 77, 82, 58, 70, + 78, 83, 20, 45, 58, 70, 21, 46, 59, 71, 55, 69, 78, 83, 59, 71, 79, + 84, 38, 44, 56, 68, 56, 68, 75, 82, 56, 68, 75, 96, 104, 105, 108, + 109, 56, 68, 104, 105, 75, 96, 108, 109, 75, 82, 108, 109, 108, 109, + 114, 115, 44, 120, 68, 121, 68, 121, 96, 122, 68, 121, 82, 122, 105, + 123, 109, 124, 68, 121, 105, 123, 82, 122, 109, 124, 96, 122, 109, + 124, 109, 124, 115, 125, 6, 51, 13, 52, 13, 52, 36, 55, 15, 56, 17, + 57, 40, 65, 42, 66, 15, 56, 40, 65, 17, 57, 42, 66, 40, 68, 48, 70, + 48, 70, 62, 72, 7, 54, 14, 55, 14, 55, 37, 64, 16, 58, 18, 59, 41, + 66, 43, 67, 16, 58, 41, 66, 18, 59, 43, 67, 41, 69, 49, 71, 49, 71, + 63, 73, 15, 56, 20, 58, 52, 75, 55, 78, 26, 65, 27, 66, 65, 98, 66, + 99, 56, 104, 68, 105, 75, 108, 96, 109, 65, 105, 70, 106, 98, 111, + 100, 112, 20, 68, 25, 69, 55, 96, 64, 97, 27, 70, 28, 71, 66, 100, + 67, 101, 58, 105, 69, 106, 78, 109, 97, 110, 66, 106, 71, 107, 99, + 112, 101, 113, 15, 56, 52, 75, 20, 58, 55, 78, 56, 104, 75, 108, 68, + 105, 96, 109, 26, 65, 65, 98, 27, 66, 66, 99, 65, 105, 98, 111, 70, + 106, 100, 112, 20, 68, 55, 96, 25, 69, 64, 97, 58, 105, 78, 109, 69, + 106, 97, 110, 27, 70, 66, 100, 28, 71, 67, 101, 66, 106, 99, 112, 71, + 107, 101, 113, 40, 68, 58, 82, 58, 82, 78, 90, 65, 105, 80, 109, 105, + 127, 109, 129, 65, 105, 105, 127, 80, 109, 109, 129, 98, 127, 111, + 131, 111, 131, 116, 133, 45, 121, 69, 122, 69, 122, 97, 126, 70, 123, + 83, 124, 106, 128, 110, 130, 70, 123, 106, 128, 83, 124, 110, 130, + 100, 128, 112, 132, 112, 132, 117, 134, 6, 51, 15, 56, 15, 56, 40, + 68, 13, 52, 17, 57, 40, 65, 48, 70, 13, 52, 40, 65, 17, 57, 48, 70, + 36, 55, 42, 66, 42, 66, 62, 72, 7, 54, 16, 58, 16, 58, 41, 69, 14, + 55, 18, 59, 41, 66, 49, 71, 14, 55, 41, 66, 18, 59, 49, 71, 37, 64, + 43, 67, 43, 67, 63, 73, 15, 56, 26, 65, 56, 104, 65, 105, 20, 58, 27, + 66, 68, 105, 70, 106, 52, 75, 65, 98, 75, 108, 98, 111, 55, 78, 66, + 99, 96, 109, 100, 112, 20, 68, 27, 70, 58, 105, 66, 106, 25, 69, 28, + 71, 69, 106, 71, 107, 55, 96, 66, 100, 78, 109, 99, 112, 64, 97, 67, + 101, 97, 110, 101, 113, 15, 56, 56, 104, 26, 65, 65, 105, 52, 75, 75, + 108, 65, 98, 98, 111, 20, 58, 68, 105, 27, 66, 70, 106, 55, 78, 96, + 109, 66, 99, 100, 112, 20, 68, 58, 105, 27, 70, 66, 106, 55, 96, 78, + 109, 66, 100, 99, 112, 25, 69, 69, 106, 28, 71, 71, 107, 64, 97, 97, + 110, 67, 101, 101, 113, 40, 68, 65, 105, 65, 105, 98, 127, 58, 82, + 80, 109, 105, 127, 111, 131, 58, 82, 105, 127, 80, 109, 111, 131, 78, + 90, 109, 129, 109, 129, 116, 133, 45, 121, 70, 123, 70, 123, 100, + 128, 69, 122, 83, 124, 106, 128, 112, 132, 69, 122, 106, 128, 83, + 124, 112, 132, 97, 126, 110, 130, 110, 130, 117, 134, 8, 74, 17, 75, + 17, 75, 42, 96, 17, 75, 23, 76, 48, 98, 62, 100, 17, 75, 48, 98, 23, + 76, 62, 100, 42, 96, 62, 100, 62, 100, 94, 102, 9, 77, 18, 78, 18, + 78, 43, 97, 18, 78, 24, 79, 49, 99, 63, 101, 18, 78, 49, 99, 24, 79, + 63, 101, 43, 97, 63, 101, 63, 101, 95, 103, 17, 75, 27, 80, 57, 108, + 66, 109, 27, 80, 29, 81, 70, 111, 72, 112, 57, 108, 70, 111, 76, 114, + 100, 116, 66, 109, 72, 112, 100, 116, 102, 118, 21, 82, 28, 83, 59, + 109, 67, 110, 28, 83, 30, 84, 71, 112, 73, 113, 59, 109, 71, 112, 79, + 115, 101, 117, 67, 110, 73, 113, 101, 117, 103, 119, 17, 75, 57, 108, + 27, 80, 66, 109, 57, 108, 76, 114, 70, 111, 100, 116, 27, 80, 70, + 111, 29, 81, 72, 112, 66, 109, 100, 116, 72, 112, 102, 118, 21, 82, + 59, 109, 28, 83, 67, 110, 59, 109, 79, 115, 71, 112, 101, 117, 28, + 83, 71, 112, 30, 84, 73, 113, 67, 110, 101, 117, 73, 113, 103, 119, + 42, 96, 66, 109, 66, 109, 99, 129, 66, 109, 81, 115, 106, 131, 112, + 133, 66, 109, 106, 131, 81, 115, 112, 133, 99, 129, 112, 133, 112, + 133, 118, 135, 46, 122, 71, 124, 71, 124, 101, 130, 71, 124, 84, 125, + 107, 132, 113, 134, 71, 124, 107, 132, 84, 125, 113, 134, 101, 130, + 113, 134, 113, 134, 119, 136, 4, 13, 7, 16, 13, 17, 16, 21, 7, 16, 9, + 18, 20, 27, 21, 28, 13, 17, 20, 27, 17, 23, 27, 29, 16, 21, 21, 28, + 27, 29, 29, 31, 7, 20, 9, 21, 16, 27, 18, 28, 9, 21, 10, 22, 21, 29, + 22, 30, 16, 27, 21, 29, 21, 29, 28, 31, 18, 28, 22, 30, 28, 31, 30, + 32, 13, 39, 16, 41, 52, 57, 53, 59, 16, 41, 18, 43, 58, 66, 59, 67, + 52, 57, 58, 66, 75, 76, 80, 81, 53, 59, 59, 67, 80, 81, 81, 85, 20, + 45, 21, 46, 58, 70, 59, 71, 21, 46, 22, 47, 60, 72, 61, 73, 58, 70, + 60, 72, 82, 86, 83, 87, 59, 71, 61, 73, 83, 87, 84, 88, 35, 40, 54, + 58, 40, 48, 58, 60, 54, 58, 77, 78, 68, 70, 82, 83, 40, 48, 68, 70, + 48, 62, 70, 72, 58, 60, 82, 83, 70, 72, 86, 87, 54, 68, 77, 82, 58, + 70, 78, 83, 77, 82, 89, 90, 82, 86, 90, 91, 58, 70, 82, 86, 60, 72, + 83, 87, 78, 83, 90, 91, 83, 87, 91, 92, 40, 45, 58, 69, 65, 70, 80, + 83, 58, 69, 78, 97, 105, 106, 109, 110, 65, 70, 105, 106, 98, 100, + 111, 112, 80, 83, 109, 110, 111, 112, 116, 117, 68, 121, 82, 122, + 105, 123, 109, 124, 82, 122, 90, 126, 127, 128, 129, 130, 105, 123, + 127, 128, 127, 128, 131, 132, 109, 124, 129, 130, 131, 132, 133, 134, + 13, 52, 16, 53, 39, 57, 41, 59, 20, 58, 21, 59, 45, 70, 46, 71, 52, + 75, 58, 80, 57, 76, 66, 81, 58, 82, 60, 83, 70, 86, 72, 87, 16, 58, + 18, 59, 41, 66, 43, 67, 21, 60, 22, 61, 46, 72, 47, 73, 53, 80, 59, + 81, 59, 81, 67, 85, 59, 83, 61, 84, 71, 87, 73, 88, 17, 57, 21, 59, + 57, 76, 59, 79, 27, 66, 28, 67, 70, 100, 71, 101, 75, 108, 82, 109, + 108, 114, 109, 115, 80, 109, 83, 110, 111, 116, 112, 117, 27, 70, 28, + 71, 66, 100, 67, 101, 29, 72, 30, 73, 72, 102, 73, 103, 80, 111, 83, + 112, 109, 116, 110, 117, 81, 112, 84, 113, 112, 118, 113, 119, 40, + 65, 58, 80, 45, 70, 69, 83, 68, 105, 82, 109, 121, 123, 122, 124, 65, + 98, 105, 111, 70, 100, 106, 112, 105, 127, 127, 131, 123, 128, 128, + 132, 58, 105, 78, 109, 69, 106, 97, 110, 82, 127, 90, 129, 122, 128, + 126, 130, 80, 111, 109, 116, 83, 112, 110, 117, 109, 131, 129, 133, + 124, 132, 130, 134, 48, 70, 60, 83, 70, 86, 83, 91, 70, 106, 83, 110, + 123, 128, 124, 130, 98, 111, 127, 131, 111, 116, 131, 133, 111, 131, + 131, 143, 144, 145, 145, 146, 70, 123, 83, 124, 106, 128, 110, 130, + 86, 128, 91, 130, 128, 141, 130, 142, 111, 144, 131, 145, 131, 145, + 143, 146, 116, 145, 133, 146, 145, 147, 146, 148, 13, 52, 20, 58, 52, + 75, 58, 82, 16, 53, 21, 59, 58, 80, 60, 83, 39, 57, 45, 70, 57, 76, + 70, 86, 41, 59, 46, 71, 66, 81, 72, 87, 16, 58, 21, 60, 53, 80, 59, + 83, 18, 59, 22, 61, 59, 81, 61, 84, 41, 66, 46, 72, 59, 81, 71, 87, + 43, 67, 47, 73, 67, 85, 73, 88, 17, 57, 27, 66, 75, 108, 80, 109, 21, + 59, 28, 67, 82, 109, 83, 110, 57, 76, 70, 100, 108, 114, 111, 116, + 59, 79, 71, 101, 109, 115, 112, 117, 27, 70, 29, 72, 80, 111, 81, + 112, 28, 71, 30, 73, 83, 112, 84, 113, 66, 100, 72, 102, 109, 116, + 112, 118, 67, 101, 73, 103, 110, 117, 113, 119, 40, 65, 68, 105, 65, + 98, 105, 127, 58, 80, 82, 109, 105, 111, 127, 131, 45, 70, 121, 123, + 70, 100, 123, 128, 69, 83, 122, 124, 106, 112, 128, 132, 58, 105, 82, + 127, 80, 111, 109, 131, 78, 109, 90, 129, 109, 116, 129, 133, 69, + 106, 122, 128, 83, 112, 124, 132, 97, 110, 126, 130, 110, 117, 130, + 134, 48, 70, 70, 106, 98, 111, 111, 131, 60, 83, 83, 110, 127, 131, + 131, 143, 70, 86, 123, 128, 111, 116, 144, 145, 83, 91, 124, 130, + 131, 133, 145, 146, 70, 123, 86, 128, 111, 144, 116, 145, 83, 124, + 91, 130, 131, 145, 133, 146, 106, 128, 128, 141, 131, 145, 145, 147, + 110, 130, 130, 142, 143, 146, 146, 148, 17, 75, 27, 80, 57, 108, 66, + 109, 27, 80, 29, 81, 70, 111, 72, 112, 57, 108, 70, 111, 76, 114, + 100, 116, 66, 109, 72, 112, 100, 116, 102, 118, 21, 82, 28, 83, 59, + 109, 67, 110, 28, 83, 30, 84, 71, 112, 73, 113, 59, 109, 71, 112, 79, + 115, 101, 117, 67, 110, 73, 113, 101, 117, 103, 119, 23, 76, 29, 81, + 76, 114, 81, 115, 29, 81, 31, 85, 86, 116, 87, 117, 76, 114, 86, 116, + 114, 137, 116, 138, 81, 115, 87, 117, 116, 138, 118, 139, 29, 86, 31, + 87, 81, 116, 85, 117, 31, 87, 32, 88, 87, 118, 88, 119, 81, 116, 87, + 118, 115, 138, 117, 139, 85, 117, 88, 119, 117, 139, 119, 140, 48, + 98, 70, 111, 70, 111, 106, 131, 70, 111, 86, 116, 123, 144, 128, 145, + 70, 111, 123, 144, 86, 116, 128, 145, 106, 131, 128, 145, 128, 145, + 141, 147, 60, 127, 83, 131, 83, 131, 110, 143, 83, 131, 91, 133, 124, + 145, 130, 146, 83, 131, 124, 145, 91, 133, 130, 146, 110, 143, 130, + 146, 130, 146, 142, 148, 62, 100, 72, 112, 100, 116, 112, 133, 72, + 112, 87, 117, 128, 145, 132, 146, 100, 116, 128, 145, 116, 138, 145, + 149, 112, 133, 132, 146, 145, 149, 147, 150, 72, 128, 87, 132, 112, + 145, 117, 146, 87, 132, 92, 134, 132, 147, 134, 148, 112, 145, 132, + 147, 133, 149, 146, 150, 117, 146, 134, 148, 146, 150, 148, 151, 4, + 13, 13, 17, 7, 16, 16, 21, 13, 17, 17, 23, 20, 27, 27, 29, 7, 16, 20, + 27, 9, 18, 21, 28, 16, 21, 27, 29, 21, 28, 29, 31, 7, 20, 16, 27, 9, + 21, 18, 28, 16, 27, 21, 29, 21, 29, 28, 31, 9, 21, 21, 29, 10, 22, + 22, 30, 18, 28, 28, 31, 22, 30, 30, 32, 35, 40, 40, 48, 54, 58, 58, + 60, 40, 48, 48, 62, 68, 70, 70, 72, 54, 58, 68, 70, 77, 78, 82, 83, + 58, 60, 70, 72, 82, 83, 86, 87, 54, 68, 58, 70, 77, 82, 78, 83, 58, + 70, 60, 72, 82, 86, 83, 87, 77, 82, 82, 86, 89, 90, 90, 91, 78, 83, + 83, 87, 90, 91, 91, 92, 13, 39, 52, 57, 16, 41, 53, 59, 52, 57, 75, + 76, 58, 66, 80, 81, 16, 41, 58, 66, 18, 43, 59, 67, 53, 59, 80, 81, + 59, 67, 81, 85, 20, 45, 58, 70, 21, 46, 59, 71, 58, 70, 82, 86, 60, + 72, 83, 87, 21, 46, 60, 72, 22, 47, 61, 73, 59, 71, 83, 87, 61, 73, + 84, 88, 40, 45, 65, 70, 58, 69, 80, 83, 65, 70, 98, 100, 105, 106, + 111, 112, 58, 69, 105, 106, 78, 97, 109, 110, 80, 83, 111, 112, 109, + 110, 116, 117, 68, 121, 105, 123, 82, 122, 109, 124, 105, 123, 127, + 128, 127, 128, 131, 132, 82, 122, 127, 128, 90, 126, 129, 130, 109, + 124, 131, 132, 129, 130, 133, 134, 13, 52, 39, 57, 16, 53, 41, 59, + 52, 75, 57, 76, 58, 80, 66, 81, 20, 58, 45, 70, 21, 59, 46, 71, 58, + 82, 70, 86, 60, 83, 72, 87, 16, 58, 41, 66, 18, 59, 43, 67, 53, 80, + 59, 81, 59, 81, 67, 85, 21, 60, 46, 72, 22, 61, 47, 73, 59, 83, 71, + 87, 61, 84, 73, 88, 40, 65, 45, 70, 58, 80, 69, 83, 65, 98, 70, 100, + 105, 111, 106, 112, 68, 105, 121, 123, 82, 109, 122, 124, 105, 127, + 123, 128, 127, 131, 128, 132, 58, 105, 69, 106, 78, 109, 97, 110, 80, + 111, 83, 112, 109, 116, 110, 117, 82, 127, 122, 128, 90, 129, 126, + 130, 109, 131, 124, 132, 129, 133, 130, 134, 17, 57, 57, 76, 21, 59, + 59, 79, 75, 108, 108, 114, 82, 109, 109, 115, 27, 66, 70, 100, 28, + 67, 71, 101, 80, 109, 111, 116, 83, 110, 112, 117, 27, 70, 66, 100, + 28, 71, 67, 101, 80, 111, 109, 116, 83, 112, 110, 117, 29, 72, 72, + 102, 30, 73, 73, 103, 81, 112, 112, 118, 84, 113, 113, 119, 48, 70, + 70, 86, 60, 83, 83, 91, 98, 111, 111, 116, 127, 131, 131, 133, 70, + 106, 123, 128, 83, 110, 124, 130, 111, 131, 144, 145, 131, 143, 145, + 146, 70, 123, 106, 128, 83, 124, 110, 130, 111, 144, 131, 145, 131, + 145, 143, 146, 86, 128, 128, 141, 91, 130, 130, 142, 116, 145, 145, + 147, 133, 146, 146, 148, 13, 52, 52, 75, 20, 58, 58, 82, 39, 57, 57, + 76, 45, 70, 70, 86, 16, 53, 58, 80, 21, 59, 60, 83, 41, 59, 66, 81, + 46, 71, 72, 87, 16, 58, 53, 80, 21, 60, 59, 83, 41, 66, 59, 81, 46, + 72, 71, 87, 18, 59, 59, 81, 22, 61, 61, 84, 43, 67, 67, 85, 47, 73, + 73, 88, 40, 65, 65, 98, 68, 105, 105, 127, 45, 70, 70, 100, 121, 123, + 123, 128, 58, 80, 105, 111, 82, 109, 127, 131, 69, 83, 106, 112, 122, + 124, 128, 132, 58, 105, 80, 111, 82, 127, 109, 131, 69, 106, 83, 112, + 122, 128, 124, 132, 78, 109, 109, 116, 90, 129, 129, 133, 97, 110, + 110, 117, 126, 130, 130, 134, 17, 57, 75, 108, 27, 66, 80, 109, 57, + 76, 108, 114, 70, 100, 111, 116, 21, 59, 82, 109, 28, 67, 83, 110, + 59, 79, 109, 115, 71, 101, 112, 117, 27, 70, 80, 111, 29, 72, 81, + 112, 66, 100, 109, 116, 72, 102, 112, 118, 28, 71, 83, 112, 30, 73, + 84, 113, 67, 101, 110, 117, 73, 103, 113, 119, 48, 70, 98, 111, 70, + 106, 111, 131, 70, 86, 111, 116, 123, 128, 144, 145, 60, 83, 127, + 131, 83, 110, 131, 143, 83, 91, 131, 133, 124, 130, 145, 146, 70, + 123, 111, 144, 86, 128, 116, 145, 106, 128, 131, 145, 128, 141, 145, + 147, 83, 124, 131, 145, 91, 130, 133, 146, 110, 130, 143, 146, 130, + 142, 146, 148, 17, 75, 57, 108, 27, 80, 66, 109, 57, 108, 76, 114, + 70, 111, 100, 116, 27, 80, 70, 111, 29, 81, 72, 112, 66, 109, 100, + 116, 72, 112, 102, 118, 21, 82, 59, 109, 28, 83, 67, 110, 59, 109, + 79, 115, 71, 112, 101, 117, 28, 83, 71, 112, 30, 84, 73, 113, 67, + 110, 101, 117, 73, 113, 103, 119, 48, 98, 70, 111, 70, 111, 106, 131, + 70, 111, 86, 116, 123, 144, 128, 145, 70, 111, 123, 144, 86, 116, + 128, 145, 106, 131, 128, 145, 128, 145, 141, 147, 60, 127, 83, 131, + 83, 131, 110, 143, 83, 131, 91, 133, 124, 145, 130, 146, 83, 131, + 124, 145, 91, 133, 130, 146, 110, 143, 130, 146, 130, 146, 142, 148, + 23, 76, 76, 114, 29, 81, 81, 115, 76, 114, 114, 137, 86, 116, 116, + 138, 29, 81, 86, 116, 31, 85, 87, 117, 81, 115, 116, 138, 87, 117, + 118, 139, 29, 86, 81, 116, 31, 87, 85, 117, 81, 116, 115, 138, 87, + 118, 117, 139, 31, 87, 87, 118, 32, 88, 88, 119, 85, 117, 117, 139, + 88, 119, 119, 140, 62, 100, 100, 116, 72, 112, 112, 133, 100, 116, + 116, 138, 128, 145, 145, 149, 72, 112, 128, 145, 87, 117, 132, 146, + 112, 133, 145, 149, 132, 146, 147, 150, 72, 128, 112, 145, 87, 132, + 117, 146, 112, 145, 133, 149, 132, 147, 146, 150, 87, 132, 132, 147, + 92, 134, 134, 148, 117, 146, 146, 150, 134, 148, 148, 151, 11, 14, + 14, 18, 14, 18, 18, 22, 14, 18, 18, 24, 25, 28, 28, 30, 14, 18, 25, + 28, 18, 24, 28, 30, 18, 22, 28, 30, 28, 30, 31, 32, 14, 25, 18, 28, + 18, 28, 24, 30, 18, 28, 22, 30, 28, 31, 30, 32, 18, 28, 28, 31, 22, + 30, 30, 32, 24, 30, 30, 32, 30, 32, 32, 33, 36, 41, 41, 49, 55, 59, + 59, 61, 41, 49, 49, 63, 69, 71, 71, 73, 55, 59, 69, 71, 78, 79, 83, + 84, 59, 61, 71, 73, 83, 84, 87, 88, 55, 69, 59, 71, 78, 83, 79, 84, + 59, 71, 61, 73, 83, 87, 84, 88, 78, 83, 83, 87, 90, 91, 91, 92, 79, + 84, 84, 88, 91, 92, 92, 93, 36, 41, 55, 59, 41, 49, 59, 61, 55, 59, + 78, 79, 69, 71, 83, 84, 41, 49, 69, 71, 49, 63, 71, 73, 59, 61, 83, + 84, 71, 73, 87, 88, 55, 69, 78, 83, 59, 71, 79, 84, 78, 83, 90, 91, + 83, 87, 91, 92, 59, 71, 83, 87, 61, 73, 84, 88, 79, 84, 91, 92, 84, + 88, 92, 93, 42, 46, 66, 71, 66, 71, 81, 84, 66, 71, 99, 101, 106, + 107, 112, 113, 66, 71, 106, 107, 99, 101, 112, 113, 81, 84, 112, 113, + 112, 113, 118, 119, 96, 122, 109, 124, 109, 124, 115, 125, 109, 124, + 129, 130, 131, 132, 133, 134, 109, 124, 131, 132, 129, 130, 133, 134, + 115, 125, 133, 134, 133, 134, 135, 136, 36, 55, 41, 59, 41, 59, 49, + 61, 55, 78, 59, 79, 69, 83, 71, 84, 55, 78, 69, 83, 59, 79, 71, 84, + 78, 90, 83, 91, 83, 91, 87, 92, 41, 69, 49, 71, 49, 71, 63, 73, 59, + 83, 61, 84, 71, 87, 73, 88, 59, 83, 71, 87, 61, 84, 73, 88, 79, 91, + 84, 92, 84, 92, 88, 93, 42, 66, 46, 71, 66, 81, 71, 84, 66, 99, 71, + 101, 106, 112, 107, 113, 96, 109, 122, 124, 109, 115, 124, 125, 109, + 129, 124, 130, 131, 133, 132, 134, 66, 106, 71, 107, 99, 112, 101, + 113, 81, 112, 84, 113, 112, 118, 113, 119, 109, 131, 124, 132, 129, + 133, 130, 134, 115, 133, 125, 134, 133, 135, 134, 136, 42, 66, 66, + 81, 46, 71, 71, 84, 96, 109, 109, 115, 122, 124, 124, 125, 66, 99, + 106, 112, 71, 101, 107, 113, 109, 129, 131, 133, 124, 130, 132, 134, + 66, 106, 99, 112, 71, 107, 101, 113, 109, 131, 129, 133, 124, 132, + 130, 134, 81, 112, 112, 118, 84, 113, 113, 119, 115, 133, 133, 135, + 125, 134, 134, 136, 62, 72, 72, 87, 72, 87, 87, 92, 100, 112, 112, + 117, 128, 132, 132, 134, 100, 112, 128, 132, 112, 117, 132, 134, 116, + 133, 145, 146, 145, 146, 147, 148, 100, 128, 112, 132, 112, 132, 117, + 134, 116, 145, 133, 146, 145, 147, 146, 148, 116, 145, 145, 147, 133, + 146, 146, 148, 138, 149, 149, 150, 149, 150, 150, 151, 36, 55, 55, + 78, 55, 78, 78, 90, 41, 59, 59, 79, 69, 83, 83, 91, 41, 59, 69, 83, + 59, 79, 83, 91, 49, 61, 71, 84, 71, 84, 87, 92, 41, 69, 59, 83, 59, + 83, 79, 91, 49, 71, 61, 84, 71, 87, 84, 92, 49, 71, 71, 87, 61, 84, + 84, 92, 63, 73, 73, 88, 73, 88, 88, 93, 42, 66, 66, 99, 96, 109, 109, + 129, 46, 71, 71, 101, 122, 124, 124, 130, 66, 81, 106, 112, 109, 115, + 131, 133, 71, 84, 107, 113, 124, 125, 132, 134, 66, 106, 81, 112, + 109, 131, 115, 133, 71, 107, 84, 113, 124, 132, 125, 134, 99, 112, + 112, 118, 129, 133, 133, 135, 101, 113, 113, 119, 130, 134, 134, 136, + 42, 66, 96, 109, 66, 99, 109, 129, 66, 81, 109, 115, 106, 112, 131, + 133, 46, 71, 122, 124, 71, 101, 124, 130, 71, 84, 124, 125, 107, 113, + 132, 134, 66, 106, 109, 131, 81, 112, 115, 133, 99, 112, 129, 133, + 112, 118, 133, 135, 71, 107, 124, 132, 84, 113, 125, 134, 101, 113, + 130, 134, 113, 119, 134, 136, 62, 72, 100, 112, 100, 112, 116, 133, + 72, 87, 112, 117, 128, 132, 145, 146, 72, 87, 128, 132, 112, 117, + 145, 146, 87, 92, 132, 134, 132, 134, 147, 148, 100, 128, 116, 145, + 116, 145, 138, 149, 112, 132, 133, 146, 145, 147, 149, 150, 112, 132, + 145, 147, 133, 146, 149, 150, 117, 134, 146, 148, 146, 148, 150, 151, + 42, 96, 66, 109, 66, 109, 99, 129, 66, 109, 81, 115, 106, 131, 112, + 133, 66, 109, 106, 131, 81, 115, 112, 133, 99, 129, 112, 133, 112, + 133, 118, 135, 46, 122, 71, 124, 71, 124, 101, 130, 71, 124, 84, 125, + 107, 132, 113, 134, 71, 124, 107, 132, 84, 125, 113, 134, 101, 130, + 113, 134, 113, 134, 119, 136, 62, 100, 72, 112, 100, 116, 112, 133, + 72, 112, 87, 117, 128, 145, 132, 146, 100, 116, 128, 145, 116, 138, + 145, 149, 112, 133, 132, 146, 145, 149, 147, 150, 72, 128, 87, 132, + 112, 145, 117, 146, 87, 132, 92, 134, 132, 147, 134, 148, 112, 145, + 132, 147, 133, 149, 146, 150, 117, 146, 134, 148, 146, 150, 148, 151, + 62, 100, 100, 116, 72, 112, 112, 133, 100, 116, 116, 138, 128, 145, + 145, 149, 72, 112, 128, 145, 87, 117, 132, 146, 112, 133, 145, 149, + 132, 146, 147, 150, 72, 128, 112, 145, 87, 132, 117, 146, 112, 145, + 133, 149, 132, 147, 146, 150, 87, 132, 132, 147, 92, 134, 134, 148, + 117, 146, 146, 150, 134, 148, 148, 151, 94, 102, 102, 118, 102, 118, + 118, 135, 102, 118, 118, 139, 141, 147, 147, 150, 102, 118, 141, 147, + 118, 139, 147, 150, 118, 135, 147, 150, 147, 150, 152, 153, 102, 141, + 118, 147, 118, 147, 139, 150, 118, 147, 135, 150, 147, 152, 150, 153, + 118, 147, 147, 152, 135, 150, 150, 153, 139, 150, 150, 153, 150, 153, + 153, 154, 1, 5, 5, 12, 5, 12, 12, 19, 5, 12, 12, 35, 50, 51, 51, 54, + 5, 12, 50, 51, 12, 35, 51, 54, 12, 19, 51, 54, 51, 54, 74, 77, 5, 50, + 12, 51, 12, 51, 35, 54, 12, 51, 19, 54, 51, 74, 54, 77, 12, 51, 51, + 74, 19, 54, 54, 77, 35, 54, 54, 77, 54, 77, 77, 89, 2, 6, 6, 13, 12, + 15, 15, 20, 6, 13, 13, 36, 51, 52, 52, 55, 12, 15, 51, 52, 38, 40, + 56, 58, 15, 20, 52, 55, 56, 58, 75, 78, 12, 51, 15, 52, 38, 56, 40, + 58, 15, 52, 20, 55, 56, 75, 58, 78, 38, 56, 56, 75, 44, 68, 68, 82, + 40, 58, 58, 78, 68, 82, 82, 90, 2, 6, 12, 15, 6, 13, 15, 20, 12, 15, + 38, 40, 51, 52, 56, 58, 6, 13, 51, 52, 13, 36, 52, 55, 15, 20, 56, + 58, 52, 55, 75, 78, 12, 51, 38, 56, 15, 52, 40, 58, 38, 56, 44, 68, + 56, 75, 68, 82, 15, 52, 56, 75, 20, 55, 58, 78, 40, 58, 68, 82, 58, + 78, 82, 90, 4, 7, 13, 16, 13, 16, 17, 21, 13, 16, 39, 41, 52, 53, 57, + 59, 13, 16, 52, 53, 39, 41, 57, 59, 17, 21, 57, 59, 57, 59, 76, 79, + 35, 54, 40, 58, 40, 58, 48, 60, 40, 58, 45, 69, 65, 80, 70, 83, 40, + 58, 65, 80, 45, 69, 70, 83, 48, 60, 70, 83, 70, 83, 86, 91, 2, 12, 6, + 15, 6, 15, 13, 20, 12, 38, 15, 40, 51, 56, 52, 58, 12, 38, 51, 56, + 15, 40, 52, 58, 38, 44, 56, 68, 56, 68, 75, 82, 6, 51, 13, 52, 13, 52, + 36, 55, 15, 56, 20, 58, 52, 75, 55, 78, 15, 56, 52, 75, 20, 58, 55, + 78, 40, 68, 58, 82, 58, 82, 78, 90, 4, 13, 7, 16, 13, 17, 16, 21, 13, + 39, 16, 41, 52, 57, 53, 59, 35, 40, 54, 58, 40, 48, 58, 60, 40, 45, + 58, 69, 65, 70, 80, 83, 13, 52, 16, 53, 39, 57, 41, 59, 17, 57, 21, + 59, 57, 76, 59, 79, 40, 65, 58, 80, 45, 70, 69, 83, 48, 70, 60, 83, + 70, 86, 83, 91, 4, 13, 13, 17, 7, 16, 16, 21, 35, 40, 40, 48, 54, 58, + 58, 60, 13, 39, 52, 57, 16, 41, 53, 59, 40, 45, 65, 70, 58, 69, 80, + 83, 13, 52, 39, 57, 16, 53, 41, 59, 40, 65, 45, 70, 58, 80, 69, 83, + 17, 57, 57, 76, 21, 59, 59, 79, 48, 70, 70, 86, 60, 83, 83, 91, 11, + 14, 14, 18, 14, 18, 18, 22, 36, 41, 41, 49, 55, 59, 59, 61, 36, 41, + 55, 59, 41, 49, 59, 61, 42, 46, 66, 71, 66, 71, 81, 84, 36, 55, 41, + 59, 41, 59, 49, 61, 42, 66, 46, 71, 66, 81, 71, 84, 42, 66, 66, 81, + 46, 71, 71, 84, 62, 72, 72, 87, 72, 87, 87, 92, 2, 12, 12, 38, 12, + 38, 38, 44, 6, 15, 15, 40, 51, 56, 56, 68, 6, 15, 51, 56, 15, 40, 56, + 68, 13, 20, 52, 58, 52, 58, 75, 82, 6, 51, 15, 56, 15, 56, 40, 68, + 13, 52, 20, 58, 52, 75, 58, 82, 13, 52, 52, 75, 20, 58, 58, 82, 36, + 55, 55, 78, 55, 78, 78, 90, 4, 13, 13, 39, 35, 40, 40, 45, 7, 16, 16, + 41, 54, 58, 58, 69, 13, 17, 52, 57, 40, 48, 65, 70, 16, 21, 53, 59, + 58, 60, 80, 83, 13, 52, 17, 57, 40, 65, 48, 70, 16, 53, 21, 59, 58, + 80, 60, 83, 39, 57, 57, 76, 45, 70, 70, 86, 41, 59, 59, 79, 69, 83, + 83, 91, 4, 13, 35, 40, 13, 39, 40, 45, 13, 17, 40, 48, 52, 57, 65, + 70, 7, 16, 54, 58, 16, 41, 58, 69, 16, 21, 58, 60, 53, 59, 80, 83, + 13, 52, 40, 65, 17, 57, 48, 70, 39, 57, 45, 70, 57, 76, 70, 86, 16, + 53, 58, 80, 21, 59, 60, 83, 41, 59, 69, 83, 59, 79, 83, 91, 11, 14, + 36, 41, 36, 41, 42, 46, 14, 18, 41, 49, 55, 59, 66, 71, 14, 18, 55, + 59, 41, 49, 66, 71, 18, 22, 59, 61, 59, 61, 81, 84, 36, 55, 42, 66, + 42, 66, 62, 72, 41, 59, 46, 71, 66, 81, 72, 87, 41, 59, 66, 81, 46, + 71, 72, 87, 49, 61, 71, 84, 71, 84, 87, 92, 4, 35, 13, 40, 13, 40, + 39, 45, 13, 40, 17, 48, 52, 65, 57, 70, 13, 40, 52, 65, 17, 48, 57, + 70, 39, 45, 57, 70, 57, 70, 76, 86, 7, 54, 16, 58, 16, 58, 41, 69, + 16, 58, 21, 60, 53, 80, 59, 83, 16, 58, 53, 80, 21, 60, 59, 83, 41, + 69, 59, 83, 59, 83, 79, 91, 11, 36, 14, 41, 36, 42, 41, 46, 14, 41, + 18, 49, 55, 66, 59, 71, 36, 42, 55, 66, 42, 62, 66, 72, 41, 46, 59, + 71, 66, 72, 81, 87, 14, 55, 18, 59, 41, 66, 49, 71, 18, 59, 22, 61, + 59, 81, 61, 84, 41, 66, 59, 81, 46, 72, 71, 87, 49, 71, 61, 84, 71, + 87, 84, 92, 11, 36, 36, 42, 14, 41, 41, 46, 36, 42, 42, 62, 55, 66, + 66, 72, 14, 41, 55, 66, 18, 49, 59, 71, 41, 46, 66, 72, 59, 71, 81, + 87, 14, 55, 41, 66, 18, 59, 49, 71, 41, 66, 46, 72, 59, 81, 71, 87, + 18, 59, 59, 81, 22, 61, 61, 84, 49, 71, 71, 87, 61, 84, 84, 92, 34, + 37, 37, 43, 37, 43, 43, 47, 37, 43, 43, 63, 64, 67, 67, 73, 37, 43, + 64, 67, 43, 63, 67, 73, 43, 47, 67, 73, 67, 73, 85, 88, 37, 64, 43, + 67, 43, 67, 63, 73, 43, 67, 47, 73, 67, 85, 73, 88, 43, 67, 67, 85, + 47, 73, 73, 88, 63, 73, 73, 88, 73, 88, 88, 93, 2, 6, 6, 13, 12, 15, + 15, 20, 6, 13, 13, 36, 51, 52, 52, 55, 12, 15, 51, 52, 38, 40, 56, + 58, 15, 20, 52, 55, 56, 58, 75, 78, 12, 51, 15, 52, 38, 56, 40, 58, + 15, 52, 20, 55, 56, 75, 58, 78, 38, 56, 56, 75, 44, 68, 68, 82, 40, + 58, 58, 78, 68, 82, 82, 90, 3, 7, 7, 14, 19, 20, 20, 25, 7, 14, 14, + 37, 54, 55, 55, 64, 19, 20, 54, 55, 44, 45, 68, 69, 20, 25, 55, 64, + 68, 69, 96, 97, 19, 54, 20, 55, 44, 68, 45, 69, 20, 55, 25, 64, 68, + 96, 69, 97, 44, 68, 68, 96, 120, 121, 121, 122, 45, 69, 69, 97, 121, + 122, 122, 126, 6, 8, 15, 17, 15, 17, 26, 27, 15, 17, 40, 42, 56, 57, + 65, 66, 15, 17, 56, 57, 40, 42, 65, 66, 26, 27, 65, 66, 65, 66, 98, + 99, 51, 74, 56, 75, 56, 75, 65, 80, 56, 75, 68, 96, 104, 108, 105, + 109, 56, 75, 104, 108, 68, 96, 105, 109, 65, 80, 105, 109, 105, 109, + 127, 129, 7, 9, 16, 18, 20, 21, 27, 28, 16, 18, 41, 43, 58, 59, 66, + 67, 20, 21, 58, 59, 45, 46, 70, 71, 27, 28, 66, 67, 70, 71, 100, 101, + 54, 77, 58, 78, 68, 82, 70, 83, 58, 78, 69, 97, 105, 109, 106, 110, + 68, 82, 105, 109, 121, 122, 123, 124, 70, 83, 106, 110, 123, 124, + 128, 130, 6, 15, 8, 17, 15, 26, 17, 27, 15, 40, 17, 42, 56, 65, 57, + 66, 51, 56, 74, 75, 56, 65, 75, 80, 56, 68, 75, 96, 104, 105, 108, + 109, 15, 56, 17, 57, 40, 65, 42, 66, 26, 65, 27, 66, 65, 98, 66, 99, + 56, 104, 75, 108, 68, 105, 96, 109, 65, 105, 80, 109, 105, 127, 109, + 129, 7, 16, 9, 18, 20, 27, 21, 28, 16, 41, 18, 43, 58, 66, 59, 67, + 54, 58, 77, 78, 68, 70, 82, 83, 58, 69, 78, 97, 105, 106, 109, 110, + 20, 58, 21, 59, 45, 70, 46, 71, 27, 66, 28, 67, 70, 100, 71, 101, 68, + 105, 82, 109, 121, 123, 122, 124, 70, 106, 83, 110, 123, 128, 124, + 130, 13, 17, 17, 23, 20, 27, 27, 29, 40, 48, 48, 62, 68, 70, 70, 72, + 52, 57, 75, 76, 58, 66, 80, 81, 65, 70, 98, 100, 105, 106, 111, 112, + 52, 75, 57, 76, 58, 80, 66, 81, 65, 98, 70, 100, 105, 111, 106, 112, + 75, 108, 108, 114, 82, 109, 109, 115, 98, 111, 111, 116, 127, 131, + 131, 133, 14, 18, 18, 24, 25, 28, 28, 30, 41, 49, 49, 63, 69, 71, 71, + 73, 55, 59, 78, 79, 69, 71, 83, 84, 66, 71, 99, 101, 106, 107, 112, + 113, 55, 78, 59, 79, 69, 83, 71, 84, 66, 99, 71, 101, 106, 112, 107, + 113, 96, 109, 109, 115, 122, 124, 124, 125, 100, 112, 112, 117, 128, + 132, 132, 134, 6, 15, 15, 40, 51, 56, 56, 68, 8, 17, 17, 42, 74, 75, + 75, 96, 15, 26, 56, 65, 56, 65, 104, 105, 17, 27, 57, 66, 75, 80, + 108, 109, 15, 56, 26, 65, 56, 104, 65, 105, 17, 57, 27, 66, 75, 108, + 80, 109, 40, 65, 65, 98, 68, 105, 105, 127, 42, 66, 66, 99, 96, 109, + 109, 129, 7, 16, 16, 41, 54, 58, 58, 69, 9, 18, 18, 43, 77, 78, 78, + 97, 20, 27, 58, 66, 68, 70, 105, 106, 21, 28, 59, 67, 82, 83, 109, + 110, 20, 58, 27, 66, 68, 105, 70, 106, 21, 59, 28, 67, 82, 109, 83, + 110, 45, 70, 70, 100, 121, 123, 123, 128, 46, 71, 71, 101, 122, 124, + 124, 130, 13, 17, 40, 48, 52, 57, 65, 70, 17, 23, 48, 62, 75, 76, 98, + 100, 20, 27, 68, 70, 58, 66, 105, 106, 27, 29, 70, 72, 80, 81, 111, + 112, 52, 75, 65, 98, 75, 108, 98, 111, 57, 76, 70, 100, 108, 114, + 111, 116, 58, 80, 105, 111, 82, 109, 127, 131, 66, 81, 106, 112, 109, + 115, 131, 133, 14, 18, 41, 49, 55, 59, 66, 71, 18, 24, 49, 63, 78, + 79, 99, 101, 25, 28, 69, 71, 69, 71, 106, 107, 28, 30, 71, 73, 83, + 84, 112, 113, 55, 78, 66, 99, 96, 109, 100, 112, 59, 79, 71, 101, + 109, 115, 112, 117, 69, 83, 106, 112, 122, 124, 128, 132, 71, 84, + 107, 113, 124, 125, 132, 134, 13, 40, 17, 48, 52, 65, 57, 70, 17, 48, + 23, 62, 75, 98, 76, 100, 52, 65, 75, 98, 75, 98, 108, 111, 57, 70, + 76, 100, 108, 111, 114, 116, 20, 68, 27, 70, 58, 105, 66, 106, 27, + 70, 29, 72, 80, 111, 81, 112, 58, 105, 80, 111, 82, 127, 109, 131, + 66, 106, 81, 112, 109, 131, 115, 133, 14, 41, 18, 49, 55, 66, 59, 71, + 18, 49, 24, 63, 78, 99, 79, 101, 55, 66, 78, 99, 96, 100, 109, 112, + 59, 71, 79, 101, 109, 112, 115, 117, 25, 69, 28, 71, 69, 106, 71, + 107, 28, 71, 30, 73, 83, 112, 84, 113, 69, 106, 83, 112, 122, 128, + 124, 132, 71, 107, 84, 113, 124, 132, 125, 134, 36, 42, 42, 62, 55, + 66, 66, 72, 42, 62, 62, 94, 96, 100, 100, 102, 55, 66, 96, 100, 78, + 99, 109, 112, 66, 72, 100, 102, 109, 112, 116, 118, 55, 96, 66, 100, + 78, 109, 99, 112, 66, 100, 72, 102, 109, 116, 112, 118, 78, 109, 109, + 116, 90, 129, 129, 133, 99, 112, 112, 118, 129, 133, 133, 135, 37, + 43, 43, 63, 64, 67, 67, 73, 43, 63, 63, 95, 97, 101, 101, 103, 64, + 67, 97, 101, 97, 101, 110, 113, 67, 73, 101, 103, 110, 113, 117, 119, + 64, 97, 67, 101, 97, 110, 101, 113, 67, 101, 73, 103, 110, 117, 113, + 119, 97, 110, 110, 117, 126, 130, 130, 134, 101, 113, 113, 119, 130, + 134, 134, 136, 2, 6, 12, 15, 6, 13, 15, 20, 12, 15, 38, 40, 51, 52, + 56, 58, 6, 13, 51, 52, 13, 36, 52, 55, 15, 20, 56, 58, 52, 55, 75, + 78, 12, 51, 38, 56, 15, 52, 40, 58, 38, 56, 44, 68, 56, 75, 68, 82, + 15, 52, 56, 75, 20, 55, 58, 78, 40, 58, 68, 82, 58, 78, 82, 90, 6, 8, + 15, 17, 15, 17, 26, 27, 15, 17, 40, 42, 56, 57, 65, 66, 15, 17, 56, + 57, 40, 42, 65, 66, 26, 27, 65, 66, 65, 66, 98, 99, 51, 74, 56, 75, + 56, 75, 65, 80, 56, 75, 68, 96, 104, 108, 105, 109, 56, 75, 104, 108, + 68, 96, 105, 109, 65, 80, 105, 109, 105, 109, 127, 129, 3, 7, 19, 20, + 7, 14, 20, 25, 19, 20, 44, 45, 54, 55, 68, 69, 7, 14, 54, 55, 14, 37, + 55, 64, 20, 25, 68, 69, 55, 64, 96, 97, 19, 54, 44, 68, 20, 55, 45, + 69, 44, 68, 120, 121, 68, 96, 121, 122, 20, 55, 68, 96, 25, 64, 69, + 97, 45, 69, 121, 122, 69, 97, 122, 126, 7, 9, 20, 21, 16, 18, 27, 28, + 20, 21, 45, 46, 58, 59, 70, 71, 16, 18, 58, 59, 41, 43, 66, 67, 27, + 28, 70, 71, 66, 67, 100, 101, 54, 77, 68, 82, 58, 78, 70, 83, 68, 82, + 121, 122, 105, 109, 123, 124, 58, 78, 105, 109, 69, 97, 106, 110, 70, + 83, 123, 124, 106, 110, 128, 130, 6, 15, 15, 26, 8, 17, 17, 27, 51, + 56, 56, 65, 74, 75, 75, 80, 15, 40, 56, 65, 17, 42, 57, 66, 56, 68, + 104, 105, 75, 96, 108, 109, 15, 56, 40, 65, 17, 57, 42, 66, 56, 104, + 68, 105, 75, 108, 96, 109, 26, 65, 65, 98, 27, 66, 66, 99, 65, 105, + 105, 127, 80, 109, 109, 129, 13, 17, 20, 27, 17, 23, 27, 29, 52, 57, + 58, 66, 75, 76, 80, 81, 40, 48, 68, 70, 48, 62, 70, 72, 65, 70, 105, + 106, 98, 100, 111, 112, 52, 75, 58, 80, 57, 76, 66, 81, 75, 108, 82, + 109, 108, 114, 109, 115, 65, 98, 105, 111, 70, 100, 106, 112, 98, + 111, 127, 131, 111, 116, 131, 133, 7, 16, 20, 27, 9, 18, 21, 28, 54, + 58, 68, 70, 77, 78, 82, 83, 16, 41, 58, 66, 18, 43, 59, 67, 58, 69, + 105, 106, 78, 97, 109, 110, 20, 58, 45, 70, 21, 59, 46, 71, 68, 105, + 121, 123, 82, 109, 122, 124, 27, 66, 70, 100, 28, 67, 71, 101, 70, + 106, 123, 128, 83, 110, 124, 130, 14, 18, 25, 28, 18, 24, 28, 30, 55, + 59, 69, 71, 78, 79, 83, 84, 41, 49, 69, 71, 49, 63, 71, 73, 66, 71, + 106, 107, 99, 101, 112, 113, 55, 78, 69, 83, 59, 79, 71, 84, 96, 109, + 122, 124, 109, 115, 124, 125, 66, 99, 106, 112, 71, 101, 107, 113, + 100, 112, 128, 132, 112, 117, 132, 134, 6, 15, 51, 56, 15, 40, 56, + 68, 15, 26, 56, 65, 56, 65, 104, 105, 8, 17, 74, 75, 17, 42, 75, 96, + 17, 27, 75, 80, 57, 66, 108, 109, 15, 56, 56, 104, 26, 65, 65, 105, + 40, 65, 68, 105, 65, 98, 105, 127, 17, 57, 75, 108, 27, 66, 80, 109, + 42, 66, 96, 109, 66, 99, 109, 129, 13, 17, 52, 57, 40, 48, 65, 70, + 20, 27, 58, 66, 68, 70, 105, 106, 17, 23, 75, 76, 48, 62, 98, 100, + 27, 29, 80, 81, 70, 72, 111, 112, 52, 75, 75, 108, 65, 98, 98, 111, + 58, 80, 82, 109, 105, 111, 127, 131, 57, 76, 108, 114, 70, 100, 111, + 116, 66, 81, 109, 115, 106, 112, 131, 133, 7, 16, 54, 58, 16, 41, 58, + 69, 20, 27, 68, 70, 58, 66, 105, 106, 9, 18, 77, 78, 18, 43, 78, 97, + 21, 28, 82, 83, 59, 67, 109, 110, 20, 58, 68, 105, 27, 66, 70, 106, + 45, 70, 121, 123, 70, 100, 123, 128, 21, 59, 82, 109, 28, 67, 83, + 110, 46, 71, 122, 124, 71, 101, 124, 130, 14, 18, 55, 59, 41, 49, 66, + 71, 25, 28, 69, 71, 69, 71, 106, 107, 18, 24, 78, 79, 49, 63, 99, + 101, 28, 30, 83, 84, 71, 73, 112, 113, 55, 78, 96, 109, 66, 99, 100, + 112, 69, 83, 122, 124, 106, 112, 128, 132, 59, 79, 109, 115, 71, 101, + 112, 117, 71, 84, 124, 125, 107, 113, 132, 134, 13, 40, 52, 65, 17, + 48, 57, 70, 52, 65, 75, 98, 75, 98, 108, 111, 17, 48, 75, 98, 23, 62, + 76, 100, 57, 70, 108, 111, 76, 100, 114, 116, 20, 68, 58, 105, 27, + 70, 66, 106, 58, 105, 82, 127, 80, 111, 109, 131, 27, 70, 80, 111, + 29, 72, 81, 112, 66, 106, 109, 131, 81, 112, 115, 133, 36, 42, 55, + 66, 42, 62, 66, 72, 55, 66, 78, 99, 96, 100, 109, 112, 42, 62, 96, + 100, 62, 94, 100, 102, 66, 72, 109, 112, 100, 102, 116, 118, 55, 96, + 78, 109, 66, 100, 99, 112, 78, 109, 90, 129, 109, 116, 129, 133, 66, + 100, 109, 116, 72, 102, 112, 118, 99, 112, 129, 133, 112, 118, 133, + 135, 14, 41, 55, 66, 18, 49, 59, 71, 55, 66, 96, 100, 78, 99, 109, + 112, 18, 49, 78, 99, 24, 63, 79, 101, 59, 71, 109, 112, 79, 101, 115, + 117, 25, 69, 69, 106, 28, 71, 71, 107, 69, 106, 122, 128, 83, 112, + 124, 132, 28, 71, 83, 112, 30, 73, 84, 113, 71, 107, 124, 132, 84, + 113, 125, 134, 37, 43, 64, 67, 43, 63, 67, 73, 64, 67, 97, 101, 97, + 101, 110, 113, 43, 63, 97, 101, 63, 95, 101, 103, 67, 73, 110, 113, + 101, 103, 117, 119, 64, 97, 97, 110, 67, 101, 101, 113, 97, 110, 126, + 130, 110, 117, 130, 134, 67, 101, 110, 117, 73, 103, 113, 119, 101, + 113, 130, 134, 113, 119, 134, 136, 4, 7, 13, 16, 13, 16, 17, 21, 13, + 16, 39, 41, 52, 53, 57, 59, 13, 16, 52, 53, 39, 41, 57, 59, 17, 21, + 57, 59, 57, 59, 76, 79, 35, 54, 40, 58, 40, 58, 48, 60, 40, 58, 45, + 69, 65, 80, 70, 83, 40, 58, 65, 80, 45, 69, 70, 83, 48, 60, 70, 83, + 70, 83, 86, 91, 7, 9, 16, 18, 20, 21, 27, 28, 16, 18, 41, 43, 58, 59, + 66, 67, 20, 21, 58, 59, 45, 46, 70, 71, 27, 28, 66, 67, 70, 71, 100, + 101, 54, 77, 58, 78, 68, 82, 70, 83, 58, 78, 69, 97, 105, 109, 106, + 110, 68, 82, 105, 109, 121, 122, 123, 124, 70, 83, 106, 110, 123, + 124, 128, 130, 7, 9, 20, 21, 16, 18, 27, 28, 20, 21, 45, 46, 58, 59, + 70, 71, 16, 18, 58, 59, 41, 43, 66, 67, 27, 28, 70, 71, 66, 67, 100, + 101, 54, 77, 68, 82, 58, 78, 70, 83, 68, 82, 121, 122, 105, 109, 123, + 124, 58, 78, 105, 109, 69, 97, 106, 110, 70, 83, 123, 124, 106, 110, + 128, 130, 9, 10, 21, 22, 21, 22, 29, 30, 21, 22, 46, 47, 60, 61, 72, + 73, 21, 22, 60, 61, 46, 47, 72, 73, 29, 30, 72, 73, 72, 73, 102, 103, + 77, 89, 82, 90, 82, 90, 86, 91, 82, 90, 122, 126, 127, 129, 128, 130, + 82, 90, 127, 129, 122, 126, 128, 130, 86, 91, 128, 130, 128, 130, + 141, 142, 13, 20, 17, 27, 17, 27, 23, 29, 52, 58, 57, 66, 75, 80, 76, + 81, 52, 58, 75, 80, 57, 66, 76, 81, 75, 82, 108, 109, 108, 109, 114, + 115, 40, 68, 48, 70, 48, 70, 62, 72, 65, 105, 70, 106, 98, 111, 100, + 112, 65, 105, 98, 111, 70, 106, 100, 112, 98, 127, 111, 131, 111, + 131, 116, 133, 16, 21, 21, 28, 27, 29, 29, 31, 53, 59, 59, 67, 80, + 81, 81, 85, 58, 60, 82, 83, 70, 72, 86, 87, 80, 83, 109, 110, 111, + 112, 116, 117, 58, 82, 60, 83, 70, 86, 72, 87, 80, 109, 83, 110, 111, + 116, 112, 117, 105, 127, 127, 131, 123, 128, 128, 132, 111, 131, 131, + 143, 144, 145, 145, 146, 16, 21, 27, 29, 21, 28, 29, 31, 58, 60, 70, + 72, 82, 83, 86, 87, 53, 59, 80, 81, 59, 67, 81, 85, 80, 83, 111, 112, + 109, 110, 116, 117, 58, 82, 70, 86, 60, 83, 72, 87, 105, 127, 123, + 128, 127, 131, 128, 132, 80, 109, 111, 116, 83, 110, 112, 117, 111, + 131, 144, 145, 131, 143, 145, 146, 18, 22, 28, 30, 28, 30, 31, 32, + 59, 61, 71, 73, 83, 84, 87, 88, 59, 61, 83, 84, 71, 73, 87, 88, 81, + 84, 112, 113, 112, 113, 118, 119, 78, 90, 83, 91, 83, 91, 87, 92, + 109, 129, 124, 130, 131, 133, 132, 134, 109, 129, 131, 133, 124, 130, + 132, 134, 116, 133, 145, 146, 145, 146, 147, 148, 13, 20, 52, 58, 52, + 58, 75, 82, 17, 27, 57, 66, 75, 80, 108, 109, 17, 27, 75, 80, 57, 66, + 108, 109, 23, 29, 76, 81, 76, 81, 114, 115, 40, 68, 65, 105, 65, 105, + 98, 127, 48, 70, 70, 106, 98, 111, 111, 131, 48, 70, 98, 111, 70, + 106, 111, 131, 62, 72, 100, 112, 100, 112, 116, 133, 16, 21, 53, 59, + 58, 60, 80, 83, 21, 28, 59, 67, 82, 83, 109, 110, 27, 29, 80, 81, 70, + 72, 111, 112, 29, 31, 81, 85, 86, 87, 116, 117, 58, 82, 80, 109, 105, + 127, 111, 131, 60, 83, 83, 110, 127, 131, 131, 143, 70, 86, 111, 116, + 123, 128, 144, 145, 72, 87, 112, 117, 128, 132, 145, 146, 16, 21, 58, + 60, 53, 59, 80, 83, 27, 29, 70, 72, 80, 81, 111, 112, 21, 28, 82, 83, + 59, 67, 109, 110, 29, 31, 86, 87, 81, 85, 116, 117, 58, 82, 105, 127, + 80, 109, 111, 131, 70, 86, 123, 128, 111, 116, 144, 145, 60, 83, 127, + 131, 83, 110, 131, 143, 72, 87, 128, 132, 112, 117, 145, 146, 18, 22, + 59, 61, 59, 61, 81, 84, 28, 30, 71, 73, 83, 84, 112, 113, 28, 30, 83, + 84, 71, 73, 112, 113, 31, 32, 87, 88, 87, 88, 118, 119, 78, 90, 109, + 129, 109, 129, 116, 133, 83, 91, 124, 130, 131, 133, 145, 146, 83, + 91, 131, 133, 124, 130, 145, 146, 87, 92, 132, 134, 132, 134, 147, + 148, 39, 45, 57, 70, 57, 70, 76, 86, 57, 70, 76, 100, 108, 111, 114, + 116, 57, 70, 108, 111, 76, 100, 114, 116, 76, 86, 114, 116, 114, 116, + 137, 138, 45, 121, 70, 123, 70, 123, 100, 128, 70, 123, 86, 128, 111, + 144, 116, 145, 70, 123, 111, 144, 86, 128, 116, 145, 100, 128, 116, + 145, 116, 145, 138, 149, 41, 46, 59, 71, 66, 72, 81, 87, 59, 71, 79, + 101, 109, 112, 115, 117, 66, 72, 109, 112, 100, 102, 116, 118, 81, + 87, 115, 117, 116, 118, 138, 139, 69, 122, 83, 124, 106, 128, 112, + 132, 83, 124, 91, 130, 131, 145, 133, 146, 106, 128, 131, 145, 128, + 141, 145, 147, 112, 132, 133, 146, 145, 147, 149, 150, 41, 46, 66, + 72, 59, 71, 81, 87, 66, 72, 100, 102, 109, 112, 116, 118, 59, 71, + 109, 112, 79, 101, 115, 117, 81, 87, 116, 118, 115, 117, 138, 139, + 69, 122, 106, 128, 83, 124, 112, 132, 106, 128, 128, 141, 131, 145, + 145, 147, 83, 124, 131, 145, 91, 130, 133, 146, 112, 132, 145, 147, + 133, 146, 149, 150, 43, 47, 67, 73, 67, 73, 85, 88, 67, 73, 101, 103, + 110, 113, 117, 119, 67, 73, 110, 113, 101, 103, 117, 119, 85, 88, + 117, 119, 117, 119, 139, 140, 97, 126, 110, 130, 110, 130, 117, 134, + 110, 130, 130, 142, 143, 146, 146, 148, 110, 130, 143, 146, 130, 142, + 146, 148, 117, 134, 146, 148, 146, 148, 150, 151, 2, 12, 6, 15, 6, + 15, 13, 20, 12, 38, 15, 40, 51, 56, 52, 58, 12, 38, 51, 56, 15, 40, + 52, 58, 38, 44, 56, 68, 56, 68, 75, 82, 6, 51, 13, 52, 13, 52, 36, + 55, 15, 56, 20, 58, 52, 75, 55, 78, 15, 56, 52, 75, 20, 58, 55, 78, + 40, 68, 58, 82, 58, 82, 78, 90, 6, 15, 8, 17, 15, 26, 17, 27, 15, 40, + 17, 42, 56, 65, 57, 66, 51, 56, 74, 75, 56, 65, 75, 80, 56, 68, 75, + 96, 104, 105, 108, 109, 15, 56, 17, 57, 40, 65, 42, 66, 26, 65, 27, + 66, 65, 98, 66, 99, 56, 104, 75, 108, 68, 105, 96, 109, 65, 105, 80, + 109, 105, 127, 109, 129, 6, 15, 15, 26, 8, 17, 17, 27, 51, 56, 56, + 65, 74, 75, 75, 80, 15, 40, 56, 65, 17, 42, 57, 66, 56, 68, 104, 105, + 75, 96, 108, 109, 15, 56, 40, 65, 17, 57, 42, 66, 56, 104, 68, 105, + 75, 108, 96, 109, 26, 65, 65, 98, 27, 66, 66, 99, 65, 105, 105, 127, + 80, 109, 109, 129, 13, 20, 17, 27, 17, 27, 23, 29, 52, 58, 57, 66, + 75, 80, 76, 81, 52, 58, 75, 80, 57, 66, 76, 81, 75, 82, 108, 109, + 108, 109, 114, 115, 40, 68, 48, 70, 48, 70, 62, 72, 65, 105, 70, 106, + 98, 111, 100, 112, 65, 105, 98, 111, 70, 106, 100, 112, 98, 127, 111, + 131, 111, 131, 116, 133, 3, 19, 7, 20, 7, 20, 14, 25, 19, 44, 20, 45, + 54, 68, 55, 69, 19, 44, 54, 68, 20, 45, 55, 69, 44, 120, 68, 121, 68, + 121, 96, 122, 7, 54, 14, 55, 14, 55, 37, 64, 20, 68, 25, 69, 55, 96, + 64, 97, 20, 68, 55, 96, 25, 69, 64, 97, 45, 121, 69, 122, 69, 122, + 97, 126, 7, 20, 9, 21, 16, 27, 18, 28, 20, 45, 21, 46, 58, 70, 59, + 71, 54, 68, 77, 82, 58, 70, 78, 83, 68, 121, 82, 122, 105, 123, 109, + 124, 16, 58, 18, 59, 41, 66, 43, 67, 27, 70, 28, 71, 66, 100, 67, + 101, 58, 105, 78, 109, 69, 106, 97, 110, 70, 123, 83, 124, 106, 128, + 110, 130, 7, 20, 16, 27, 9, 21, 18, 28, 54, 68, 58, 70, 77, 82, 78, + 83, 20, 45, 58, 70, 21, 46, 59, 71, 68, 121, 105, 123, 82, 122, 109, + 124, 16, 58, 41, 66, 18, 59, 43, 67, 58, 105, 69, 106, 78, 109, 97, + 110, 27, 70, 66, 100, 28, 71, 67, 101, 70, 123, 106, 128, 83, 124, + 110, 130, 14, 25, 18, 28, 18, 28, 24, 30, 55, 69, 59, 71, 78, 83, 79, + 84, 55, 69, 78, 83, 59, 71, 79, 84, 96, 122, 109, 124, 109, 124, 115, + 125, 41, 69, 49, 71, 49, 71, 63, 73, 66, 106, 71, 107, 99, 112, 101, + 113, 66, 106, 99, 112, 71, 107, 101, 113, 100, 128, 112, 132, 112, + 132, 117, 134, 6, 51, 15, 56, 15, 56, 40, 68, 15, 56, 26, 65, 56, + 104, 65, 105, 15, 56, 56, 104, 26, 65, 65, 105, 40, 68, 65, 105, 65, + 105, 98, 127, 8, 74, 17, 75, 17, 75, 42, 96, 17, 75, 27, 80, 57, 108, + 66, 109, 17, 75, 57, 108, 27, 80, 66, 109, 42, 96, 66, 109, 66, 109, + 99, 129, 13, 52, 17, 57, 40, 65, 48, 70, 20, 58, 27, 66, 68, 105, 70, + 106, 52, 75, 75, 108, 65, 98, 98, 111, 58, 82, 80, 109, 105, 127, + 111, 131, 17, 75, 23, 76, 48, 98, 62, 100, 27, 80, 29, 81, 70, 111, + 72, 112, 57, 108, 76, 114, 70, 111, 100, 116, 66, 109, 81, 115, 106, + 131, 112, 133, 13, 52, 40, 65, 17, 57, 48, 70, 52, 75, 65, 98, 75, + 108, 98, 111, 20, 58, 68, 105, 27, 66, 70, 106, 58, 82, 105, 127, 80, + 109, 111, 131, 17, 75, 48, 98, 23, 76, 62, 100, 57, 108, 70, 111, 76, + 114, 100, 116, 27, 80, 70, 111, 29, 81, 72, 112, 66, 109, 106, 131, + 81, 115, 112, 133, 36, 55, 42, 66, 42, 66, 62, 72, 55, 78, 66, 99, + 96, 109, 100, 112, 55, 78, 96, 109, 66, 99, 100, 112, 78, 90, 109, + 129, 109, 129, 116, 133, 42, 96, 62, 100, 62, 100, 94, 102, 66, 109, + 72, 112, 100, 116, 102, 118, 66, 109, 100, 116, 72, 112, 102, 118, + 99, 129, 112, 133, 112, 133, 118, 135, 7, 54, 16, 58, 16, 58, 41, 69, + 20, 68, 27, 70, 58, 105, 66, 106, 20, 68, 58, 105, 27, 70, 66, 106, + 45, 121, 70, 123, 70, 123, 100, 128, 9, 77, 18, 78, 18, 78, 43, 97, + 21, 82, 28, 83, 59, 109, 67, 110, 21, 82, 59, 109, 28, 83, 67, 110, + 46, 122, 71, 124, 71, 124, 101, 130, 14, 55, 18, 59, 41, 66, 49, 71, + 25, 69, 28, 71, 69, 106, 71, 107, 55, 96, 78, 109, 66, 100, 99, 112, + 69, 122, 83, 124, 106, 128, 112, 132, 18, 78, 24, 79, 49, 99, 63, + 101, 28, 83, 30, 84, 71, 112, 73, 113, 59, 109, 79, 115, 71, 112, + 101, 117, 71, 124, 84, 125, 107, 132, 113, 134, 14, 55, 41, 66, 18, + 59, 49, 71, 55, 96, 66, 100, 78, 109, 99, 112, 25, 69, 69, 106, 28, + 71, 71, 107, 69, 122, 106, 128, 83, 124, 112, 132, 18, 78, 49, 99, + 24, 79, 63, 101, 59, 109, 71, 112, 79, 115, 101, 117, 28, 83, 71, + 112, 30, 84, 73, 113, 71, 124, 107, 132, 84, 125, 113, 134, 37, 64, + 43, 67, 43, 67, 63, 73, 64, 97, 67, 101, 97, 110, 101, 113, 64, 97, + 97, 110, 67, 101, 101, 113, 97, 126, 110, 130, 110, 130, 117, 134, + 43, 97, 63, 101, 63, 101, 95, 103, 67, 110, 73, 113, 101, 117, 103, + 119, 67, 110, 101, 117, 73, 113, 103, 119, 101, 130, 113, 134, 113, + 134, 119, 136, 4, 13, 7, 16, 13, 17, 16, 21, 13, 39, 16, 41, 52, 57, + 53, 59, 35, 40, 54, 58, 40, 48, 58, 60, 40, 45, 58, 69, 65, 70, 80, + 83, 13, 52, 16, 53, 39, 57, 41, 59, 17, 57, 21, 59, 57, 76, 59, 79, + 40, 65, 58, 80, 45, 70, 69, 83, 48, 70, 60, 83, 70, 86, 83, 91, 7, + 16, 9, 18, 20, 27, 21, 28, 16, 41, 18, 43, 58, 66, 59, 67, 54, 58, + 77, 78, 68, 70, 82, 83, 58, 69, 78, 97, 105, 106, 109, 110, 20, 58, + 21, 59, 45, 70, 46, 71, 27, 66, 28, 67, 70, 100, 71, 101, 68, 105, + 82, 109, 121, 123, 122, 124, 70, 106, 83, 110, 123, 128, 124, 130, + 13, 17, 20, 27, 17, 23, 27, 29, 52, 57, 58, 66, 75, 76, 80, 81, 40, + 48, 68, 70, 48, 62, 70, 72, 65, 70, 105, 106, 98, 100, 111, 112, 52, + 75, 58, 80, 57, 76, 66, 81, 75, 108, 82, 109, 108, 114, 109, 115, 65, + 98, 105, 111, 70, 100, 106, 112, 98, 111, 127, 131, 111, 116, 131, + 133, 16, 21, 21, 28, 27, 29, 29, 31, 53, 59, 59, 67, 80, 81, 81, 85, + 58, 60, 82, 83, 70, 72, 86, 87, 80, 83, 109, 110, 111, 112, 116, 117, + 58, 82, 60, 83, 70, 86, 72, 87, 80, 109, 83, 110, 111, 116, 112, 117, + 105, 127, 127, 131, 123, 128, 128, 132, 111, 131, 131, 143, 144, 145, + 145, 146, 7, 20, 9, 21, 16, 27, 18, 28, 20, 45, 21, 46, 58, 70, 59, + 71, 54, 68, 77, 82, 58, 70, 78, 83, 68, 121, 82, 122, 105, 123, 109, + 124, 16, 58, 18, 59, 41, 66, 43, 67, 27, 70, 28, 71, 66, 100, 67, + 101, 58, 105, 78, 109, 69, 106, 97, 110, 70, 123, 83, 124, 106, 128, + 110, 130, 9, 21, 10, 22, 21, 29, 22, 30, 21, 46, 22, 47, 60, 72, 61, + 73, 77, 82, 89, 90, 82, 86, 90, 91, 82, 122, 90, 126, 127, 128, 129, + 130, 21, 60, 22, 61, 46, 72, 47, 73, 29, 72, 30, 73, 72, 102, 73, + 103, 82, 127, 90, 129, 122, 128, 126, 130, 86, 128, 91, 130, 128, + 141, 130, 142, 16, 27, 21, 29, 21, 29, 28, 31, 58, 70, 60, 72, 82, + 86, 83, 87, 58, 70, 82, 86, 60, 72, 83, 87, 105, 123, 127, 128, 127, + 128, 131, 132, 53, 80, 59, 81, 59, 81, 67, 85, 80, 111, 83, 112, 109, + 116, 110, 117, 80, 111, 109, 116, 83, 112, 110, 117, 111, 144, 131, + 145, 131, 145, 143, 146, 18, 28, 22, 30, 28, 31, 30, 32, 59, 71, 61, + 73, 83, 87, 84, 88, 78, 83, 90, 91, 83, 87, 91, 92, 109, 124, 129, + 130, 131, 132, 133, 134, 59, 83, 61, 84, 71, 87, 73, 88, 81, 112, 84, + 113, 112, 118, 113, 119, 109, 131, 129, 133, 124, 132, 130, 134, 116, + 145, 133, 146, 145, 147, 146, 148, 13, 52, 20, 58, 52, 75, 58, 82, + 17, 57, 27, 66, 75, 108, 80, 109, 40, 65, 68, 105, 65, 98, 105, 127, + 48, 70, 70, 106, 98, 111, 111, 131, 17, 75, 27, 80, 57, 108, 66, 109, + 23, 76, 29, 81, 76, 114, 81, 115, 48, 98, 70, 111, 70, 111, 106, 131, + 62, 100, 72, 112, 100, 116, 112, 133, 16, 53, 21, 59, 58, 80, 60, 83, + 21, 59, 28, 67, 82, 109, 83, 110, 58, 80, 82, 109, 105, 111, 127, + 131, 60, 83, 83, 110, 127, 131, 131, 143, 27, 80, 29, 81, 70, 111, + 72, 112, 29, 81, 31, 85, 86, 116, 87, 117, 70, 111, 86, 116, 123, + 144, 128, 145, 72, 112, 87, 117, 128, 145, 132, 146, 39, 57, 45, 70, + 57, 76, 70, 86, 57, 76, 70, 100, 108, 114, 111, 116, 45, 70, 121, + 123, 70, 100, 123, 128, 70, 86, 123, 128, 111, 116, 144, 145, 57, + 108, 70, 111, 76, 114, 100, 116, 76, 114, 86, 116, 114, 137, 116, + 138, 70, 111, 123, 144, 86, 116, 128, 145, 100, 116, 128, 145, 116, + 138, 145, 149, 41, 59, 46, 71, 66, 81, 72, 87, 59, 79, 71, 101, 109, + 115, 112, 117, 69, 83, 122, 124, 106, 112, 128, 132, 83, 91, 124, + 130, 131, 133, 145, 146, 66, 109, 72, 112, 100, 116, 102, 118, 81, + 115, 87, 117, 116, 138, 118, 139, 106, 131, 128, 145, 128, 145, 141, + 147, 112, 133, 132, 146, 145, 149, 147, 150, 16, 58, 21, 60, 53, 80, + 59, 83, 27, 70, 29, 72, 80, 111, 81, 112, 58, 105, 82, 127, 80, 111, + 109, 131, 70, 123, 86, 128, 111, 144, 116, 145, 21, 82, 28, 83, 59, + 109, 67, 110, 29, 86, 31, 87, 81, 116, 85, 117, 60, 127, 83, 131, 83, + 131, 110, 143, 72, 128, 87, 132, 112, 145, 117, 146, 18, 59, 22, 61, + 59, 81, 61, 84, 28, 71, 30, 73, 83, 112, 84, 113, 78, 109, 90, 129, + 109, 116, 129, 133, 83, 124, 91, 130, 131, 145, 133, 146, 28, 83, 30, + 84, 71, 112, 73, 113, 31, 87, 32, 88, 87, 118, 88, 119, 83, 131, 91, + 133, 124, 145, 130, 146, 87, 132, 92, 134, 132, 147, 134, 148, 41, + 66, 46, 72, 59, 81, 71, 87, 66, 100, 72, 102, 109, 116, 112, 118, 69, + 106, 122, 128, 83, 112, 124, 132, 106, 128, 128, 141, 131, 145, 145, + 147, 59, 109, 71, 112, 79, 115, 101, 117, 81, 116, 87, 118, 115, 138, + 117, 139, 83, 131, 124, 145, 91, 133, 130, 146, 112, 145, 132, 147, + 133, 149, 146, 150, 43, 67, 47, 73, 67, 85, 73, 88, 67, 101, 73, 103, + 110, 117, 113, 119, 97, 110, 126, 130, 110, 117, 130, 134, 110, 130, + 130, 142, 143, 146, 146, 148, 67, 110, 73, 113, 101, 117, 103, 119, + 85, 117, 88, 119, 117, 139, 119, 140, 110, 143, 130, 146, 130, 146, + 142, 148, 117, 146, 134, 148, 146, 150, 148, 151, 4, 13, 13, 17, 7, + 16, 16, 21, 35, 40, 40, 48, 54, 58, 58, 60, 13, 39, 52, 57, 16, 41, + 53, 59, 40, 45, 65, 70, 58, 69, 80, 83, 13, 52, 39, 57, 16, 53, 41, + 59, 40, 65, 45, 70, 58, 80, 69, 83, 17, 57, 57, 76, 21, 59, 59, 79, + 48, 70, 70, 86, 60, 83, 83, 91, 13, 17, 17, 23, 20, 27, 27, 29, 40, + 48, 48, 62, 68, 70, 70, 72, 52, 57, 75, 76, 58, 66, 80, 81, 65, 70, + 98, 100, 105, 106, 111, 112, 52, 75, 57, 76, 58, 80, 66, 81, 65, 98, + 70, 100, 105, 111, 106, 112, 75, 108, 108, 114, 82, 109, 109, 115, + 98, 111, 111, 116, 127, 131, 131, 133, 7, 16, 20, 27, 9, 18, 21, 28, + 54, 58, 68, 70, 77, 78, 82, 83, 16, 41, 58, 66, 18, 43, 59, 67, 58, + 69, 105, 106, 78, 97, 109, 110, 20, 58, 45, 70, 21, 59, 46, 71, 68, + 105, 121, 123, 82, 109, 122, 124, 27, 66, 70, 100, 28, 67, 71, 101, + 70, 106, 123, 128, 83, 110, 124, 130, 16, 21, 27, 29, 21, 28, 29, 31, + 58, 60, 70, 72, 82, 83, 86, 87, 53, 59, 80, 81, 59, 67, 81, 85, 80, + 83, 111, 112, 109, 110, 116, 117, 58, 82, 70, 86, 60, 83, 72, 87, + 105, 127, 123, 128, 127, 131, 128, 132, 80, 109, 111, 116, 83, 110, + 112, 117, 111, 131, 144, 145, 131, 143, 145, 146, 7, 20, 16, 27, 9, + 21, 18, 28, 54, 68, 58, 70, 77, 82, 78, 83, 20, 45, 58, 70, 21, 46, + 59, 71, 68, 121, 105, 123, 82, 122, 109, 124, 16, 58, 41, 66, 18, 59, + 43, 67, 58, 105, 69, 106, 78, 109, 97, 110, 27, 70, 66, 100, 28, 71, + 67, 101, 70, 123, 106, 128, 83, 124, 110, 130, 16, 27, 21, 29, 21, + 29, 28, 31, 58, 70, 60, 72, 82, 86, 83, 87, 58, 70, 82, 86, 60, 72, + 83, 87, 105, 123, 127, 128, 127, 128, 131, 132, 53, 80, 59, 81, 59, + 81, 67, 85, 80, 111, 83, 112, 109, 116, 110, 117, 80, 111, 109, 116, + 83, 112, 110, 117, 111, 144, 131, 145, 131, 145, 143, 146, 9, 21, 21, + 29, 10, 22, 22, 30, 77, 82, 82, 86, 89, 90, 90, 91, 21, 46, 60, 72, + 22, 47, 61, 73, 82, 122, 127, 128, 90, 126, 129, 130, 21, 60, 46, 72, + 22, 61, 47, 73, 82, 127, 122, 128, 90, 129, 126, 130, 29, 72, 72, + 102, 30, 73, 73, 103, 86, 128, 128, 141, 91, 130, 130, 142, 18, 28, + 28, 31, 22, 30, 30, 32, 78, 83, 83, 87, 90, 91, 91, 92, 59, 71, 83, + 87, 61, 73, 84, 88, 109, 124, 131, 132, 129, 130, 133, 134, 59, 83, + 71, 87, 61, 84, 73, 88, 109, 131, 124, 132, 129, 133, 130, 134, 81, + 112, 112, 118, 84, 113, 113, 119, 116, 145, 145, 147, 133, 146, 146, + 148, 13, 52, 52, 75, 20, 58, 58, 82, 40, 65, 65, 98, 68, 105, 105, + 127, 17, 57, 75, 108, 27, 66, 80, 109, 48, 70, 98, 111, 70, 106, 111, + 131, 17, 75, 57, 108, 27, 80, 66, 109, 48, 98, 70, 111, 70, 111, 106, + 131, 23, 76, 76, 114, 29, 81, 81, 115, 62, 100, 100, 116, 72, 112, + 112, 133, 39, 57, 57, 76, 45, 70, 70, 86, 45, 70, 70, 100, 121, 123, + 123, 128, 57, 76, 108, 114, 70, 100, 111, 116, 70, 86, 111, 116, 123, + 128, 144, 145, 57, 108, 76, 114, 70, 111, 100, 116, 70, 111, 86, 116, + 123, 144, 128, 145, 76, 114, 114, 137, 86, 116, 116, 138, 100, 116, + 116, 138, 128, 145, 145, 149, 16, 53, 58, 80, 21, 59, 60, 83, 58, 80, + 105, 111, 82, 109, 127, 131, 21, 59, 82, 109, 28, 67, 83, 110, 60, + 83, 127, 131, 83, 110, 131, 143, 27, 80, 70, 111, 29, 81, 72, 112, + 70, 111, 123, 144, 86, 116, 128, 145, 29, 81, 86, 116, 31, 85, 87, + 117, 72, 112, 128, 145, 87, 117, 132, 146, 41, 59, 66, 81, 46, 71, + 72, 87, 69, 83, 106, 112, 122, 124, 128, 132, 59, 79, 109, 115, 71, + 101, 112, 117, 83, 91, 131, 133, 124, 130, 145, 146, 66, 109, 100, + 116, 72, 112, 102, 118, 106, 131, 128, 145, 128, 145, 141, 147, 81, + 115, 116, 138, 87, 117, 118, 139, 112, 133, 145, 149, 132, 146, 147, + 150, 16, 58, 53, 80, 21, 60, 59, 83, 58, 105, 80, 111, 82, 127, 109, + 131, 27, 70, 80, 111, 29, 72, 81, 112, 70, 123, 111, 144, 86, 128, + 116, 145, 21, 82, 59, 109, 28, 83, 67, 110, 60, 127, 83, 131, 83, + 131, 110, 143, 29, 86, 81, 116, 31, 87, 85, 117, 72, 128, 112, 145, + 87, 132, 117, 146, 41, 66, 59, 81, 46, 72, 71, 87, 69, 106, 83, 112, + 122, 128, 124, 132, 66, 100, 109, 116, 72, 102, 112, 118, 106, 128, + 131, 145, 128, 141, 145, 147, 59, 109, 79, 115, 71, 112, 101, 117, + 83, 131, 91, 133, 124, 145, 130, 146, 81, 116, 115, 138, 87, 118, + 117, 139, 112, 145, 133, 149, 132, 147, 146, 150, 18, 59, 59, 81, 22, + 61, 61, 84, 78, 109, 109, 116, 90, 129, 129, 133, 28, 71, 83, 112, + 30, 73, 84, 113, 83, 124, 131, 145, 91, 130, 133, 146, 28, 83, 71, + 112, 30, 84, 73, 113, 83, 131, 124, 145, 91, 133, 130, 146, 31, 87, + 87, 118, 32, 88, 88, 119, 87, 132, 132, 147, 92, 134, 134, 148, 43, + 67, 67, 85, 47, 73, 73, 88, 97, 110, 110, 117, 126, 130, 130, 134, + 67, 101, 110, 117, 73, 103, 113, 119, 110, 130, 143, 146, 130, 142, + 146, 148, 67, 110, 101, 117, 73, 113, 103, 119, 110, 143, 130, 146, + 130, 146, 142, 148, 85, 117, 117, 139, 88, 119, 119, 140, 117, 146, + 146, 150, 134, 148, 148, 151, 11, 14, 14, 18, 14, 18, 18, 22, 36, 41, + 41, 49, 55, 59, 59, 61, 36, 41, 55, 59, 41, 49, 59, 61, 42, 46, 66, + 71, 66, 71, 81, 84, 36, 55, 41, 59, 41, 59, 49, 61, 42, 66, 46, 71, + 66, 81, 71, 84, 42, 66, 66, 81, 46, 71, 71, 84, 62, 72, 72, 87, 72, + 87, 87, 92, 14, 18, 18, 24, 25, 28, 28, 30, 41, 49, 49, 63, 69, 71, + 71, 73, 55, 59, 78, 79, 69, 71, 83, 84, 66, 71, 99, 101, 106, 107, + 112, 113, 55, 78, 59, 79, 69, 83, 71, 84, 66, 99, 71, 101, 106, 112, + 107, 113, 96, 109, 109, 115, 122, 124, 124, 125, 100, 112, 112, 117, + 128, 132, 132, 134, 14, 18, 25, 28, 18, 24, 28, 30, 55, 59, 69, 71, + 78, 79, 83, 84, 41, 49, 69, 71, 49, 63, 71, 73, 66, 71, 106, 107, 99, + 101, 112, 113, 55, 78, 69, 83, 59, 79, 71, 84, 96, 109, 122, 124, + 109, 115, 124, 125, 66, 99, 106, 112, 71, 101, 107, 113, 100, 112, + 128, 132, 112, 117, 132, 134, 18, 22, 28, 30, 28, 30, 31, 32, 59, 61, + 71, 73, 83, 84, 87, 88, 59, 61, 83, 84, 71, 73, 87, 88, 81, 84, 112, + 113, 112, 113, 118, 119, 78, 90, 83, 91, 83, 91, 87, 92, 109, 129, + 124, 130, 131, 133, 132, 134, 109, 129, 131, 133, 124, 130, 132, 134, + 116, 133, 145, 146, 145, 146, 147, 148, 14, 25, 18, 28, 18, 28, 24, + 30, 55, 69, 59, 71, 78, 83, 79, 84, 55, 69, 78, 83, 59, 71, 79, 84, + 96, 122, 109, 124, 109, 124, 115, 125, 41, 69, 49, 71, 49, 71, 63, + 73, 66, 106, 71, 107, 99, 112, 101, 113, 66, 106, 99, 112, 71, 107, + 101, 113, 100, 128, 112, 132, 112, 132, 117, 134, 18, 28, 22, 30, 28, + 31, 30, 32, 59, 71, 61, 73, 83, 87, 84, 88, 78, 83, 90, 91, 83, 87, + 91, 92, 109, 124, 129, 130, 131, 132, 133, 134, 59, 83, 61, 84, 71, + 87, 73, 88, 81, 112, 84, 113, 112, 118, 113, 119, 109, 131, 129, 133, + 124, 132, 130, 134, 116, 145, 133, 146, 145, 147, 146, 148, 18, 28, + 28, 31, 22, 30, 30, 32, 78, 83, 83, 87, 90, 91, 91, 92, 59, 71, 83, + 87, 61, 73, 84, 88, 109, 124, 131, 132, 129, 130, 133, 134, 59, 83, + 71, 87, 61, 84, 73, 88, 109, 131, 124, 132, 129, 133, 130, 134, 81, + 112, 112, 118, 84, 113, 113, 119, 116, 145, 145, 147, 133, 146, 146, + 148, 24, 30, 30, 32, 30, 32, 32, 33, 79, 84, 84, 88, 91, 92, 92, 93, + 79, 84, 91, 92, 84, 88, 92, 93, 115, 125, 133, 134, 133, 134, 135, + 136, 79, 91, 84, 92, 84, 92, 88, 93, 115, 133, 125, 134, 133, 135, + 134, 136, 115, 133, 133, 135, 125, 134, 134, 136, 138, 149, 149, 150, + 149, 150, 150, 151, 36, 55, 55, 78, 55, 78, 78, 90, 42, 66, 66, 99, + 96, 109, 109, 129, 42, 66, 96, 109, 66, 99, 109, 129, 62, 72, 100, + 112, 100, 112, 116, 133, 42, 96, 66, 109, 66, 109, 99, 129, 62, 100, + 72, 112, 100, 116, 112, 133, 62, 100, 100, 116, 72, 112, 112, 133, + 94, 102, 102, 118, 102, 118, 118, 135, 41, 59, 59, 79, 69, 83, 83, + 91, 46, 71, 71, 101, 122, 124, 124, 130, 66, 81, 109, 115, 106, 112, + 131, 133, 72, 87, 112, 117, 128, 132, 145, 146, 66, 109, 81, 115, + 106, 131, 112, 133, 72, 112, 87, 117, 128, 145, 132, 146, 100, 116, + 116, 138, 128, 145, 145, 149, 102, 118, 118, 139, 141, 147, 147, 150, + 41, 59, 69, 83, 59, 79, 83, 91, 66, 81, 106, 112, 109, 115, 131, 133, + 46, 71, 122, 124, 71, 101, 124, 130, 72, 87, 128, 132, 112, 117, 145, + 146, 66, 109, 106, 131, 81, 115, 112, 133, 100, 116, 128, 145, 116, + 138, 145, 149, 72, 112, 128, 145, 87, 117, 132, 146, 102, 118, 141, + 147, 118, 139, 147, 150, 49, 61, 71, 84, 71, 84, 87, 92, 71, 84, 107, + 113, 124, 125, 132, 134, 71, 84, 124, 125, 107, 113, 132, 134, 87, + 92, 132, 134, 132, 134, 147, 148, 99, 129, 112, 133, 112, 133, 118, + 135, 112, 133, 132, 146, 145, 149, 147, 150, 112, 133, 145, 149, 132, + 146, 147, 150, 118, 135, 147, 150, 147, 150, 152, 153, 41, 69, 59, + 83, 59, 83, 79, 91, 66, 106, 81, 112, 109, 131, 115, 133, 66, 106, + 109, 131, 81, 112, 115, 133, 100, 128, 116, 145, 116, 145, 138, 149, + 46, 122, 71, 124, 71, 124, 101, 130, 72, 128, 87, 132, 112, 145, 117, + 146, 72, 128, 112, 145, 87, 132, 117, 146, 102, 141, 118, 147, 118, + 147, 139, 150, 49, 71, 61, 84, 71, 87, 84, 92, 71, 107, 84, 113, 124, + 132, 125, 134, 99, 112, 129, 133, 112, 118, 133, 135, 112, 132, 133, + 146, 145, 147, 149, 150, 71, 124, 84, 125, 107, 132, 113, 134, 87, + 132, 92, 134, 132, 147, 134, 148, 112, 145, 133, 149, 132, 147, 146, + 150, 118, 147, 135, 150, 147, 152, 150, 153, 49, 71, 71, 87, 61, 84, + 84, 92, 99, 112, 112, 118, 129, 133, 133, 135, 71, 107, 124, 132, 84, + 113, 125, 134, 112, 132, 145, 147, 133, 146, 149, 150, 71, 124, 107, + 132, 84, 125, 113, 134, 112, 145, 132, 147, 133, 149, 146, 150, 87, + 132, 132, 147, 92, 134, 134, 148, 118, 147, 147, 152, 135, 150, 150, + 153, 63, 73, 73, 88, 73, 88, 88, 93, 101, 113, 113, 119, 130, 134, + 134, 136, 101, 113, 130, 134, 113, 119, 134, 136, 117, 134, 146, 148, + 146, 148, 150, 151, 101, 130, 113, 134, 113, 134, 119, 136, 117, 146, + 134, 148, 146, 150, 148, 151, 117, 146, 146, 150, 134, 148, 148, 151, + 139, 150, 150, 153, 150, 153, 153, 154, 2, 12, 12, 38, 12, 38, 38, + 44, 6, 15, 15, 40, 51, 56, 56, 68, 6, 15, 51, 56, 15, 40, 56, 68, 13, + 20, 52, 58, 52, 58, 75, 82, 6, 51, 15, 56, 15, 56, 40, 68, 13, 52, + 20, 58, 52, 75, 58, 82, 13, 52, 52, 75, 20, 58, 58, 82, 36, 55, 55, + 78, 55, 78, 78, 90, 6, 15, 15, 40, 51, 56, 56, 68, 8, 17, 17, 42, 74, + 75, 75, 96, 15, 26, 56, 65, 56, 65, 104, 105, 17, 27, 57, 66, 75, 80, + 108, 109, 15, 56, 26, 65, 56, 104, 65, 105, 17, 57, 27, 66, 75, 108, + 80, 109, 40, 65, 65, 98, 68, 105, 105, 127, 42, 66, 66, 99, 96, 109, + 109, 129, 6, 15, 51, 56, 15, 40, 56, 68, 15, 26, 56, 65, 56, 65, 104, + 105, 8, 17, 74, 75, 17, 42, 75, 96, 17, 27, 75, 80, 57, 66, 108, 109, + 15, 56, 56, 104, 26, 65, 65, 105, 40, 65, 68, 105, 65, 98, 105, 127, + 17, 57, 75, 108, 27, 66, 80, 109, 42, 66, 96, 109, 66, 99, 109, 129, + 13, 20, 52, 58, 52, 58, 75, 82, 17, 27, 57, 66, 75, 80, 108, 109, 17, + 27, 75, 80, 57, 66, 108, 109, 23, 29, 76, 81, 76, 81, 114, 115, 40, + 68, 65, 105, 65, 105, 98, 127, 48, 70, 70, 106, 98, 111, 111, 131, + 48, 70, 98, 111, 70, 106, 111, 131, 62, 72, 100, 112, 100, 112, 116, + 133, 6, 51, 15, 56, 15, 56, 40, 68, 15, 56, 26, 65, 56, 104, 65, 105, + 15, 56, 56, 104, 26, 65, 65, 105, 40, 68, 65, 105, 65, 105, 98, 127, + 8, 74, 17, 75, 17, 75, 42, 96, 17, 75, 27, 80, 57, 108, 66, 109, 17, + 75, 57, 108, 27, 80, 66, 109, 42, 96, 66, 109, 66, 109, 99, 129, 13, + 52, 20, 58, 52, 75, 58, 82, 17, 57, 27, 66, 75, 108, 80, 109, 40, 65, + 68, 105, 65, 98, 105, 127, 48, 70, 70, 106, 98, 111, 111, 131, 17, + 75, 27, 80, 57, 108, 66, 109, 23, 76, 29, 81, 76, 114, 81, 115, 48, + 98, 70, 111, 70, 111, 106, 131, 62, 100, 72, 112, 100, 116, 112, 133, + 13, 52, 52, 75, 20, 58, 58, 82, 40, 65, 65, 98, 68, 105, 105, 127, + 17, 57, 75, 108, 27, 66, 80, 109, 48, 70, 98, 111, 70, 106, 111, 131, + 17, 75, 57, 108, 27, 80, 66, 109, 48, 98, 70, 111, 70, 111, 106, 131, + 23, 76, 76, 114, 29, 81, 81, 115, 62, 100, 100, 116, 72, 112, 112, + 133, 36, 55, 55, 78, 55, 78, 78, 90, 42, 66, 66, 99, 96, 109, 109, + 129, 42, 66, 96, 109, 66, 99, 109, 129, 62, 72, 100, 112, 100, 112, + 116, 133, 42, 96, 66, 109, 66, 109, 99, 129, 62, 100, 72, 112, 100, + 116, 112, 133, 62, 100, 100, 116, 72, 112, 112, 133, 94, 102, 102, + 118, 102, 118, 118, 135, 3, 19, 19, 44, 19, 44, 44, 120, 7, 20, 20, + 45, 54, 68, 68, 121, 7, 20, 54, 68, 20, 45, 68, 121, 14, 25, 55, 69, + 55, 69, 96, 122, 7, 54, 20, 68, 20, 68, 45, 121, 14, 55, 25, 69, 55, + 96, 69, 122, 14, 55, 55, 96, 25, 69, 69, 122, 37, 64, 64, 97, 64, 97, + 97, 126, 7, 20, 20, 45, 54, 68, 68, 121, 9, 21, 21, 46, 77, 82, 82, + 122, 16, 27, 58, 70, 58, 70, 105, 123, 18, 28, 59, 71, 78, 83, 109, + 124, 16, 58, 27, 70, 58, 105, 70, 123, 18, 59, 28, 71, 78, 109, 83, + 124, 41, 66, 66, 100, 69, 106, 106, 128, 43, 67, 67, 101, 97, 110, + 110, 130, 7, 20, 54, 68, 20, 45, 68, 121, 16, 27, 58, 70, 58, 70, + 105, 123, 9, 21, 77, 82, 21, 46, 82, 122, 18, 28, 78, 83, 59, 71, + 109, 124, 16, 58, 58, 105, 27, 70, 70, 123, 41, 66, 69, 106, 66, 100, + 106, 128, 18, 59, 78, 109, 28, 71, 83, 124, 43, 67, 97, 110, 67, 101, + 110, 130, 14, 25, 55, 69, 55, 69, 96, 122, 18, 28, 59, 71, 78, 83, + 109, 124, 18, 28, 78, 83, 59, 71, 109, 124, 24, 30, 79, 84, 79, 84, + 115, 125, 41, 69, 66, 106, 66, 106, 100, 128, 49, 71, 71, 107, 99, + 112, 112, 132, 49, 71, 99, 112, 71, 107, 112, 132, 63, 73, 101, 113, + 101, 113, 117, 134, 7, 54, 20, 68, 20, 68, 45, 121, 16, 58, 27, 70, + 58, 105, 70, 123, 16, 58, 58, 105, 27, 70, 70, 123, 41, 69, 66, 106, + 66, 106, 100, 128, 9, 77, 21, 82, 21, 82, 46, 122, 18, 78, 28, 83, + 59, 109, 71, 124, 18, 78, 59, 109, 28, 83, 71, 124, 43, 97, 67, 110, + 67, 110, 101, 130, 14, 55, 25, 69, 55, 96, 69, 122, 18, 59, 28, 71, + 78, 109, 83, 124, 41, 66, 69, 106, 66, 100, 106, 128, 49, 71, 71, + 107, 99, 112, 112, 132, 18, 78, 28, 83, 59, 109, 71, 124, 24, 79, 30, + 84, 79, 115, 84, 125, 49, 99, 71, 112, 71, 112, 107, 132, 63, 101, + 73, 113, 101, 117, 113, 134, 14, 55, 55, 96, 25, 69, 69, 122, 41, 66, + 66, 100, 69, 106, 106, 128, 18, 59, 78, 109, 28, 71, 83, 124, 49, 71, + 99, 112, 71, 107, 112, 132, 18, 78, 59, 109, 28, 83, 71, 124, 49, 99, + 71, 112, 71, 112, 107, 132, 24, 79, 79, 115, 30, 84, 84, 125, 63, + 101, 101, 117, 73, 113, 113, 134, 37, 64, 64, 97, 64, 97, 97, 126, + 43, 67, 67, 101, 97, 110, 110, 130, 43, 67, 97, 110, 67, 101, 110, + 130, 63, 73, 101, 113, 101, 113, 117, 134, 43, 97, 67, 110, 67, 110, + 101, 130, 63, 101, 73, 113, 101, 117, 113, 134, 63, 101, 101, 117, + 73, 113, 113, 134, 95, 103, 103, 119, 103, 119, 119, 136, 4, 13, 13, + 39, 35, 40, 40, 45, 7, 16, 16, 41, 54, 58, 58, 69, 13, 17, 52, 57, + 40, 48, 65, 70, 16, 21, 53, 59, 58, 60, 80, 83, 13, 52, 17, 57, 40, + 65, 48, 70, 16, 53, 21, 59, 58, 80, 60, 83, 39, 57, 57, 76, 45, 70, + 70, 86, 41, 59, 59, 79, 69, 83, 83, 91, 7, 16, 16, 41, 54, 58, 58, + 69, 9, 18, 18, 43, 77, 78, 78, 97, 20, 27, 58, 66, 68, 70, 105, 106, + 21, 28, 59, 67, 82, 83, 109, 110, 20, 58, 27, 66, 68, 105, 70, 106, + 21, 59, 28, 67, 82, 109, 83, 110, 45, 70, 70, 100, 121, 123, 123, + 128, 46, 71, 71, 101, 122, 124, 124, 130, 13, 17, 52, 57, 40, 48, 65, + 70, 20, 27, 58, 66, 68, 70, 105, 106, 17, 23, 75, 76, 48, 62, 98, + 100, 27, 29, 80, 81, 70, 72, 111, 112, 52, 75, 75, 108, 65, 98, 98, + 111, 58, 80, 82, 109, 105, 111, 127, 131, 57, 76, 108, 114, 70, 100, + 111, 116, 66, 81, 109, 115, 106, 112, 131, 133, 16, 21, 53, 59, 58, + 60, 80, 83, 21, 28, 59, 67, 82, 83, 109, 110, 27, 29, 80, 81, 70, 72, + 111, 112, 29, 31, 81, 85, 86, 87, 116, 117, 58, 82, 80, 109, 105, + 127, 111, 131, 60, 83, 83, 110, 127, 131, 131, 143, 70, 86, 111, 116, + 123, 128, 144, 145, 72, 87, 112, 117, 128, 132, 145, 146, 13, 52, 17, + 57, 40, 65, 48, 70, 20, 58, 27, 66, 68, 105, 70, 106, 52, 75, 75, + 108, 65, 98, 98, 111, 58, 82, 80, 109, 105, 127, 111, 131, 17, 75, + 23, 76, 48, 98, 62, 100, 27, 80, 29, 81, 70, 111, 72, 112, 57, 108, + 76, 114, 70, 111, 100, 116, 66, 109, 81, 115, 106, 131, 112, 133, 16, + 53, 21, 59, 58, 80, 60, 83, 21, 59, 28, 67, 82, 109, 83, 110, 58, 80, + 82, 109, 105, 111, 127, 131, 60, 83, 83, 110, 127, 131, 131, 143, 27, + 80, 29, 81, 70, 111, 72, 112, 29, 81, 31, 85, 86, 116, 87, 117, 70, + 111, 86, 116, 123, 144, 128, 145, 72, 112, 87, 117, 128, 145, 132, + 146, 39, 57, 57, 76, 45, 70, 70, 86, 45, 70, 70, 100, 121, 123, 123, + 128, 57, 76, 108, 114, 70, 100, 111, 116, 70, 86, 111, 116, 123, 128, + 144, 145, 57, 108, 76, 114, 70, 111, 100, 116, 70, 111, 86, 116, 123, + 144, 128, 145, 76, 114, 114, 137, 86, 116, 116, 138, 100, 116, 116, + 138, 128, 145, 145, 149, 41, 59, 59, 79, 69, 83, 83, 91, 46, 71, 71, + 101, 122, 124, 124, 130, 66, 81, 109, 115, 106, 112, 131, 133, 72, + 87, 112, 117, 128, 132, 145, 146, 66, 109, 81, 115, 106, 131, 112, + 133, 72, 112, 87, 117, 128, 145, 132, 146, 100, 116, 116, 138, 128, + 145, 145, 149, 102, 118, 118, 139, 141, 147, 147, 150, 7, 20, 20, 45, + 54, 68, 68, 121, 9, 21, 21, 46, 77, 82, 82, 122, 16, 27, 58, 70, 58, + 70, 105, 123, 18, 28, 59, 71, 78, 83, 109, 124, 16, 58, 27, 70, 58, + 105, 70, 123, 18, 59, 28, 71, 78, 109, 83, 124, 41, 66, 66, 100, 69, + 106, 106, 128, 43, 67, 67, 101, 97, 110, 110, 130, 9, 21, 21, 46, 77, + 82, 82, 122, 10, 22, 22, 47, 89, 90, 90, 126, 21, 29, 60, 72, 82, 86, + 127, 128, 22, 30, 61, 73, 90, 91, 129, 130, 21, 60, 29, 72, 82, 127, + 86, 128, 22, 61, 30, 73, 90, 129, 91, 130, 46, 72, 72, 102, 122, 128, + 128, 141, 47, 73, 73, 103, 126, 130, 130, 142, 16, 27, 58, 70, 58, + 70, 105, 123, 21, 29, 60, 72, 82, 86, 127, 128, 21, 29, 82, 86, 60, + 72, 127, 128, 28, 31, 83, 87, 83, 87, 131, 132, 53, 80, 80, 111, 80, + 111, 111, 144, 59, 81, 83, 112, 109, 116, 131, 145, 59, 81, 109, 116, + 83, 112, 131, 145, 67, 85, 110, 117, 110, 117, 143, 146, 18, 28, 59, + 71, 78, 83, 109, 124, 22, 30, 61, 73, 90, 91, 129, 130, 28, 31, 83, + 87, 83, 87, 131, 132, 30, 32, 84, 88, 91, 92, 133, 134, 59, 83, 81, + 112, 109, 131, 116, 145, 61, 84, 84, 113, 129, 133, 133, 146, 71, 87, + 112, 118, 124, 132, 145, 147, 73, 88, 113, 119, 130, 134, 146, 148, + 16, 58, 27, 70, 58, 105, 70, 123, 21, 60, 29, 72, 82, 127, 86, 128, + 53, 80, 80, 111, 80, 111, 111, 144, 59, 83, 81, 112, 109, 131, 116, + 145, 21, 82, 29, 86, 60, 127, 72, 128, 28, 83, 31, 87, 83, 131, 87, + 132, 59, 109, 81, 116, 83, 131, 112, 145, 67, 110, 85, 117, 110, 143, + 117, 146, 18, 59, 28, 71, 78, 109, 83, 124, 22, 61, 30, 73, 90, 129, + 91, 130, 59, 81, 83, 112, 109, 116, 131, 145, 61, 84, 84, 113, 129, + 133, 133, 146, 28, 83, 31, 87, 83, 131, 87, 132, 30, 84, 32, 88, 91, + 133, 92, 134, 71, 112, 87, 118, 124, 145, 132, 147, 73, 113, 88, 119, + 130, 146, 134, 148, 41, 66, 66, 100, 69, 106, 106, 128, 46, 72, 72, + 102, 122, 128, 128, 141, 59, 81, 109, 116, 83, 112, 131, 145, 71, 87, + 112, 118, 124, 132, 145, 147, 59, 109, 81, 116, 83, 131, 112, 145, + 71, 112, 87, 118, 124, 145, 132, 147, 79, 115, 115, 138, 91, 133, + 133, 149, 101, 117, 117, 139, 130, 146, 146, 150, 43, 67, 67, 101, + 97, 110, 110, 130, 47, 73, 73, 103, 126, 130, 130, 142, 67, 85, 110, + 117, 110, 117, 143, 146, 73, 88, 113, 119, 130, 134, 146, 148, 67, + 110, 85, 117, 110, 143, 117, 146, 73, 113, 88, 119, 130, 146, 134, + 148, 101, 117, 117, 139, 130, 146, 146, 150, 103, 119, 119, 140, 142, + 148, 148, 151, 4, 13, 35, 40, 13, 39, 40, 45, 13, 17, 40, 48, 52, 57, + 65, 70, 7, 16, 54, 58, 16, 41, 58, 69, 16, 21, 58, 60, 53, 59, 80, + 83, 13, 52, 40, 65, 17, 57, 48, 70, 39, 57, 45, 70, 57, 76, 70, 86, + 16, 53, 58, 80, 21, 59, 60, 83, 41, 59, 69, 83, 59, 79, 83, 91, 13, + 17, 40, 48, 52, 57, 65, 70, 17, 23, 48, 62, 75, 76, 98, 100, 20, 27, + 68, 70, 58, 66, 105, 106, 27, 29, 70, 72, 80, 81, 111, 112, 52, 75, + 65, 98, 75, 108, 98, 111, 57, 76, 70, 100, 108, 114, 111, 116, 58, + 80, 105, 111, 82, 109, 127, 131, 66, 81, 106, 112, 109, 115, 131, + 133, 7, 16, 54, 58, 16, 41, 58, 69, 20, 27, 68, 70, 58, 66, 105, 106, + 9, 18, 77, 78, 18, 43, 78, 97, 21, 28, 82, 83, 59, 67, 109, 110, 20, + 58, 68, 105, 27, 66, 70, 106, 45, 70, 121, 123, 70, 100, 123, 128, + 21, 59, 82, 109, 28, 67, 83, 110, 46, 71, 122, 124, 71, 101, 124, + 130, 16, 21, 58, 60, 53, 59, 80, 83, 27, 29, 70, 72, 80, 81, 111, + 112, 21, 28, 82, 83, 59, 67, 109, 110, 29, 31, 86, 87, 81, 85, 116, + 117, 58, 82, 105, 127, 80, 109, 111, 131, 70, 86, 123, 128, 111, 116, + 144, 145, 60, 83, 127, 131, 83, 110, 131, 143, 72, 87, 128, 132, 112, + 117, 145, 146, 13, 52, 40, 65, 17, 57, 48, 70, 52, 75, 65, 98, 75, + 108, 98, 111, 20, 58, 68, 105, 27, 66, 70, 106, 58, 82, 105, 127, 80, + 109, 111, 131, 17, 75, 48, 98, 23, 76, 62, 100, 57, 108, 70, 111, 76, + 114, 100, 116, 27, 80, 70, 111, 29, 81, 72, 112, 66, 109, 106, 131, + 81, 115, 112, 133, 39, 57, 45, 70, 57, 76, 70, 86, 57, 76, 70, 100, + 108, 114, 111, 116, 45, 70, 121, 123, 70, 100, 123, 128, 70, 86, 123, + 128, 111, 116, 144, 145, 57, 108, 70, 111, 76, 114, 100, 116, 76, + 114, 86, 116, 114, 137, 116, 138, 70, 111, 123, 144, 86, 116, 128, + 145, 100, 116, 128, 145, 116, 138, 145, 149, 16, 53, 58, 80, 21, 59, + 60, 83, 58, 80, 105, 111, 82, 109, 127, 131, 21, 59, 82, 109, 28, 67, + 83, 110, 60, 83, 127, 131, 83, 110, 131, 143, 27, 80, 70, 111, 29, + 81, 72, 112, 70, 111, 123, 144, 86, 116, 128, 145, 29, 81, 86, 116, + 31, 85, 87, 117, 72, 112, 128, 145, 87, 117, 132, 146, 41, 59, 69, + 83, 59, 79, 83, 91, 66, 81, 106, 112, 109, 115, 131, 133, 46, 71, + 122, 124, 71, 101, 124, 130, 72, 87, 128, 132, 112, 117, 145, 146, + 66, 109, 106, 131, 81, 115, 112, 133, 100, 116, 128, 145, 116, 138, + 145, 149, 72, 112, 128, 145, 87, 117, 132, 146, 102, 118, 141, 147, + 118, 139, 147, 150, 7, 20, 54, 68, 20, 45, 68, 121, 16, 27, 58, 70, + 58, 70, 105, 123, 9, 21, 77, 82, 21, 46, 82, 122, 18, 28, 78, 83, 59, + 71, 109, 124, 16, 58, 58, 105, 27, 70, 70, 123, 41, 66, 69, 106, 66, + 100, 106, 128, 18, 59, 78, 109, 28, 71, 83, 124, 43, 67, 97, 110, 67, + 101, 110, 130, 16, 27, 58, 70, 58, 70, 105, 123, 21, 29, 60, 72, 82, + 86, 127, 128, 21, 29, 82, 86, 60, 72, 127, 128, 28, 31, 83, 87, 83, + 87, 131, 132, 53, 80, 80, 111, 80, 111, 111, 144, 59, 81, 83, 112, + 109, 116, 131, 145, 59, 81, 109, 116, 83, 112, 131, 145, 67, 85, 110, + 117, 110, 117, 143, 146, 9, 21, 77, 82, 21, 46, 82, 122, 21, 29, 82, + 86, 60, 72, 127, 128, 10, 22, 89, 90, 22, 47, 90, 126, 22, 30, 90, + 91, 61, 73, 129, 130, 21, 60, 82, 127, 29, 72, 86, 128, 46, 72, 122, + 128, 72, 102, 128, 141, 22, 61, 90, 129, 30, 73, 91, 130, 47, 73, + 126, 130, 73, 103, 130, 142, 18, 28, 78, 83, 59, 71, 109, 124, 28, + 31, 83, 87, 83, 87, 131, 132, 22, 30, 90, 91, 61, 73, 129, 130, 30, + 32, 91, 92, 84, 88, 133, 134, 59, 83, 109, 131, 81, 112, 116, 145, + 71, 87, 124, 132, 112, 118, 145, 147, 61, 84, 129, 133, 84, 113, 133, + 146, 73, 88, 130, 134, 113, 119, 146, 148, 16, 58, 58, 105, 27, 70, + 70, 123, 53, 80, 80, 111, 80, 111, 111, 144, 21, 60, 82, 127, 29, 72, + 86, 128, 59, 83, 109, 131, 81, 112, 116, 145, 21, 82, 60, 127, 29, + 86, 72, 128, 59, 109, 83, 131, 81, 116, 112, 145, 28, 83, 83, 131, + 31, 87, 87, 132, 67, 110, 110, 143, 85, 117, 117, 146, 41, 66, 69, + 106, 66, 100, 106, 128, 59, 81, 83, 112, 109, 116, 131, 145, 46, 72, + 122, 128, 72, 102, 128, 141, 71, 87, 124, 132, 112, 118, 145, 147, + 59, 109, 83, 131, 81, 116, 112, 145, 79, 115, 91, 133, 115, 138, 133, + 149, 71, 112, 124, 145, 87, 118, 132, 147, 101, 117, 130, 146, 117, + 139, 146, 150, 18, 59, 78, 109, 28, 71, 83, 124, 59, 81, 109, 116, + 83, 112, 131, 145, 22, 61, 90, 129, 30, 73, 91, 130, 61, 84, 129, + 133, 84, 113, 133, 146, 28, 83, 83, 131, 31, 87, 87, 132, 71, 112, + 124, 145, 87, 118, 132, 147, 30, 84, 91, 133, 32, 88, 92, 134, 73, + 113, 130, 146, 88, 119, 134, 148, 43, 67, 97, 110, 67, 101, 110, 130, + 67, 85, 110, 117, 110, 117, 143, 146, 47, 73, 126, 130, 73, 103, 130, + 142, 73, 88, 130, 134, 113, 119, 146, 148, 67, 110, 110, 143, 85, + 117, 117, 146, 101, 117, 130, 146, 117, 139, 146, 150, 73, 113, 130, + 146, 88, 119, 134, 148, 103, 119, 142, 148, 119, 140, 148, 151, 11, + 14, 36, 41, 36, 41, 42, 46, 14, 18, 41, 49, 55, 59, 66, 71, 14, 18, + 55, 59, 41, 49, 66, 71, 18, 22, 59, 61, 59, 61, 81, 84, 36, 55, 42, + 66, 42, 66, 62, 72, 41, 59, 46, 71, 66, 81, 72, 87, 41, 59, 66, 81, + 46, 71, 72, 87, 49, 61, 71, 84, 71, 84, 87, 92, 14, 18, 41, 49, 55, + 59, 66, 71, 18, 24, 49, 63, 78, 79, 99, 101, 25, 28, 69, 71, 69, 71, + 106, 107, 28, 30, 71, 73, 83, 84, 112, 113, 55, 78, 66, 99, 96, 109, + 100, 112, 59, 79, 71, 101, 109, 115, 112, 117, 69, 83, 106, 112, 122, + 124, 128, 132, 71, 84, 107, 113, 124, 125, 132, 134, 14, 18, 55, 59, + 41, 49, 66, 71, 25, 28, 69, 71, 69, 71, 106, 107, 18, 24, 78, 79, 49, + 63, 99, 101, 28, 30, 83, 84, 71, 73, 112, 113, 55, 78, 96, 109, 66, + 99, 100, 112, 69, 83, 122, 124, 106, 112, 128, 132, 59, 79, 109, 115, + 71, 101, 112, 117, 71, 84, 124, 125, 107, 113, 132, 134, 18, 22, 59, + 61, 59, 61, 81, 84, 28, 30, 71, 73, 83, 84, 112, 113, 28, 30, 83, 84, + 71, 73, 112, 113, 31, 32, 87, 88, 87, 88, 118, 119, 78, 90, 109, 129, + 109, 129, 116, 133, 83, 91, 124, 130, 131, 133, 145, 146, 83, 91, + 131, 133, 124, 130, 145, 146, 87, 92, 132, 134, 132, 134, 147, 148, + 36, 55, 42, 66, 42, 66, 62, 72, 55, 78, 66, 99, 96, 109, 100, 112, + 55, 78, 96, 109, 66, 99, 100, 112, 78, 90, 109, 129, 109, 129, 116, + 133, 42, 96, 62, 100, 62, 100, 94, 102, 66, 109, 72, 112, 100, 116, + 102, 118, 66, 109, 100, 116, 72, 112, 102, 118, 99, 129, 112, 133, + 112, 133, 118, 135, 41, 59, 46, 71, 66, 81, 72, 87, 59, 79, 71, 101, + 109, 115, 112, 117, 69, 83, 122, 124, 106, 112, 128, 132, 83, 91, + 124, 130, 131, 133, 145, 146, 66, 109, 72, 112, 100, 116, 102, 118, + 81, 115, 87, 117, 116, 138, 118, 139, 106, 131, 128, 145, 128, 145, + 141, 147, 112, 133, 132, 146, 145, 149, 147, 150, 41, 59, 66, 81, 46, + 71, 72, 87, 69, 83, 106, 112, 122, 124, 128, 132, 59, 79, 109, 115, + 71, 101, 112, 117, 83, 91, 131, 133, 124, 130, 145, 146, 66, 109, + 100, 116, 72, 112, 102, 118, 106, 131, 128, 145, 128, 145, 141, 147, + 81, 115, 116, 138, 87, 117, 118, 139, 112, 133, 145, 149, 132, 146, + 147, 150, 49, 61, 71, 84, 71, 84, 87, 92, 71, 84, 107, 113, 124, 125, + 132, 134, 71, 84, 124, 125, 107, 113, 132, 134, 87, 92, 132, 134, + 132, 134, 147, 148, 99, 129, 112, 133, 112, 133, 118, 135, 112, 133, + 132, 146, 145, 149, 147, 150, 112, 133, 145, 149, 132, 146, 147, 150, + 118, 135, 147, 150, 147, 150, 152, 153, 14, 25, 55, 69, 55, 69, 96, + 122, 18, 28, 59, 71, 78, 83, 109, 124, 18, 28, 78, 83, 59, 71, 109, + 124, 24, 30, 79, 84, 79, 84, 115, 125, 41, 69, 66, 106, 66, 106, 100, + 128, 49, 71, 71, 107, 99, 112, 112, 132, 49, 71, 99, 112, 71, 107, + 112, 132, 63, 73, 101, 113, 101, 113, 117, 134, 18, 28, 59, 71, 78, + 83, 109, 124, 22, 30, 61, 73, 90, 91, 129, 130, 28, 31, 83, 87, 83, + 87, 131, 132, 30, 32, 84, 88, 91, 92, 133, 134, 59, 83, 81, 112, 109, + 131, 116, 145, 61, 84, 84, 113, 129, 133, 133, 146, 71, 87, 112, 118, + 124, 132, 145, 147, 73, 88, 113, 119, 130, 134, 146, 148, 18, 28, 78, + 83, 59, 71, 109, 124, 28, 31, 83, 87, 83, 87, 131, 132, 22, 30, 90, + 91, 61, 73, 129, 130, 30, 32, 91, 92, 84, 88, 133, 134, 59, 83, 109, + 131, 81, 112, 116, 145, 71, 87, 124, 132, 112, 118, 145, 147, 61, 84, + 129, 133, 84, 113, 133, 146, 73, 88, 130, 134, 113, 119, 146, 148, + 24, 30, 79, 84, 79, 84, 115, 125, 30, 32, 84, 88, 91, 92, 133, 134, + 30, 32, 91, 92, 84, 88, 133, 134, 32, 33, 92, 93, 92, 93, 135, 136, + 79, 91, 115, 133, 115, 133, 138, 149, 84, 92, 125, 134, 133, 135, + 149, 150, 84, 92, 133, 135, 125, 134, 149, 150, 88, 93, 134, 136, + 134, 136, 150, 151, 41, 69, 66, 106, 66, 106, 100, 128, 59, 83, 81, + 112, 109, 131, 116, 145, 59, 83, 109, 131, 81, 112, 116, 145, 79, 91, + 115, 133, 115, 133, 138, 149, 46, 122, 72, 128, 72, 128, 102, 141, + 71, 124, 87, 132, 112, 145, 118, 147, 71, 124, 112, 145, 87, 132, + 118, 147, 101, 130, 117, 146, 117, 146, 139, 150, 49, 71, 71, 107, + 99, 112, 112, 132, 61, 84, 84, 113, 129, 133, 133, 146, 71, 87, 124, + 132, 112, 118, 145, 147, 84, 92, 125, 134, 133, 135, 149, 150, 71, + 124, 87, 132, 112, 145, 118, 147, 84, 125, 92, 134, 133, 149, 135, + 150, 107, 132, 132, 147, 132, 147, 147, 152, 113, 134, 134, 148, 146, + 150, 150, 153, 49, 71, 99, 112, 71, 107, 112, 132, 71, 87, 112, 118, + 124, 132, 145, 147, 61, 84, 129, 133, 84, 113, 133, 146, 84, 92, 133, + 135, 125, 134, 149, 150, 71, 124, 112, 145, 87, 132, 118, 147, 107, + 132, 132, 147, 132, 147, 147, 152, 84, 125, 133, 149, 92, 134, 135, + 150, 113, 134, 146, 150, 134, 148, 150, 153, 63, 73, 101, 113, 101, + 113, 117, 134, 73, 88, 113, 119, 130, 134, 146, 148, 73, 88, 130, + 134, 113, 119, 146, 148, 88, 93, 134, 136, 134, 136, 150, 151, 101, + 130, 117, 146, 117, 146, 139, 150, 113, 134, 134, 148, 146, 150, 150, + 153, 113, 134, 146, 150, 134, 148, 150, 153, 119, 136, 148, 151, 148, + 151, 153, 154, 4, 35, 13, 40, 13, 40, 39, 45, 13, 40, 17, 48, 52, 65, + 57, 70, 13, 40, 52, 65, 17, 48, 57, 70, 39, 45, 57, 70, 57, 70, 76, + 86, 7, 54, 16, 58, 16, 58, 41, 69, 16, 58, 21, 60, 53, 80, 59, 83, + 16, 58, 53, 80, 21, 60, 59, 83, 41, 69, 59, 83, 59, 83, 79, 91, 13, + 40, 17, 48, 52, 65, 57, 70, 17, 48, 23, 62, 75, 98, 76, 100, 52, 65, + 75, 98, 75, 98, 108, 111, 57, 70, 76, 100, 108, 111, 114, 116, 20, + 68, 27, 70, 58, 105, 66, 106, 27, 70, 29, 72, 80, 111, 81, 112, 58, + 105, 80, 111, 82, 127, 109, 131, 66, 106, 81, 112, 109, 131, 115, + 133, 13, 40, 52, 65, 17, 48, 57, 70, 52, 65, 75, 98, 75, 98, 108, + 111, 17, 48, 75, 98, 23, 62, 76, 100, 57, 70, 108, 111, 76, 100, 114, + 116, 20, 68, 58, 105, 27, 70, 66, 106, 58, 105, 82, 127, 80, 111, + 109, 131, 27, 70, 80, 111, 29, 72, 81, 112, 66, 106, 109, 131, 81, + 112, 115, 133, 39, 45, 57, 70, 57, 70, 76, 86, 57, 70, 76, 100, 108, + 111, 114, 116, 57, 70, 108, 111, 76, 100, 114, 116, 76, 86, 114, 116, + 114, 116, 137, 138, 45, 121, 70, 123, 70, 123, 100, 128, 70, 123, 86, + 128, 111, 144, 116, 145, 70, 123, 111, 144, 86, 128, 116, 145, 100, + 128, 116, 145, 116, 145, 138, 149, 7, 54, 16, 58, 16, 58, 41, 69, 20, + 68, 27, 70, 58, 105, 66, 106, 20, 68, 58, 105, 27, 70, 66, 106, 45, + 121, 70, 123, 70, 123, 100, 128, 9, 77, 18, 78, 18, 78, 43, 97, 21, + 82, 28, 83, 59, 109, 67, 110, 21, 82, 59, 109, 28, 83, 67, 110, 46, + 122, 71, 124, 71, 124, 101, 130, 16, 58, 21, 60, 53, 80, 59, 83, 27, + 70, 29, 72, 80, 111, 81, 112, 58, 105, 82, 127, 80, 111, 109, 131, + 70, 123, 86, 128, 111, 144, 116, 145, 21, 82, 28, 83, 59, 109, 67, + 110, 29, 86, 31, 87, 81, 116, 85, 117, 60, 127, 83, 131, 83, 131, + 110, 143, 72, 128, 87, 132, 112, 145, 117, 146, 16, 58, 53, 80, 21, + 60, 59, 83, 58, 105, 80, 111, 82, 127, 109, 131, 27, 70, 80, 111, 29, + 72, 81, 112, 70, 123, 111, 144, 86, 128, 116, 145, 21, 82, 59, 109, + 28, 83, 67, 110, 60, 127, 83, 131, 83, 131, 110, 143, 29, 86, 81, + 116, 31, 87, 85, 117, 72, 128, 112, 145, 87, 132, 117, 146, 41, 69, + 59, 83, 59, 83, 79, 91, 66, 106, 81, 112, 109, 131, 115, 133, 66, + 106, 109, 131, 81, 112, 115, 133, 100, 128, 116, 145, 116, 145, 138, + 149, 46, 122, 71, 124, 71, 124, 101, 130, 72, 128, 87, 132, 112, 145, + 117, 146, 72, 128, 112, 145, 87, 132, 117, 146, 102, 141, 118, 147, + 118, 147, 139, 150, 7, 54, 20, 68, 20, 68, 45, 121, 16, 58, 27, 70, + 58, 105, 70, 123, 16, 58, 58, 105, 27, 70, 70, 123, 41, 69, 66, 106, + 66, 106, 100, 128, 9, 77, 21, 82, 21, 82, 46, 122, 18, 78, 28, 83, + 59, 109, 71, 124, 18, 78, 59, 109, 28, 83, 71, 124, 43, 97, 67, 110, + 67, 110, 101, 130, 16, 58, 27, 70, 58, 105, 70, 123, 21, 60, 29, 72, + 82, 127, 86, 128, 53, 80, 80, 111, 80, 111, 111, 144, 59, 83, 81, + 112, 109, 131, 116, 145, 21, 82, 29, 86, 60, 127, 72, 128, 28, 83, + 31, 87, 83, 131, 87, 132, 59, 109, 81, 116, 83, 131, 112, 145, 67, + 110, 85, 117, 110, 143, 117, 146, 16, 58, 58, 105, 27, 70, 70, 123, + 53, 80, 80, 111, 80, 111, 111, 144, 21, 60, 82, 127, 29, 72, 86, 128, + 59, 83, 109, 131, 81, 112, 116, 145, 21, 82, 60, 127, 29, 86, 72, + 128, 59, 109, 83, 131, 81, 116, 112, 145, 28, 83, 83, 131, 31, 87, + 87, 132, 67, 110, 110, 143, 85, 117, 117, 146, 41, 69, 66, 106, 66, + 106, 100, 128, 59, 83, 81, 112, 109, 131, 116, 145, 59, 83, 109, 131, + 81, 112, 116, 145, 79, 91, 115, 133, 115, 133, 138, 149, 46, 122, 72, + 128, 72, 128, 102, 141, 71, 124, 87, 132, 112, 145, 118, 147, 71, + 124, 112, 145, 87, 132, 118, 147, 101, 130, 117, 146, 117, 146, 139, + 150, 9, 77, 21, 82, 21, 82, 46, 122, 21, 82, 29, 86, 60, 127, 72, + 128, 21, 82, 60, 127, 29, 86, 72, 128, 46, 122, 72, 128, 72, 128, + 102, 141, 10, 89, 22, 90, 22, 90, 47, 126, 22, 90, 30, 91, 61, 129, + 73, 130, 22, 90, 61, 129, 30, 91, 73, 130, 47, 126, 73, 130, 73, 130, + 103, 142, 18, 78, 28, 83, 59, 109, 71, 124, 28, 83, 31, 87, 83, 131, + 87, 132, 59, 109, 83, 131, 81, 116, 112, 145, 71, 124, 87, 132, 112, + 145, 118, 147, 22, 90, 30, 91, 61, 129, 73, 130, 30, 91, 32, 92, 84, + 133, 88, 134, 61, 129, 84, 133, 84, 133, 113, 146, 73, 130, 88, 134, + 113, 146, 119, 148, 18, 78, 59, 109, 28, 83, 71, 124, 59, 109, 81, + 116, 83, 131, 112, 145, 28, 83, 83, 131, 31, 87, 87, 132, 71, 124, + 112, 145, 87, 132, 118, 147, 22, 90, 61, 129, 30, 91, 73, 130, 61, + 129, 84, 133, 84, 133, 113, 146, 30, 91, 84, 133, 32, 92, 88, 134, + 73, 130, 113, 146, 88, 134, 119, 148, 43, 97, 67, 110, 67, 110, 101, + 130, 67, 110, 85, 117, 110, 143, 117, 146, 67, 110, 110, 143, 85, + 117, 117, 146, 101, 130, 117, 146, 117, 146, 139, 150, 47, 126, 73, + 130, 73, 130, 103, 142, 73, 130, 88, 134, 113, 146, 119, 148, 73, + 130, 113, 146, 88, 134, 119, 148, 103, 142, 119, 148, 119, 148, 140, + 151, 11, 36, 14, 41, 36, 42, 41, 46, 14, 41, 18, 49, 55, 66, 59, 71, + 36, 42, 55, 66, 42, 62, 66, 72, 41, 46, 59, 71, 66, 72, 81, 87, 14, + 55, 18, 59, 41, 66, 49, 71, 18, 59, 22, 61, 59, 81, 61, 84, 41, 66, + 59, 81, 46, 72, 71, 87, 49, 71, 61, 84, 71, 87, 84, 92, 14, 41, 18, + 49, 55, 66, 59, 71, 18, 49, 24, 63, 78, 99, 79, 101, 55, 66, 78, 99, + 96, 100, 109, 112, 59, 71, 79, 101, 109, 112, 115, 117, 25, 69, 28, + 71, 69, 106, 71, 107, 28, 71, 30, 73, 83, 112, 84, 113, 69, 106, 83, + 112, 122, 128, 124, 132, 71, 107, 84, 113, 124, 132, 125, 134, 36, + 42, 55, 66, 42, 62, 66, 72, 55, 66, 78, 99, 96, 100, 109, 112, 42, + 62, 96, 100, 62, 94, 100, 102, 66, 72, 109, 112, 100, 102, 116, 118, + 55, 96, 78, 109, 66, 100, 99, 112, 78, 109, 90, 129, 109, 116, 129, + 133, 66, 100, 109, 116, 72, 102, 112, 118, 99, 112, 129, 133, 112, + 118, 133, 135, 41, 46, 59, 71, 66, 72, 81, 87, 59, 71, 79, 101, 109, + 112, 115, 117, 66, 72, 109, 112, 100, 102, 116, 118, 81, 87, 115, + 117, 116, 118, 138, 139, 69, 122, 83, 124, 106, 128, 112, 132, 83, + 124, 91, 130, 131, 145, 133, 146, 106, 128, 131, 145, 128, 141, 145, + 147, 112, 132, 133, 146, 145, 147, 149, 150, 14, 55, 18, 59, 41, 66, + 49, 71, 25, 69, 28, 71, 69, 106, 71, 107, 55, 96, 78, 109, 66, 100, + 99, 112, 69, 122, 83, 124, 106, 128, 112, 132, 18, 78, 24, 79, 49, + 99, 63, 101, 28, 83, 30, 84, 71, 112, 73, 113, 59, 109, 79, 115, 71, + 112, 101, 117, 71, 124, 84, 125, 107, 132, 113, 134, 18, 59, 22, 61, + 59, 81, 61, 84, 28, 71, 30, 73, 83, 112, 84, 113, 78, 109, 90, 129, + 109, 116, 129, 133, 83, 124, 91, 130, 131, 145, 133, 146, 28, 83, 30, + 84, 71, 112, 73, 113, 31, 87, 32, 88, 87, 118, 88, 119, 83, 131, 91, + 133, 124, 145, 130, 146, 87, 132, 92, 134, 132, 147, 134, 148, 41, + 66, 59, 81, 46, 72, 71, 87, 69, 106, 83, 112, 122, 128, 124, 132, 66, + 100, 109, 116, 72, 102, 112, 118, 106, 128, 131, 145, 128, 141, 145, + 147, 59, 109, 79, 115, 71, 112, 101, 117, 83, 131, 91, 133, 124, 145, + 130, 146, 81, 116, 115, 138, 87, 118, 117, 139, 112, 145, 133, 149, + 132, 147, 146, 150, 49, 71, 61, 84, 71, 87, 84, 92, 71, 107, 84, 113, + 124, 132, 125, 134, 99, 112, 129, 133, 112, 118, 133, 135, 112, 132, + 133, 146, 145, 147, 149, 150, 71, 124, 84, 125, 107, 132, 113, 134, + 87, 132, 92, 134, 132, 147, 134, 148, 112, 145, 133, 149, 132, 147, + 146, 150, 118, 147, 135, 150, 147, 152, 150, 153, 14, 55, 25, 69, 55, + 96, 69, 122, 18, 59, 28, 71, 78, 109, 83, 124, 41, 66, 69, 106, 66, + 100, 106, 128, 49, 71, 71, 107, 99, 112, 112, 132, 18, 78, 28, 83, + 59, 109, 71, 124, 24, 79, 30, 84, 79, 115, 84, 125, 49, 99, 71, 112, + 71, 112, 107, 132, 63, 101, 73, 113, 101, 117, 113, 134, 18, 59, 28, + 71, 78, 109, 83, 124, 22, 61, 30, 73, 90, 129, 91, 130, 59, 81, 83, + 112, 109, 116, 131, 145, 61, 84, 84, 113, 129, 133, 133, 146, 28, 83, + 31, 87, 83, 131, 87, 132, 30, 84, 32, 88, 91, 133, 92, 134, 71, 112, + 87, 118, 124, 145, 132, 147, 73, 113, 88, 119, 130, 146, 134, 148, + 41, 66, 69, 106, 66, 100, 106, 128, 59, 81, 83, 112, 109, 116, 131, + 145, 46, 72, 122, 128, 72, 102, 128, 141, 71, 87, 124, 132, 112, 118, + 145, 147, 59, 109, 83, 131, 81, 116, 112, 145, 79, 115, 91, 133, 115, + 138, 133, 149, 71, 112, 124, 145, 87, 118, 132, 147, 101, 117, 130, + 146, 117, 139, 146, 150, 49, 71, 71, 107, 99, 112, 112, 132, 61, 84, + 84, 113, 129, 133, 133, 146, 71, 87, 124, 132, 112, 118, 145, 147, + 84, 92, 125, 134, 133, 135, 149, 150, 71, 124, 87, 132, 112, 145, + 118, 147, 84, 125, 92, 134, 133, 149, 135, 150, 107, 132, 132, 147, + 132, 147, 147, 152, 113, 134, 134, 148, 146, 150, 150, 153, 18, 78, + 28, 83, 59, 109, 71, 124, 28, 83, 31, 87, 83, 131, 87, 132, 59, 109, + 83, 131, 81, 116, 112, 145, 71, 124, 87, 132, 112, 145, 118, 147, 22, + 90, 30, 91, 61, 129, 73, 130, 30, 91, 32, 92, 84, 133, 88, 134, 61, + 129, 84, 133, 84, 133, 113, 146, 73, 130, 88, 134, 113, 146, 119, + 148, 24, 79, 30, 84, 79, 115, 84, 125, 30, 84, 32, 88, 91, 133, 92, + 134, 79, 115, 91, 133, 115, 138, 133, 149, 84, 125, 92, 134, 133, + 149, 135, 150, 30, 91, 32, 92, 84, 133, 88, 134, 32, 92, 33, 93, 92, + 135, 93, 136, 84, 133, 92, 135, 125, 149, 134, 150, 88, 134, 93, 136, + 134, 150, 136, 151, 49, 99, 71, 112, 71, 112, 107, 132, 71, 112, 87, + 118, 124, 145, 132, 147, 71, 112, 124, 145, 87, 118, 132, 147, 107, + 132, 132, 147, 132, 147, 147, 152, 61, 129, 84, 133, 84, 133, 113, + 146, 84, 133, 92, 135, 125, 149, 134, 150, 84, 133, 125, 149, 92, + 135, 134, 150, 113, 146, 134, 150, 134, 150, 148, 153, 63, 101, 73, + 113, 101, 117, 113, 134, 73, 113, 88, 119, 130, 146, 134, 148, 101, + 117, 130, 146, 117, 139, 146, 150, 113, 134, 134, 148, 146, 150, 150, + 153, 73, 130, 88, 134, 113, 146, 119, 148, 88, 134, 93, 136, 134, + 150, 136, 151, 113, 146, 134, 150, 134, 150, 148, 153, 119, 148, 136, + 151, 148, 153, 151, 154, 11, 36, 36, 42, 14, 41, 41, 46, 36, 42, 42, + 62, 55, 66, 66, 72, 14, 41, 55, 66, 18, 49, 59, 71, 41, 46, 66, 72, + 59, 71, 81, 87, 14, 55, 41, 66, 18, 59, 49, 71, 41, 66, 46, 72, 59, + 81, 71, 87, 18, 59, 59, 81, 22, 61, 61, 84, 49, 71, 71, 87, 61, 84, + 84, 92, 36, 42, 42, 62, 55, 66, 66, 72, 42, 62, 62, 94, 96, 100, 100, + 102, 55, 66, 96, 100, 78, 99, 109, 112, 66, 72, 100, 102, 109, 112, + 116, 118, 55, 96, 66, 100, 78, 109, 99, 112, 66, 100, 72, 102, 109, + 116, 112, 118, 78, 109, 109, 116, 90, 129, 129, 133, 99, 112, 112, + 118, 129, 133, 133, 135, 14, 41, 55, 66, 18, 49, 59, 71, 55, 66, 96, + 100, 78, 99, 109, 112, 18, 49, 78, 99, 24, 63, 79, 101, 59, 71, 109, + 112, 79, 101, 115, 117, 25, 69, 69, 106, 28, 71, 71, 107, 69, 106, + 122, 128, 83, 112, 124, 132, 28, 71, 83, 112, 30, 73, 84, 113, 71, + 107, 124, 132, 84, 113, 125, 134, 41, 46, 66, 72, 59, 71, 81, 87, 66, + 72, 100, 102, 109, 112, 116, 118, 59, 71, 109, 112, 79, 101, 115, + 117, 81, 87, 116, 118, 115, 117, 138, 139, 69, 122, 106, 128, 83, + 124, 112, 132, 106, 128, 128, 141, 131, 145, 145, 147, 83, 124, 131, + 145, 91, 130, 133, 146, 112, 132, 145, 147, 133, 146, 149, 150, 14, + 55, 41, 66, 18, 59, 49, 71, 55, 96, 66, 100, 78, 109, 99, 112, 25, + 69, 69, 106, 28, 71, 71, 107, 69, 122, 106, 128, 83, 124, 112, 132, + 18, 78, 49, 99, 24, 79, 63, 101, 59, 109, 71, 112, 79, 115, 101, 117, + 28, 83, 71, 112, 30, 84, 73, 113, 71, 124, 107, 132, 84, 125, 113, + 134, 41, 66, 46, 72, 59, 81, 71, 87, 66, 100, 72, 102, 109, 116, 112, + 118, 69, 106, 122, 128, 83, 112, 124, 132, 106, 128, 128, 141, 131, + 145, 145, 147, 59, 109, 71, 112, 79, 115, 101, 117, 81, 116, 87, 118, + 115, 138, 117, 139, 83, 131, 124, 145, 91, 133, 130, 146, 112, 145, + 132, 147, 133, 149, 146, 150, 18, 59, 59, 81, 22, 61, 61, 84, 78, + 109, 109, 116, 90, 129, 129, 133, 28, 71, 83, 112, 30, 73, 84, 113, + 83, 124, 131, 145, 91, 130, 133, 146, 28, 83, 71, 112, 30, 84, 73, + 113, 83, 131, 124, 145, 91, 133, 130, 146, 31, 87, 87, 118, 32, 88, + 88, 119, 87, 132, 132, 147, 92, 134, 134, 148, 49, 71, 71, 87, 61, + 84, 84, 92, 99, 112, 112, 118, 129, 133, 133, 135, 71, 107, 124, 132, + 84, 113, 125, 134, 112, 132, 145, 147, 133, 146, 149, 150, 71, 124, + 107, 132, 84, 125, 113, 134, 112, 145, 132, 147, 133, 149, 146, 150, + 87, 132, 132, 147, 92, 134, 134, 148, 118, 147, 147, 152, 135, 150, + 150, 153, 14, 55, 55, 96, 25, 69, 69, 122, 41, 66, 66, 100, 69, 106, + 106, 128, 18, 59, 78, 109, 28, 71, 83, 124, 49, 71, 99, 112, 71, 107, + 112, 132, 18, 78, 59, 109, 28, 83, 71, 124, 49, 99, 71, 112, 71, 112, + 107, 132, 24, 79, 79, 115, 30, 84, 84, 125, 63, 101, 101, 117, 73, + 113, 113, 134, 41, 66, 66, 100, 69, 106, 106, 128, 46, 72, 72, 102, + 122, 128, 128, 141, 59, 81, 109, 116, 83, 112, 131, 145, 71, 87, 112, + 118, 124, 132, 145, 147, 59, 109, 81, 116, 83, 131, 112, 145, 71, + 112, 87, 118, 124, 145, 132, 147, 79, 115, 115, 138, 91, 133, 133, + 149, 101, 117, 117, 139, 130, 146, 146, 150, 18, 59, 78, 109, 28, 71, + 83, 124, 59, 81, 109, 116, 83, 112, 131, 145, 22, 61, 90, 129, 30, + 73, 91, 130, 61, 84, 129, 133, 84, 113, 133, 146, 28, 83, 83, 131, + 31, 87, 87, 132, 71, 112, 124, 145, 87, 118, 132, 147, 30, 84, 91, + 133, 32, 88, 92, 134, 73, 113, 130, 146, 88, 119, 134, 148, 49, 71, + 99, 112, 71, 107, 112, 132, 71, 87, 112, 118, 124, 132, 145, 147, 61, + 84, 129, 133, 84, 113, 133, 146, 84, 92, 133, 135, 125, 134, 149, + 150, 71, 124, 112, 145, 87, 132, 118, 147, 107, 132, 132, 147, 132, + 147, 147, 152, 84, 125, 133, 149, 92, 134, 135, 150, 113, 134, 146, + 150, 134, 148, 150, 153, 18, 78, 59, 109, 28, 83, 71, 124, 59, 109, + 81, 116, 83, 131, 112, 145, 28, 83, 83, 131, 31, 87, 87, 132, 71, + 124, 112, 145, 87, 132, 118, 147, 22, 90, 61, 129, 30, 91, 73, 130, + 61, 129, 84, 133, 84, 133, 113, 146, 30, 91, 84, 133, 32, 92, 88, + 134, 73, 130, 113, 146, 88, 134, 119, 148, 49, 99, 71, 112, 71, 112, + 107, 132, 71, 112, 87, 118, 124, 145, 132, 147, 71, 112, 124, 145, + 87, 118, 132, 147, 107, 132, 132, 147, 132, 147, 147, 152, 61, 129, + 84, 133, 84, 133, 113, 146, 84, 133, 92, 135, 125, 149, 134, 150, 84, + 133, 125, 149, 92, 135, 134, 150, 113, 146, 134, 150, 134, 150, 148, + 153, 24, 79, 79, 115, 30, 84, 84, 125, 79, 115, 115, 138, 91, 133, + 133, 149, 30, 84, 91, 133, 32, 88, 92, 134, 84, 125, 133, 149, 92, + 134, 135, 150, 30, 91, 84, 133, 32, 92, 88, 134, 84, 133, 125, 149, + 92, 135, 134, 150, 32, 92, 92, 135, 33, 93, 93, 136, 88, 134, 134, + 150, 93, 136, 136, 151, 63, 101, 101, 117, 73, 113, 113, 134, 101, + 117, 117, 139, 130, 146, 146, 150, 73, 113, 130, 146, 88, 119, 134, + 148, 113, 134, 146, 150, 134, 148, 150, 153, 73, 130, 113, 146, 88, + 134, 119, 148, 113, 146, 134, 150, 134, 150, 148, 153, 88, 134, 134, + 150, 93, 136, 136, 151, 119, 148, 148, 153, 136, 151, 151, 154, 34, + 37, 37, 43, 37, 43, 43, 47, 37, 43, 43, 63, 64, 67, 67, 73, 37, 43, + 64, 67, 43, 63, 67, 73, 43, 47, 67, 73, 67, 73, 85, 88, 37, 64, 43, + 67, 43, 67, 63, 73, 43, 67, 47, 73, 67, 85, 73, 88, 43, 67, 67, 85, + 47, 73, 73, 88, 63, 73, 73, 88, 73, 88, 88, 93, 37, 43, 43, 63, 64, + 67, 67, 73, 43, 63, 63, 95, 97, 101, 101, 103, 64, 67, 97, 101, 97, + 101, 110, 113, 67, 73, 101, 103, 110, 113, 117, 119, 64, 97, 67, 101, + 97, 110, 101, 113, 67, 101, 73, 103, 110, 117, 113, 119, 97, 110, + 110, 117, 126, 130, 130, 134, 101, 113, 113, 119, 130, 134, 134, 136, + 37, 43, 64, 67, 43, 63, 67, 73, 64, 67, 97, 101, 97, 101, 110, 113, + 43, 63, 97, 101, 63, 95, 101, 103, 67, 73, 110, 113, 101, 103, 117, + 119, 64, 97, 97, 110, 67, 101, 101, 113, 97, 110, 126, 130, 110, 117, + 130, 134, 67, 101, 110, 117, 73, 103, 113, 119, 101, 113, 130, 134, + 113, 119, 134, 136, 43, 47, 67, 73, 67, 73, 85, 88, 67, 73, 101, 103, + 110, 113, 117, 119, 67, 73, 110, 113, 101, 103, 117, 119, 85, 88, + 117, 119, 117, 119, 139, 140, 97, 126, 110, 130, 110, 130, 117, 134, + 110, 130, 130, 142, 143, 146, 146, 148, 110, 130, 143, 146, 130, 142, + 146, 148, 117, 134, 146, 148, 146, 148, 150, 151, 37, 64, 43, 67, 43, + 67, 63, 73, 64, 97, 67, 101, 97, 110, 101, 113, 64, 97, 97, 110, 67, + 101, 101, 113, 97, 126, 110, 130, 110, 130, 117, 134, 43, 97, 63, + 101, 63, 101, 95, 103, 67, 110, 73, 113, 101, 117, 103, 119, 67, 110, + 101, 117, 73, 113, 103, 119, 101, 130, 113, 134, 113, 134, 119, 136, + 43, 67, 47, 73, 67, 85, 73, 88, 67, 101, 73, 103, 110, 117, 113, 119, + 97, 110, 126, 130, 110, 117, 130, 134, 110, 130, 130, 142, 143, 146, + 146, 148, 67, 110, 73, 113, 101, 117, 103, 119, 85, 117, 88, 119, + 117, 139, 119, 140, 110, 143, 130, 146, 130, 146, 142, 148, 117, 146, + 134, 148, 146, 150, 148, 151, 43, 67, 67, 85, 47, 73, 73, 88, 97, + 110, 110, 117, 126, 130, 130, 134, 67, 101, 110, 117, 73, 103, 113, + 119, 110, 130, 143, 146, 130, 142, 146, 148, 67, 110, 101, 117, 73, + 113, 103, 119, 110, 143, 130, 146, 130, 146, 142, 148, 85, 117, 117, + 139, 88, 119, 119, 140, 117, 146, 146, 150, 134, 148, 148, 151, 63, + 73, 73, 88, 73, 88, 88, 93, 101, 113, 113, 119, 130, 134, 134, 136, + 101, 113, 130, 134, 113, 119, 134, 136, 117, 134, 146, 148, 146, 148, + 150, 151, 101, 130, 113, 134, 113, 134, 119, 136, 117, 146, 134, 148, + 146, 150, 148, 151, 117, 146, 146, 150, 134, 148, 148, 151, 139, 150, + 150, 153, 150, 153, 153, 154, 37, 64, 64, 97, 64, 97, 97, 126, 43, + 67, 67, 101, 97, 110, 110, 130, 43, 67, 97, 110, 67, 101, 110, 130, + 63, 73, 101, 113, 101, 113, 117, 134, 43, 97, 67, 110, 67, 110, 101, + 130, 63, 101, 73, 113, 101, 117, 113, 134, 63, 101, 101, 117, 73, + 113, 113, 134, 95, 103, 103, 119, 103, 119, 119, 136, 43, 67, 67, + 101, 97, 110, 110, 130, 47, 73, 73, 103, 126, 130, 130, 142, 67, 85, + 110, 117, 110, 117, 143, 146, 73, 88, 113, 119, 130, 134, 146, 148, + 67, 110, 85, 117, 110, 143, 117, 146, 73, 113, 88, 119, 130, 146, + 134, 148, 101, 117, 117, 139, 130, 146, 146, 150, 103, 119, 119, 140, + 142, 148, 148, 151, 43, 67, 97, 110, 67, 101, 110, 130, 67, 85, 110, + 117, 110, 117, 143, 146, 47, 73, 126, 130, 73, 103, 130, 142, 73, 88, + 130, 134, 113, 119, 146, 148, 67, 110, 110, 143, 85, 117, 117, 146, + 101, 117, 130, 146, 117, 139, 146, 150, 73, 113, 130, 146, 88, 119, + 134, 148, 103, 119, 142, 148, 119, 140, 148, 151, 63, 73, 101, 113, + 101, 113, 117, 134, 73, 88, 113, 119, 130, 134, 146, 148, 73, 88, + 130, 134, 113, 119, 146, 148, 88, 93, 134, 136, 134, 136, 150, 151, + 101, 130, 117, 146, 117, 146, 139, 150, 113, 134, 134, 148, 146, 150, + 150, 153, 113, 134, 146, 150, 134, 148, 150, 153, 119, 136, 148, 151, + 148, 151, 153, 154, 43, 97, 67, 110, 67, 110, 101, 130, 67, 110, 85, + 117, 110, 143, 117, 146, 67, 110, 110, 143, 85, 117, 117, 146, 101, + 130, 117, 146, 117, 146, 139, 150, 47, 126, 73, 130, 73, 130, 103, + 142, 73, 130, 88, 134, 113, 146, 119, 148, 73, 130, 113, 146, 88, + 134, 119, 148, 103, 142, 119, 148, 119, 148, 140, 151, 63, 101, 73, + 113, 101, 117, 113, 134, 73, 113, 88, 119, 130, 146, 134, 148, 101, + 117, 130, 146, 117, 139, 146, 150, 113, 134, 134, 148, 146, 150, 150, + 153, 73, 130, 88, 134, 113, 146, 119, 148, 88, 134, 93, 136, 134, + 150, 136, 151, 113, 146, 134, 150, 134, 150, 148, 153, 119, 148, 136, + 151, 148, 153, 151, 154, 63, 101, 101, 117, 73, 113, 113, 134, 101, + 117, 117, 139, 130, 146, 146, 150, 73, 113, 130, 146, 88, 119, 134, + 148, 113, 134, 146, 150, 134, 148, 150, 153, 73, 130, 113, 146, 88, + 134, 119, 148, 113, 146, 134, 150, 134, 150, 148, 153, 88, 134, 134, + 150, 93, 136, 136, 151, 119, 148, 148, 153, 136, 151, 151, 154, 95, + 103, 103, 119, 103, 119, 119, 136, 103, 119, 119, 140, 142, 148, 148, + 151, 103, 119, 142, 148, 119, 140, 148, 151, 119, 136, 148, 151, 148, + 151, 153, 154, 103, 142, 119, 148, 119, 148, 140, 151, 119, 148, 136, + 151, 148, 153, 151, 154, 119, 148, 148, 153, 136, 151, 151, 154, 140, + 151, 151, 154, 151, 154, 154, 155 +}; + +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_isographs_5u[] = { + 0, 1, 3, 7, 11, 12, 13, 15, 30, 31, 63, 75, 76, 77, 79, 86, 87, 94, + 95, 116, 117, 119, 127, 222, 223, 235, 236, 237, 239, 254, 255, 507, + 511, 1023 +}; +const unsigned int igraph_i_isographs_6u[] = { + 0, 1, 3, 7, 11, 12, 13, 15, 30, 31, 63, 75, 76, 77, 79, 86, 87, 94, + 95, 116, 117, 119, 127, 222, 223, 235, 236, 237, 239, 254, 255, 507, + 511, 1023, 1099, 1100, 1101, 1103, 1108, 1109, 1110, 1111, 1118, + 1119, 1140, 1141, 1143, 1151, 1182, 1183, 1184, 1185, 1187, 1191, + 1194, 1195, 1196, 1197, 1198, 1199, 1214, 1215, 1246, 1247, 1259, + 1260, 1261, 1263, 1268, 1269, 1270, 1271, 1278, 1279, 1456, 1457, + 1459, 1460, 1461, 1463, 1465, 1467, 1468, 1469, 1471, 1531, 1532, + 1533, 1535, 1972, 1973, 1975, 1983, 2047, 3294, 3295, 3306, 3307, + 3308, 3309, 3310, 3311, 3326, 3327, 3440, 3441, 3443, 3447, 3448, + 3449, 3451, 3452, 3453, 3455, 3576, 3577, 3578, 3579, 3582, 3583, + 3873, 3875, 3879, 3885, 3887, 3903, 3947, 3948, 3949, 3950, 3951, + 3958, 3959, 3966, 3967, 4094, 4095, 7672, 7673, 7675, 7679, 7902, + 7903, 7915, 7916, 7917, 7919, 7934, 7935, 8185, 8187, 8191, 16350, + 16351, 16383, 32767 +}; + +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 }; +const unsigned int igraph_i_classedges_5u[] = { 3, 4, 2, 4, 1, 4, 0, 4, 2, 3, 1, 3, + 0, 3, 1, 2, 0, 2, 0, 1 }; +const unsigned int igraph_i_classedges_6u[] = { 4, 5, 3, 5, 2, 5, 1, 5, 0, 5, 3, 4, + 2, 4, 1, 4, 0, 4, 2, 3, 1, 3, 0, 3, + 1, 2, 0, 2, 0, 1 }; + +/** + * \function igraph_isoclass + * \brief Determine the isomorphism class of small graphs. + * + * + * 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. + * + * + * 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. + * + * + * The first isomorphism class is numbered zero and it contains the edgeless + * graph. The last isomorphism class contains the full graph. The number of + * isomorphism classes 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). + * For 5 and 6 vertex undirected graphs, it is 34 and 156, respectively. + * For more information, see https://oeis.org/A000273 and https://oeis.org/A000088. + * + * + * At the moment, 3- and 4-vertex directed graphs and 3 to 6 vertex + * undirected graphs are supported. + * + * + * Multi-edges and self-loops are ignored by this function. + * + * \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. + * + * + * 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); + unsigned int idx, mul; + const unsigned int *arr_idx, *arr_code; + unsigned int code; + + if (igraph_is_directed(graph)) { + switch (no_of_nodes) { + case 3: + arr_idx = igraph_i_isoclass_3_idx; + arr_code = igraph_i_isoclass2_3; + mul = 3; + break; + case 4: + arr_idx = igraph_i_isoclass_4_idx; + arr_code = igraph_i_isoclass2_4; + mul = 4; + break; + default: + IGRAPH_ERROR("Directed isoclass is only implemented for graphs with 3 or 4 vertices.", + IGRAPH_UNIMPLEMENTED); + } + } else { + switch (no_of_nodes) { + case 3: + arr_idx = igraph_i_isoclass_3u_idx; + arr_code = igraph_i_isoclass2_3u; + mul = 3; + break; + case 4: + arr_idx = igraph_i_isoclass_4u_idx; + arr_code = igraph_i_isoclass2_4u; + mul = 4; + break; + case 5: + arr_idx = igraph_i_isoclass_5u_idx; + arr_code = igraph_i_isoclass2_5u; + mul = 5; + break; + case 6: + arr_idx = igraph_i_isoclass_6u_idx; + arr_code = igraph_i_isoclass2_6u; + mul = 6; + break; + default: + IGRAPH_ERROR("Undirected isoclass is only implemented for graphs with 3 to 6 vertices.", + IGRAPH_UNIMPLEMENTED); + } + } + + code = 0; + for (e = 0; e < no_of_edges; e++) { + idx = mul * IGRAPH_FROM(graph, e) + IGRAPH_TO(graph, e); + code |= arr_idx[idx]; + } + + *isoclass = (igraph_integer_t) arr_code[code]; + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_isoclass_subgraph + * \brief The isomorphism class of a subgraph of a graph. + * + * This function identifies the isomorphism class of the subgraph + * induced the vertices specified in \p vids. + * + * + * At the moment, 3- and 4-vertex directed graphs and 3 to 6 vertex + * undirected graphs are supported. + * + * + * Multi-edges and self-loops are ignored by this function. + * + * \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, const igraph_vector_t *vids, + igraph_integer_t *isoclass) { + int subgraph_size = (int) igraph_vector_size(vids); + igraph_vector_t neis; + + unsigned int mul, idx; + const unsigned int *arr_idx, *arr_code; + unsigned int code = 0; + + long int i, j, s; + + IGRAPH_VECTOR_INIT_FINALLY(&neis, 0); + + if (igraph_is_directed(graph)) { + switch (subgraph_size) { + case 3: + arr_idx = igraph_i_isoclass_3_idx; + arr_code = igraph_i_isoclass2_3; + mul = 3; + break; + case 4: + arr_idx = igraph_i_isoclass_4_idx; + arr_code = igraph_i_isoclass2_4; + mul = 4; + break; + default: + IGRAPH_ERROR("Directed isoclass is only implemented for graphs with 3 or 4 vertices.", + IGRAPH_UNIMPLEMENTED); + } + } else { + switch (subgraph_size) { + case 3: + arr_idx = igraph_i_isoclass_3u_idx; + arr_code = igraph_i_isoclass2_3u; + mul = 3; + break; + case 4: + arr_idx = igraph_i_isoclass_4u_idx; + arr_code = igraph_i_isoclass2_4u; + mul = 4; + break; + case 5: + arr_idx = igraph_i_isoclass_5u_idx; + arr_code = igraph_i_isoclass2_5u; + mul = 5; + break; + case 6: + arr_idx = igraph_i_isoclass_6u_idx; + arr_code = igraph_i_isoclass2_6u; + mul = 6; + break; + default: + IGRAPH_ERROR("Undirected isoclass is only implemented for graphs with 3 to 6 vertices.", + IGRAPH_UNIMPLEMENTED); + } + } + + for (i = 0; i < subgraph_size; 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 = (mul * i + to); + code |= arr_idx[idx]; + } + } + } + + *isoclass = (igraph_integer_t) arr_code[code]; + igraph_vector_destroy(&neis); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_isoclass_create + * \brief Creates a graph from the given isomorphism class. + * + * + * This function creates the canonical representative graph of the + * given isomorphism class. + * + * + * The isomorphism class is an integer between 0 and the number of + * unique unlabeled (i.e. non-isomorphic) graphs on the given number + * of vertices and give directedness. See https://oeis.org/A000273 + * and https://oeis.org/A000088 for the number of directed and + * undirected graphs on \p size nodes. + * + * + * At the moment, 3- and 4-vertex directed graphs and 3 to 6 vertex + * undirected graphs are supported. + * + * \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 graphcount; + long int power; + long int pos; + unsigned int code; + + IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); + +#define CHECK_ISOCLASS(number, directed, size, graphcount) \ + IGRAPH_ERRORF( \ + "Isoclass %" IGRAPH_PRId " requested, but there are only %ld" \ + " %s graphs of size %" IGRAPH_PRId ".", IGRAPH_EINVAL, \ + (igraph_integer_t) number, graphcount, directed ? "directed" : "undirected", size) + + if (directed) { + switch (size) { + case 3: { + classedges = igraph_i_classedges_3; + graphcount = sizeof(igraph_i_isographs_3) / sizeof(igraph_i_isographs_3[0]); + + if (number < 0 || number >= graphcount) { + CHECK_ISOCLASS(number, directed, size, graphcount); + } + + code = igraph_i_isographs_3[ (long int) number]; + power = 32; + + break; + } + case 4: { + classedges = igraph_i_classedges_4; + graphcount = sizeof(igraph_i_isographs_4) / sizeof(igraph_i_isographs_4[0]); + + if (number < 0 || number >= graphcount) { + CHECK_ISOCLASS(number, directed, size, graphcount); + } + + code = igraph_i_isographs_4[ (long int) number]; + power = 2048; + + break; + } + default: + IGRAPH_ERROR("Directed isoclasses are supported only for graphs with 3 or 4 vertices.", + IGRAPH_UNIMPLEMENTED); + } + + } else { + switch (size) { + case 3: { + classedges = igraph_i_classedges_3u; + graphcount = sizeof(igraph_i_isographs_3u) / sizeof(igraph_i_isographs_3u[0]); + + if (number < 0 || number >= graphcount) { + CHECK_ISOCLASS(number, directed, size, graphcount); + } + + code = igraph_i_isographs_3u[ (long int) number]; + power = 4; + + break; + } + case 4: { + classedges = igraph_i_classedges_4u; + graphcount = sizeof(igraph_i_isographs_4u) / sizeof(igraph_i_isographs_4u[0]); + + if (number < 0 || number >= graphcount) { + CHECK_ISOCLASS(number, directed, size, graphcount); + } + + code = igraph_i_isographs_4u[ (long int) number]; + power = 32; + + break; + } + case 5: { + classedges = igraph_i_classedges_5u; + graphcount = sizeof(igraph_i_isographs_5u) / sizeof(igraph_i_isographs_5u[0]); + + if (number < 0 || number >= graphcount) { + CHECK_ISOCLASS(number, directed, size, graphcount); + } + + code = igraph_i_isographs_5u[ (long int) number]; + power = 512; + + break; + } + case 6: { + classedges = igraph_i_classedges_6u; + graphcount = sizeof(igraph_i_isographs_6u) / sizeof(igraph_i_isographs_6u[0]); + + if (number < 0 || number >= graphcount) { + CHECK_ISOCLASS(number, directed, size, graphcount); + } + + code = igraph_i_isographs_6u[ (long int) number]; + power = 16384; + + break; + } + default: + IGRAPH_ERROR("Undirected isoclasses are supported only for graphs with 3 to 6 vertices.", + IGRAPH_UNIMPLEMENTED); + } + } + +#undef CHECK_ISOCLASS + + 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 IGRAPH_SUCCESS; +} diff --git a/src/leidenalg/igraph-R/igraph_neighborhood.h b/src/rigraph/core/isomorphism/isoclasses.h similarity index 50% rename from src/leidenalg/igraph-R/igraph_neighborhood.h rename to src/rigraph/core/isomorphism/isoclasses.h index 7e2a261..67fa9a2 100644 --- a/src/leidenalg/igraph-R/igraph_neighborhood.h +++ b/src/rigraph/core/isomorphism/isoclasses.h @@ -1,50 +1,45 @@ /* -*- mode: C -*- */ -/* +/* IGraph library. - Copyright (C) 2009-2012 Gabor Csardi + Copyright (C) 2008-2020 The igraph development team 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 + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#ifndef IGRAPH_NEIGHBORHOOD_H -#define IGRAPH_NEIGHBORHOOD_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 +#ifndef IGRAPH_ISOCLASSES_H +#define IGRAPH_ISOCLASSES_H + +#include "igraph_decls.h" __BEGIN_DECLS -int igraph_neighborhood_size(const igraph_t *graph, igraph_vector_t *res, - igraph_vs_t vids, igraph_integer_t order, - igraph_neimode_t mode); -int igraph_neighborhood(const igraph_t *graph, igraph_vector_ptr_t *res, - igraph_vs_t vids, igraph_integer_t order, - igraph_neimode_t mode); -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); +extern const unsigned int igraph_i_isoclass2_3[]; +extern const unsigned int igraph_i_isoclass2_4[]; +extern const unsigned int igraph_i_isoclass2_3u[]; +extern const unsigned int igraph_i_isoclass2_4u[]; +extern const unsigned int igraph_i_isoclass2_5u[]; +extern const unsigned int igraph_i_isoclass2_6u[]; +extern const unsigned int igraph_i_isoclass_3_idx[]; +extern const unsigned int igraph_i_isoclass_4_idx[]; +extern const unsigned int igraph_i_isoclass_3u_idx[]; +extern const unsigned int igraph_i_isoclass_4u_idx[]; +extern const unsigned int igraph_i_isoclass_5u_idx[]; +extern const unsigned int igraph_i_isoclass_6u_idx[]; __END_DECLS diff --git a/src/rigraph/core/isomorphism/isomorphism_misc.c b/src/rigraph/core/isomorphism/isomorphism_misc.c new file mode 100644 index 0000000..235e5e7 --- /dev/null +++ b/src/rigraph/core/isomorphism/isomorphism_misc.c @@ -0,0 +1,115 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2006-2012 Gabor Csardi + 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_constructors.h" +#include "igraph_interface.h" +#include "igraph_iterators.h" + +/** + * \function igraph_simplify_and_colorize + * \brief Simplify the graph and compute self-loop and edge multiplicities. + * + * + * 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. + * + * + * 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/src/rigraph/core/isomorphism/lad.c b/src/rigraph/core/isomorphism/lad.c new file mode 100644 index 0000000..d6be665 --- /dev/null +++ b/src/rigraph/core/isomorphism/lad.c @@ -0,0 +1,1674 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2012 Gabor Csardi + 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 "igraph_topology.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_qsort.h" + +#include "core/interruption.h" + +#include +#include +#include +#include + + +/* 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; + +static 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; + + graph->nbVertices = no_of_nodes; + + IGRAPH_CHECK(igraph_adjlist_init(igraph, &graph->succ, IGRAPH_OUT, IGRAPH_LOOPS_ONCE, IGRAPH_MULTIPLE)); + IGRAPH_FINALLY(igraph_adjlist_destroy, &graph->succ); + + IGRAPH_VECTOR_INIT_FINALLY(&graph->nbSucc, no_of_nodes); + for (i=0; i < no_of_nodes; ++i) { + VECTOR(graph->nbSucc)[i] = igraph_vector_int_size(igraph_adjlist_get(&graph->succ, i)); + } + + 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 do not support graphs with multi-edges.", IGRAPH_EINVAL); + } + MATRIX(graph->isEdge, i, v) = 1; + } + } + + IGRAPH_FINALLY_CLEAN(3); + + return IGRAPH_SUCCESS; +} + +static void igraph_i_lad_destroyGraph(Tgraph *graph) { + igraph_matrix_char_destroy(&graph->isEdge); + igraph_adjlist_destroy(&graph->succ); + igraph_vector_destroy(&graph->nbSucc); +} + + +/* ---------------------------------------------------------*/ +/* 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; + +static bool igraph_i_lad_toFilterEmpty(Tdomain* D) { + /* return true if there is no more nodes in toFilter */ + return (D->nextOutToFilter < 0); +} + +static 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; +} + + +static 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; +} + +static 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; +} + +static 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]); +} + +static 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; +} + +static 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; +} + + +static 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; +} + + +static 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; +} + + +static 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_CHECK(igraph_i_lad_matchVertices(1, &toBeMatched, induced, D, Gp, Gt, + &invalid)); + igraph_vector_int_destroy(&toBeMatched); + IGRAPH_FINALLY_CLEAN(1); + + return invalid ? false : true; +} + + +static 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; +} + +static 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; + igraph_qsort(mu, (size_t) size_mu, sizeof(int), igraph_i_lad_qcompare); + igraph_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; +} + +static int igraph_i_lad_initDomains(bool initialDomains, + const igraph_vector_ptr_t *domains, Tdomain *D, + const Tgraph *Gp, const 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; + + ALLOC_ARRAY(val, Gp->nbVertices * Gt->nbVertices, int); + ALLOC_ARRAY(dom, Gt->nbVertices, bool); + + IGRAPH_VECTOR_INT_INIT_FINALLY(&D->globalMatchingP, Gp->nbVertices); + igraph_vector_int_fill(&D->globalMatchingP, -1L); + + IGRAPH_VECTOR_INT_INIT_FINALLY(&D->globalMatchingT, Gt->nbVertices); + igraph_vector_int_fill(&D->globalMatchingT, -1L); + + IGRAPH_VECTOR_INT_INIT_FINALLY(&D->nbVal, Gp->nbVertices); + + 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_VECTOR_INT_INIT_FINALLY(&D->toFilter, Gp->nbVertices); + + 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); + + /* On this branch, 'val' and 'matching' are unused. + * We init them anyway so that we can have a consistent destructor. */ + IGRAPH_VECTOR_INT_INIT_FINALLY(&D->val, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&D->matching, 0); + IGRAPH_FINALLY_CLEAN(12); + + return IGRAPH_SUCCESS; + } + } + IGRAPH_VECTOR_INT_INIT_FINALLY(&D->val, D->valSize); + for (i = 0; i < D->valSize; i++) { + VECTOR(D->val)[i] = val[i]; + } + + IGRAPH_VECTOR_INT_INIT_FINALLY(&D->matching, matchingSize); + igraph_vector_int_fill(&D->matching, -1); + + D->nextOutToFilter = 0; + D->lastInToFilter = (int) (Gp->nbVertices - 1); + + *empty = 0; + + igraph_free(val); + igraph_free(dom); + + IGRAPH_FINALLY_CLEAN(12); + + return IGRAPH_SUCCESS; +} + +static void igraph_i_lad_destroyDomains(Tdomain *D) { + 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_vector_int_destroy(&D->val); + igraph_vector_int_destroy(&D->matching); +} + + +/* ---------------------------------------------------------*/ +/* Coming from allDiff.c */ +/* ---------------------------------------------------------*/ + +#define white 0 +#define grey 1 +#define black 2 +#define toBeDeleted 3 +#define deleted 4 + +static void igraph_i_lad_addToDelete(int u, int* list, int* nb, int* marked) { + if (marked[u] < toBeDeleted) { + list[(*nb)++] = u; + marked[u] = toBeDeleted; + } +} + +static 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; + 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); + 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; +} + +static 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)--; +} + +static 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; +} + + +static 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 */ +/* ---------------------------------------------------------*/ + +static 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 */ +/* ---------------------------------------------------------*/ + +static 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; +} + + + +static 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 + * + * + * 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. + * + * + * + * 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. + * + * + * + * LAD works with both directed and undirected graphs. Graphs with multi-edges are not supported. + * + */ + +/** + * \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. + * + * + * 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, + const 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_FINALLY(igraph_i_lad_destroyGraph, &Gp); + + IGRAPH_CHECK(igraph_i_lad_createGraph(target, &Gt)); + IGRAPH_FINALLY(igraph_i_lad_destroyGraph, &Gt); + + if (Gp.nbVertices > Gt.nbVertices) { + goto exit3; + } + + IGRAPH_CHECK(igraph_i_lad_initDomains(initialDomains, domains, &D, &Gp, &Gt, &invalidDomain)); + IGRAPH_FINALLY(igraph_i_lad_destroyDomains, &D); + + 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: +exit2: + + igraph_i_lad_destroyDomains(&D); + IGRAPH_FINALLY_CLEAN(1); + +exit3: + + igraph_i_lad_destroyGraph(&Gt); + igraph_i_lad_destroyGraph(&Gp); + IGRAPH_FINALLY_CLEAN(2); + + return 0; +} diff --git a/src/rigraph/core/isomorphism/queries.c b/src/rigraph/core/isomorphism/queries.c new file mode 100644 index 0000000..2f55cab --- /dev/null +++ b/src/rigraph/core/isomorphism/queries.c @@ -0,0 +1,184 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2006-2020 The igraph development 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., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_topology.h" + +#include "igraph_interface.h" +#include "igraph_structural.h" + +/** + * \section about_graph_isomorphism + * + * igraph provides four set of functions to deal with graph + * isomorphism problems. + * + * 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.) + * + * 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. + * + * Functions for the Bliss algorithm constitute the third set, + * see \ref igraph_isomorphic_bliss(). + * + * 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(). + * + */ + +/** + * \function igraph_isomorphic + * \brief Decides whether two graphs are isomorphic + * + * + * In simple terms, two graphs are isomorphic if they become indistinguishable + * from each other once their vertex labels are removed (rendering the vertices + * within each graph indistiguishable). More precisely, two graphs are isomorphic + * if there is a one-to-one mapping from the vertices of the first one + * to the vertices of the second such that it transforms the edge set of the + * first graph into the edge set of the second. This mapping is called + * an \em isomorphism. + * + * Currently, this function supports simple graphs and graphs + * with self-loops, but does not support multigraphs. + * + * 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 one of the graphs has multi-edges 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 + * + * 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, multi1, multi2; + + IGRAPH_CHECK(igraph_has_multiple(graph1, &multi1)); + IGRAPH_CHECK(igraph_has_multiple(graph2, &multi2)); + + if (multi1 || multi2) { + IGRAPH_ERROR("Isomorphism testing is not implemented for multigraphs", IGRAPH_UNIMPLEMENTED); + } + + 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_FL, 0, 0)); + } + } else { + IGRAPH_CHECK(igraph_isomorphic_bliss(graph1, graph2, NULL, NULL, iso, + 0, 0, /*sh=*/ IGRAPH_BLISS_FL, 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. Multi-edges + * and self-loops are ignored by this function. + * \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_subisomorphic + * \brief 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. + * + * + * Currently this function does not support non-simple 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, NULL, NULL, NULL, NULL, iso, NULL, NULL, NULL, NULL, NULL); +} diff --git a/src/rigraph/core/isomorphism/vf2.c b/src/rigraph/core/isomorphism/vf2.c new file mode 100644 index 0000000..ade96f5 --- /dev/null +++ b/src/rigraph/core/isomorphism/vf2.c @@ -0,0 +1,1757 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2006-2012 Gabor Csardi + 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_adjlist.h" +#include "igraph_interface.h" +#include "igraph_memory.h" +#include "igraph_stack.h" + +#include "core/interruption.h" + +/** + * \section about_vf2 + * + * + * 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. + * + * + * + * VF2 supports both vertex and edge-colored graphs, as well as custom vertex or edge + * compatibility functions. + * + * + * + * 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. + * + */ + +/** + * \function igraph_isomorphic_function_vf2 + * The generic VF2 interface + * + * + * 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. + * + * 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_int_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; + long int vsize; + + 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_NO_LOOPS, IGRAPH_NO_MULTIPLE)); + IGRAPH_FINALLY(igraph_lazy_adjlist_destroy, &inadj1); + IGRAPH_CHECK(igraph_lazy_adjlist_init(graph1, &outadj1, IGRAPH_OUT, IGRAPH_NO_LOOPS, IGRAPH_NO_MULTIPLE)); + IGRAPH_FINALLY(igraph_lazy_adjlist_destroy, &outadj1); + IGRAPH_CHECK(igraph_lazy_adjlist_init(graph2, &inadj2, IGRAPH_IN, IGRAPH_NO_LOOPS, IGRAPH_NO_MULTIPLE)); + IGRAPH_FINALLY(igraph_lazy_adjlist_destroy, &inadj2); + IGRAPH_CHECK(igraph_lazy_adjlist_init(graph2, &outadj2, IGRAPH_OUT, IGRAPH_NO_LOOPS, IGRAPH_NO_MULTIPLE)); + 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); + vsize = igraph_vector_int_size(inneis_1); + for (i = 0; i < vsize; 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); + vsize = igraph_vector_int_size(outneis_1); + for (i = 0; i < vsize; 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); + vsize = igraph_vector_int_size(inneis_2); + for (i = 0; i < vsize; 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); + vsize = igraph_vector_int_size(outneis_2); + for (i = 0; i < vsize; 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; + } + + vsize = igraph_vector_int_size(inneis_1); + for (i = 0; !end && i < vsize; 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_int_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++; + } + } + } + vsize = igraph_vector_int_size(outneis_1); + for (i = 0; !end && i < vsize; 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_int_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++; + } + } + } + vsize = igraph_vector_int_size(inneis_2); + for (i = 0; !end && i < vsize; 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_int_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++; + } + } + } + vsize = igraph_vector_int_size(outneis_2); + for (i = 0; !end && i < vsize; 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_int_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); + vsize = igraph_vector_int_size(inneis_1); + for (i = 0; i < vsize; 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); + vsize = igraph_vector_int_size(outneis_1); + for (i = 0; i < vsize; 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); + vsize = igraph_vector_int_size(inneis_2); + for (i = 0; i < vsize; 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); + vsize = igraph_vector_int_size(outneis_2); + for (i = 0; i < vsize; 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; + +static 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); +} + +static 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); +} + +static 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 + * + * + * This function performs the VF2 algorithm via calling \ref + * igraph_isomorphic_function_vf2(). + * + * 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 (i.e. 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 (i.e. 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; +} + +static 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; +} + +static 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); + } +} + +static int igraph_i_get_isomorphisms_vf2_inner( + 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("", IGRAPH_ENOMEM); + } + 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 IGRAPH_SUCCESS; +} + +static igraph_bool_t igraph_i_get_isomorphisms_vf2( + const igraph_vector_t *map12, + const igraph_vector_t *map21, + void *arg) { + return igraph_i_get_isomorphisms_vf2_inner(map12, map21, arg) == IGRAPH_SUCCESS; +} + +/** + * \function igraph_get_isomorphisms_vf2 + * \brief Collect all isomorphic mappings of two graphs. + * + * This function finds all the isomorphic mappings between two simple + * 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 not isomorphic. Otherwise it contains pointers to + * \ref igraph_vector_t 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 + * \ref igraph_free() 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 : NULL; + igraph_isocompat_t *ecb = edge_compat_fn ? igraph_i_isocompat_edge_cb : NULL; + + 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, + NULL, NULL, + (igraph_isohandler_t*) + igraph_i_get_isomorphisms_vf2, + ncb, ecb, &data)); + IGRAPH_FINALLY_CLEAN(1); + return IGRAPH_SUCCESS; +} + +/** + * \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_int_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; + long int vsize; + + 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_NO_LOOPS, IGRAPH_NO_MULTIPLE)); + IGRAPH_FINALLY(igraph_lazy_adjlist_destroy, &inadj1); + IGRAPH_CHECK(igraph_lazy_adjlist_init(graph1, &outadj1, IGRAPH_OUT, IGRAPH_NO_LOOPS, IGRAPH_NO_MULTIPLE)); + IGRAPH_FINALLY(igraph_lazy_adjlist_destroy, &outadj1); + IGRAPH_CHECK(igraph_lazy_adjlist_init(graph2, &inadj2, IGRAPH_IN, IGRAPH_NO_LOOPS, IGRAPH_NO_MULTIPLE)); + IGRAPH_FINALLY(igraph_lazy_adjlist_destroy, &inadj2); + IGRAPH_CHECK(igraph_lazy_adjlist_init(graph2, &outadj2, IGRAPH_OUT, IGRAPH_NO_LOOPS, IGRAPH_NO_MULTIPLE)); + 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); + vsize = igraph_vector_int_size(inneis_1); + for (i = 0; i < vsize; 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); + vsize = igraph_vector_int_size(outneis_1); + for (i = 0; i < vsize; 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); + vsize = igraph_vector_int_size(inneis_2); + for (i = 0; i < vsize; 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); + vsize = igraph_vector_int_size(outneis_2); + for (i = 0; i < vsize; 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; + } + + vsize = igraph_vector_int_size(inneis_1); + for (i = 0; !end && i < vsize; 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++; + } + } + } + vsize = igraph_vector_int_size(outneis_1); + for (i = 0; !end && i < vsize; 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++; + } + } + } + vsize = igraph_vector_int_size(inneis_2); + for (i = 0; !end && i < vsize; 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_int_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++; + } + } + } + vsize = igraph_vector_int_size(outneis_2); + for (i = 0; !end && i < vsize; 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_int_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); + vsize = igraph_vector_int_size(inneis_1); + for (i = 0; i < vsize; 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); + vsize = igraph_vector_int_size(outneis_1); + for (i = 0; i < vsize; 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); + vsize = igraph_vector_int_size(inneis_2); + for (i = 0; i < vsize; 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); + vsize = igraph_vector_int_size(outneis_2); + for (i = 0; i < vsize; 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; +} + +static 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; +} + +static 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; +} + +static 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); + } +} + +static int igraph_i_get_subisomorphisms_vf2_inner( + 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("", IGRAPH_ENOMEM); + } + 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 IGRAPH_SUCCESS; +} + +static igraph_bool_t igraph_i_get_subisomorphisms_vf2( + const igraph_vector_t *map12, + const igraph_vector_t *map21, + void *arg) { + return igraph_i_get_subisomorphisms_vf2_inner(map12, map21, arg) == IGRAPH_SUCCESS; +} + +/** + * \function igraph_get_subisomorphisms_vf2 + * \brief 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. The graphs should be simple. + * \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 + * \ref igraph_vector_t 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 + * \ref igraph_free() 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 : NULL; + igraph_isocompat_t *ecb = edge_compat_fn ? igraph_i_isocompat_edge_cb : NULL; + + 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, + NULL, NULL, + (igraph_isohandler_t*) + igraph_i_get_subisomorphisms_vf2, + ncb, ecb, &data)); + IGRAPH_FINALLY_CLEAN(1); + return IGRAPH_SUCCESS; +} diff --git a/src/rigraph/core/layout/circular.c b/src/rigraph/core/layout/circular.c new file mode 100644 index 0000000..9a8c4ae --- /dev/null +++ b/src/rigraph/core/layout/circular.c @@ -0,0 +1,185 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2003-2020 The igraph development 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., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_layout.h" + +#include "igraph_interface.h" + +#include "core/interruption.h" +#include "core/math.h" + +/** + * \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 + * \brief Generates a star-like layout. + * + * \param graph The input graph. Its edges are ignored by this function. + * \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 (center < 0 || center >= no_of_nodes) { + IGRAPH_ERROR("The given center is not a vertex of the graph.", IGRAPH_EINVAL); + } + 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 (order && (node < 0 || node >= no_of_nodes)) { + IGRAPH_ERROR("Elements in the order vector are not all vertices of the graph.", IGRAPH_EINVAL); + } + 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. + * + * + * 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. + * + * 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; +} diff --git a/src/rigraph/core/layout/davidson_harel.c b/src/rigraph/core/layout/davidson_harel.c new file mode 100644 index 0000000..57b706f --- /dev/null +++ b/src/rigraph/core/layout/davidson_harel.c @@ -0,0 +1,464 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2003-2020 The igraph development 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., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_layout.h" + +#include "igraph_interface.h" +#include "igraph_random.h" + +#include "core/math.h" +#include "core/interruption.h" +#include "layout/layout_internal.h" + +#include + +/* not 'static', used in tests */ +igraph_bool_t igraph_i_layout_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; +} + +/* not 'static', used in tests */ +float igraph_i_layout_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. + * + * + * 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. + * + * + * The algorithm consists of two phases, an annealing phase, and a + * fine-tuning phase. There is no simulated annealing in the second + * phase. + * + * + * 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.01f; + 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); + + IGRAPH_ALLOW_INTERRUPTION(); + + 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_layout_segments_intersect(old_x, old_y, u_x, u_y, + u1_x, u1_y, u2_x, u2_y); + no += igraph_i_layout_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_layout_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_layout_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_layout_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_layout_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/src/rigraph/core/layout/drl/DensityGrid.cpp b/src/rigraph/core/layout/drl/DensityGrid.cpp new file mode 100644 index 0000000..1678db3 --- /dev/null +++ b/src/rigraph/core/layout/drl/DensityGrid.cpp @@ -0,0 +1,281 @@ +/* + * 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 "drl_Node.h" +#include "DensityGrid.h" +#include "igraph_error.h" + +#include +#include + +using namespace std; + +#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[GRID_SIZE * GRID_SIZE]; + } catch (bad_alloc&) { + // 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", IGRAPH_FILE_BASENAME, __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::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", IGRAPH_FILE_BASENAME, + __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", IGRAPH_FILE_BASENAME, + __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/src/rigraph/core/layout/drl/DensityGrid.h b/src/rigraph/core/layout/drl/DensityGrid.h new file mode 100644 index 0000000..3db6725 --- /dev/null +++ b/src/rigraph/core/layout/drl/DensityGrid.h @@ -0,0 +1,84 @@ +/* + * 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 "drl_layout.h" +#include "drl_Node.h" +#ifdef MUSE_MPI + #include +#endif + +#include + +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]; + std::deque* Bins; + + // old static variables + //float fall_off[RADIUS*2+1][RADIUS*2+1]; + //float Density[GRID_SIZE][GRID_SIZE]; + //deque Bins[GRID_SIZE][GRID_SIZE]; +}; + +} // namespace drl + +#endif // __DENSITY_GRID_H__ diff --git a/src/rigraph/core/layout/drl/DensityGrid_3d.cpp b/src/rigraph/core/layout/drl/DensityGrid_3d.cpp new file mode 100644 index 0000000..0f02f15 --- /dev/null +++ b/src/rigraph/core/layout/drl/DensityGrid_3d.cpp @@ -0,0 +1,305 @@ +/* + * 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 "drl_Node_3d.h" +#include "DensityGrid_3d.h" +#include "igraph_error.h" + +#include +#include + +using namespace std; + +#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[GRID_SIZE * GRID_SIZE * GRID_SIZE]; + } catch (bad_alloc&) { + // 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", IGRAPH_FILE_BASENAME, __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::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", IGRAPH_FILE_BASENAME, + __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", IGRAPH_FILE_BASENAME, + __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/src/rigraph/core/layout/drl/DensityGrid_3d.h b/src/rigraph/core/layout/drl/DensityGrid_3d.h new file mode 100644 index 0000000..f02ea32 --- /dev/null +++ b/src/rigraph/core/layout/drl/DensityGrid_3d.h @@ -0,0 +1,84 @@ +/* + * 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 "drl_layout_3d.h" +#include "drl_Node_3d.h" +#ifdef MUSE_MPI + #include +#endif + +#include + +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]; + std::deque* Bins; + + // old static variables + //float fall_off[RADIUS*2+1][RADIUS*2+1]; + //float Density[GRID_SIZE][GRID_SIZE]; + //deque Bins[GRID_SIZE][GRID_SIZE]; +}; + +} // namespace drl3d + +#endif // __DENSITY_GRID_H__ diff --git a/src/rigraph/core/layout/drl/drl_Node.h b/src/rigraph/core/layout/drl/drl_Node.h new file mode 100644 index 0000000..bc894c2 --- /dev/null +++ b/src/rigraph/core/layout/drl/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/src/rigraph/core/layout/drl/drl_Node_3d.h b/src/rigraph/core/layout/drl/drl_Node_3d.h new file mode 100644 index 0000000..e373d9a --- /dev/null +++ b/src/rigraph/core/layout/drl/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/src/rigraph/core/layout/drl/drl_graph.cpp b/src/rigraph/core/layout/drl/drl_graph.cpp new file mode 100644 index 0000000..71a8796 --- /dev/null +++ b/src/rigraph/core/layout/drl/drl_graph.cpp @@ -0,0 +1,1306 @@ +/* + * 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 +#include +#include + +using namespace std; + +#include "drl_graph.h" +#include "igraph_random.h" +#include "igraph_interface.h" +#include "igraph_progress.h" +#include "core/interruption.h" +#ifdef MUSE_MPI + #include +#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 >::iterator i; +// map::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 -= .05f; + } + if (min_edges > 12) { + min_edges -= .05f; + } + cut_off_length -= cut_rate; + if (damping_mult > .1) { + damping_mult -= .005f; + } + 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 -= .2f; + } + //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 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 &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 &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 ::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 ::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::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 < >::iterator i; + map::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::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/src/rigraph/core/layout/drl/drl_graph.h b/src/rigraph/core/layout/drl/drl_graph.h new file mode 100644 index 0000000..1a2804d --- /dev/null +++ b/src/rigraph/core/layout/drl/drl_graph.h @@ -0,0 +1,132 @@ +/* + * 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" + +#include +#include +#include + +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 ( std::vector &node_indices, float return_positions[2 * MAX_PROCS] ); + void update_density ( std::vector &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 + std::map id_catalog; // id_catalog[file id] = internal id + std::map > neighbors; // neighbors of nodes on this proc. + + // graph layout information + std::vector 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/src/rigraph/core/layout/drl/drl_graph_3d.cpp b/src/rigraph/core/layout/drl/drl_graph_3d.cpp new file mode 100644 index 0000000..98d11a0 --- /dev/null +++ b/src/rigraph/core/layout/drl/drl_graph_3d.cpp @@ -0,0 +1,873 @@ +/* + * 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 +#include +#include + +using namespace std; + +#include "drl_graph_3d.h" +#include "igraph_random.h" +#include "igraph_interface.h" +#include "igraph_progress.h" +#include "core/interruption.h" +#ifdef MUSE_MPI + #include +#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 -= .05f; + } + if (min_edges > 12) { + min_edges -= .05f; + } + cut_off_length -= cut_rate; + if (damping_mult > .1) { + damping_mult -= .005f; + } + 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 -= .2f; + } + //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 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 &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 &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 ::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 ::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::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::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/src/rigraph/core/layout/drl/drl_graph_3d.h b/src/rigraph/core/layout/drl/drl_graph_3d.h new file mode 100644 index 0000000..c61612c --- /dev/null +++ b/src/rigraph/core/layout/drl/drl_graph_3d.h @@ -0,0 +1,124 @@ +/* + * 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" + +#include +#include +#include + +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 ( std::vector &node_indices, float return_positions[3 * MAX_PROCS] ); + void update_density ( std::vector &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 + std::map id_catalog; // id_catalog[file id] = internal id + std::map > neighbors; // neighbors of nodes on this proc. + + // graph layout information + std::vector 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/src/rigraph/core/layout/drl/drl_layout.cpp b/src/rigraph/core/layout/drl/drl_layout.cpp new file mode 100644 index 0000000..7a4296e --- /dev/null +++ b/src/rigraph/core/layout/drl/drl_layout.cpp @@ -0,0 +1,495 @@ +/* + * 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 +#include + +using namespace std; + +// layout routines and constants +#include "drl_layout.h" +#include "drl_parse.h" +#include "drl_graph.h" + +// MPI +#ifdef MUSE_MPI + #include +#endif + +using namespace drl; +#include "igraph_layout.h" +#include "igraph_random.h" +#include "igraph_interface.h" + +#include "core/exceptions.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 + * + * + * 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. + * + * + * + * Only a subset of the complete DrL functionality is + * included in igraph, parallel runs and recursive, multi-level + * layouting is not supported. + * + * + * + * 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. + * + */ + +/** + * \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 IGRAPH_SUCCESS; +} + +/** + * \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, + const igraph_layout_drl_options_t *options, + const igraph_vector_t *weights, + const igraph_vector_bool_t *fixed) { + const char msg[] = "Damping multipliers cannot be negative, got %f."; + + if (options->init_damping_mult < 0) { + IGRAPH_ERRORF(msg, IGRAPH_EINVAL, options->init_damping_mult); + } + if (options->liquid_damping_mult < 0) { + IGRAPH_ERRORF(msg, IGRAPH_EINVAL, options->liquid_damping_mult); + } + if (options->expansion_damping_mult < 0) { + IGRAPH_ERRORF(msg, IGRAPH_EINVAL, options->expansion_damping_mult); + } + if (options->cooldown_damping_mult < 0) { + IGRAPH_ERRORF(msg, IGRAPH_EINVAL, options->cooldown_damping_mult); + } + if (options->crunch_damping_mult < 0) { + IGRAPH_ERRORF(msg, IGRAPH_EINVAL, options->crunch_damping_mult); + } + if (options->simmer_damping_mult < 0) { + IGRAPH_ERRORF(msg, IGRAPH_EINVAL, options->simmer_damping_mult); + } + + IGRAPH_HANDLE_EXCEPTIONS( + 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 IGRAPH_SUCCESS; +} diff --git a/src/rigraph/core/layout/drl/drl_layout.h b/src/rigraph/core/layout/drl/drl_layout.h new file mode 100644 index 0000000..8d3cd29 --- /dev/null +++ b/src/rigraph/core/layout/drl/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/src/rigraph/core/layout/drl/drl_layout_3d.cpp b/src/rigraph/core/layout/drl/drl_layout_3d.cpp new file mode 100644 index 0000000..7a922f0 --- /dev/null +++ b/src/rigraph/core/layout/drl/drl_layout_3d.cpp @@ -0,0 +1,121 @@ +/* + * 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 +#include + +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 +#endif + +using namespace drl3d; +#include "igraph_layout.h" +#include "igraph_random.h" +#include "igraph_interface.h" + +#include "core/exceptions.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. + * + * 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, + const igraph_layout_drl_options_t *options, + const igraph_vector_t *weights, + const igraph_vector_bool_t *fixed) { + IGRAPH_HANDLE_EXCEPTIONS( + 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/src/rigraph/core/layout/drl/drl_layout_3d.h b/src/rigraph/core/layout/drl/drl_layout_3d.h new file mode 100644 index 0000000..d9b0095 --- /dev/null +++ b/src/rigraph/core/layout/drl/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/src/rigraph/core/layout/drl/drl_parse.cpp b/src/rigraph/core/layout/drl/drl_parse.cpp new file mode 100644 index 0000000..c0e98cd --- /dev/null +++ b/src/rigraph/core/layout/drl/drl_parse.cpp @@ -0,0 +1,197 @@ +/* + * 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 "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 node_id 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 x-coord 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 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) ) +// 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/src/rigraph/core/layout/drl/drl_parse.h b/src/rigraph/core/layout/drl/drl_parse.h new file mode 100644 index 0000000..a772478 --- /dev/null +++ b/src/rigraph/core/layout/drl/drl_parse.h @@ -0,0 +1,72 @@ +/* + * 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 +#endif + +#include + +namespace drl { + +class parse { + +public: + + // Methods + + parse ( int argc, char **argv ); + ~parse () {} + + // user parameters + std::string sim_file; // .sim file + std::string coord_file; // .coord file + std::string parms_file; // .parms file + std::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/src/rigraph/core/layout/fruchterman_reingold.c b/src/rigraph/core/layout/fruchterman_reingold.c new file mode 100644 index 0000000..98b5772 --- /dev/null +++ b/src/rigraph/core/layout/fruchterman_reingold.c @@ -0,0 +1,709 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2003-2020 The igraph development 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., 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 "core/grid.h" +#include "core/interruption.h" + +static 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 = 0; + + IGRAPH_CHECK(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; + + IGRAPH_ALLOW_INTERRUPTION(); + + /* 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; +} + +static 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_ALLOW_INTERRUPTION(); + + 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. + * + * + * 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 allowed 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. + * + * 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 = 0; + + 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_CHECK(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; + + IGRAPH_ALLOW_INTERRUPTION(); + + /* 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/src/rigraph/core/layout/gem.c b/src/rigraph/core/layout/gem.c new file mode 100644 index 0000000..cd49656 --- /dev/null +++ b/src/rigraph/core/layout/gem.c @@ -0,0 +1,250 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2003-2020 The igraph development 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., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_layout.h" + +#include "igraph_interface.h" +#include "igraph_random.h" + +#include "core/math.h" +#include "core/interruption.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, barycenter_y = 0; + igraph_vector_t phi; + igraph_vector_t neis; + const float elen_des2 = 128 * 128; + const float gamma = 1 / 16.0f; + const float alpha_o = (float)M_PI; + const float alpha_r = (float)M_PI / 3.0f; + const float sigma_o = 1.0f / 3.0f; + const float sigma_r = 1.0f / 2.0f / 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) { + igraph_integer_t u, v, nlen, j; + float px, py, pvx, pvy; + + IGRAPH_ALLOW_INTERRUPTION(); + + /* choose a vertex v to update */ + 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/src/rigraph/core/layout/graphopt.c b/src/rigraph/core/layout/graphopt.c new file mode 100644 index 0000000..1668932 --- /dev/null +++ b/src/rigraph/core/layout/graphopt.c @@ -0,0 +1,444 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2003-2020 The igraph development 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., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_layout.h" + +#include "igraph_interface.h" +#include "igraph_progress.h" + +#include "core/interruption.h" + +#define COULOMBS_CONSTANT 8987500000.0 + + +static igraph_real_t igraph_i_distance_between( + const igraph_matrix_t *c, + long int a, long int b); + +static 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); + +static 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); + +static 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, + igraph_real_t spring_length, + long int other_node, + long int this_node); + +static 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, igraph_real_t spring_length, + igraph_real_t spring_constant); + +static 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); + +static 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 ); +} + +static 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; +} + +static 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; +} + +static 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, + igraph_real_t 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; +} + +static 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, igraph_real_t 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; +} + +static 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. + * + * + * 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. + * + * + * 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.) + * + * + * 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 are used as 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 starting 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 is 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. + * 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); + 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, 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 IGRAPH_SUCCESS; +} diff --git a/src/rigraph/core/layout/kamada_kawai.c b/src/rigraph/core/layout/kamada_kawai.c new file mode 100644 index 0000000..49143f8 --- /dev/null +++ b/src/rigraph/core/layout/kamada_kawai.c @@ -0,0 +1,718 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2003-2020 The igraph development 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., 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" + +#include "core/interruption.h" + +/** + * \ingroup layout + * \function igraph_layout_kamada_kawai + * \brief Places the vertices on a plane according to the Kamada-Kawai algorithm. + * + * This is a force-directed layout. A spring is inserted between all pairs + * of vertices, both those which are directly connected and those that are not. + * The unstretched length of springs is chosen based on the graph distance + * between the corresponding pair of vertices. Thus, in a weighted graph, increasing + * the weight between two vertices pushes them apart. The Young modulus of springs + * is inversely proportional to the graph distance, ensuring that springs between + * far-apart veritces will have a smaller effect on the layout. + * + * + * This layout algorithm is not suitable for large graphs. The memory + * requirements are of the order O(|V|^2). + * + * + * Reference: + * + * + * Kamada, T. and Kawai, S.: + * An Algorithm for Drawing General Undirected Graphs. + * Information Processing Letters, 31/1, 7--15, 1989. + * https://doi.org/10.1016/0020-0190(89)90102-6 + * + * \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 and there + * are any limits on the X or Y coordinates, then a random initial + * configuration is used. Otherwise the vertices are placed on a + * circle of radius 1 as the initial configuration. + * \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 && no_edges > 0 && 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; + + IGRAPH_ALLOW_INTERRUPTION(); + + 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, 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. + * + * This is the 3D version of igraph_layout_kamada_kawai(). + * See the documentation of that function for more information. + * + * + * This layout algorithm is not suitable for large graphs. The memory + * requirements are of the order O(|V|^2). + * + * \param graph A graph object. + * \param res Pointer to an initialized matrix object. This will + * contain the result (x-, y- and z-positions in columns one + * through three) 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 and there + * are any limits on the z, y or z coordinates, then a random initial + * configuration is used. Otherwise the vertices are placed uniformly + * on a sphere of radius 1 as the initial configuration. + * \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; + + IGRAPH_ALLOW_INTERRUPTION(); + + /* 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); + if (detnum != 0) { + 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; + } else { + /* No new stable position for node m; this can happen in rare + * cases, e.g., if the graph has two nodes only. It's best to leave + * the node where it is. */ + delta_x = delta_y = delta_z = 0; + } + + 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/src/rigraph/core/layout/large_graph.c b/src/rigraph/core/layout/large_graph.c new file mode 100644 index 0000000..9dfd518 --- /dev/null +++ b/src/rigraph/core/layout/large_graph.c @@ -0,0 +1,349 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2003-2020 The igraph development 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., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_layout.h" + +#include "igraph_adjlist.h" +#include "igraph_interface.h" +#include "igraph_progress.h" +#include "igraph_random.h" +#include "igraph_structural.h" +#include "igraph_visitor.h" + +#include "core/grid.h" +#include "core/interruption.h" +#include "core/math.h" + +static 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. + * + * + * 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. + * + * 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_bfs_simple(&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; + +} diff --git a/src/rigraph/core/layout/layout_bipartite.c b/src/rigraph/core/layout/layout_bipartite.c new file mode 100644 index 0000000..ebd4540 --- /dev/null +++ b/src/rigraph/core/layout/layout_bipartite.c @@ -0,0 +1,81 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2003-2020 The igraph development 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., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_layout.h" + +#include "igraph_interface.h" + +/** + * \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_ERRORF("The vertex type vector size (%ld) should be equal to the number of nodes (%ld).", + IGRAPH_EINVAL, igraph_vector_bool_size(types), no_of_nodes); + } + if (hgap < 0) { + IGRAPH_ERRORF("The horizontal gap cannot be negative, got %f.", IGRAPH_EINVAL, hgap); + } + + IGRAPH_VECTOR_INIT_FINALLY(&layers, no_of_nodes); + for (i = 0; i < no_of_nodes; i++) { + VECTOR(layers)[i] = VECTOR(*types)[i] ? 0 : 1; + } + + 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 IGRAPH_SUCCESS; +} diff --git a/src/rigraph/core/layout/layout_grid.c b/src/rigraph/core/layout/layout_grid.c new file mode 100644 index 0000000..144fdb6 --- /dev/null +++ b/src/rigraph/core/layout/layout_grid.c @@ -0,0 +1,113 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2003-2020 The igraph development 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., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_layout.h" + +#include "igraph_interface.h" + +/** + * \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; +} diff --git a/src/rigraph/core/layout/layout_internal.h b/src/rigraph/core/layout/layout_internal.h new file mode 100644 index 0000000..a6647d2 --- /dev/null +++ b/src/rigraph/core/layout/layout_internal.h @@ -0,0 +1,55 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2021 The igraph development 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., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#ifndef IGRAPH_LAYOUT_INTERNAL_H +#define IGRAPH_LAYOUT_INTERNAL_H + +#include "igraph_types.h" + +#include "layout/merge_grid.h" + +__BEGIN_DECLS + +IGRAPH_PRIVATE_EXPORT 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); + +IGRAPH_PRIVATE_EXPORT int igraph_i_layout_sphere_2d(igraph_matrix_t *coords, igraph_real_t *x, + igraph_real_t *y, igraph_real_t *r); + +IGRAPH_PRIVATE_EXPORT 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); + +IGRAPH_PRIVATE_EXPORT float igraph_i_layout_point_segment_dist2(float v_x, float v_y, + float u1_x, float u1_y, + float u2_x, float u2_y); + +IGRAPH_PRIVATE_EXPORT igraph_bool_t igraph_i_layout_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); + +__END_DECLS + +#endif diff --git a/src/rigraph/core/layout/layout_random.c b/src/rigraph/core/layout/layout_random.c new file mode 100644 index 0000000..7c19c74 --- /dev/null +++ b/src/rigraph/core/layout/layout_random.c @@ -0,0 +1,98 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2003-2020 The igraph development 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., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_layout.h" + +#include "igraph_interface.h" +#include "igraph_random.h" + +/** + * \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 Places the vertices uniform randomly in a cube. + * + * + * Vertex coordinates range from -1 to 1, and are placed in 3 columns + * of a matrix, with a row for each vertex. + * + * \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. + * + * 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 IGRAPH_SUCCESS; +} diff --git a/src/rigraph/core/layout/mds.c b/src/rigraph/core/layout/mds.c new file mode 100644 index 0000000..677b3ab --- /dev/null +++ b/src/rigraph/core/layout/mds.c @@ -0,0 +1,305 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2003-2020 The igraph development 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., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_layout.h" + +#include "igraph_blas.h" +#include "igraph_components.h" +#include "igraph_eigen.h" +#include "igraph_interface.h" +#include "igraph_memory.h" +#include "igraph_operators.h" +#include "igraph_paths.h" +#include "igraph_random.h" +#include "igraph_structural.h" + +static int igraph_i_layout_mds_step(igraph_real_t *to, const igraph_real_t *from, + int n, void *extra); + +static int igraph_i_layout_mds_single(const igraph_t* graph, igraph_matrix_t *res, + igraph_matrix_t *dist, long int dim); + +static 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. + * + * + * 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 & Cox: Multidimensional Scaling (1994), Chapman + * and Hall, London. + * + * + * 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. + * + * + * 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. + * \return Error code. + * + * Added in version 0.6. + * + * + * 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) { + 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; +} diff --git a/src/rigraph/core/layout/merge_dla.c b/src/rigraph/core/layout/merge_dla.c new file mode 100644 index 0000000..bdfe338 --- /dev/null +++ b/src/rigraph/core/layout/merge_dla.c @@ -0,0 +1,295 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2003-2020 The igraph development 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., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_layout.h" +#include "igraph_progress.h" +#include "igraph_random.h" + +#include "core/grid.h" +#include "core/interruption.h" +#include "core/math.h" +#include "layout/merge_grid.h" +#include "layout/layout_internal.h" + +/** + * \function igraph_layout_merge_dla + * \brief Merge multiple layouts by using a DLA algorithm + * + * + * 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 objects 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. + * + * + * Time complexity: TODO. + */ + +int igraph_layout_merge_dla(const igraph_vector_ptr_t *thegraphs, + const 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; +} diff --git a/src/rigraph/core/layout/merge_grid.c b/src/rigraph/core/layout/merge_grid.c new file mode 100644 index 0000000..5b7d94b --- /dev/null +++ b/src/rigraph/core/layout/merge_grid.c @@ -0,0 +1,217 @@ +/* -*- mode: C -*- */ +/* + IGraph package. + Copyright (C) 2006-2021 The igraph development 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., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_memory.h" + +#include "layout/merge_grid.h" + +static 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; istepsx; i++) { */ +/* for (j=0; jstepsy; j++) { */ +/* printf("%li ", MAT(i,j)-1); */ +/* } */ +/* printf("\n"); */ +/* } */ +/* } */ diff --git a/src/rigraph/core/layout/merge_grid.h b/src/rigraph/core/layout/merge_grid.h new file mode 100644 index 0000000..70fcfee --- /dev/null +++ b/src/rigraph/core/layout/merge_grid.h @@ -0,0 +1,58 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2009-2020 The igraph development 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., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#ifndef IGRAPH_LAYOUT_MERGE_GRID_H +#define IGRAPH_LAYOUT_MERGE_GRID_H + +#include "igraph_decls.h" +#include "igraph_types.h" + +__BEGIN_DECLS + +/* A type of grid used for merging layouts; 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; + +IGRAPH_PRIVATE_EXPORT 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); + +IGRAPH_PRIVATE_EXPORT void igraph_i_layout_mergegrid_destroy(igraph_i_layout_mergegrid_t *grid); + +IGRAPH_PRIVATE_EXPORT 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); + +__END_DECLS + +#endif diff --git a/src/rigraph/core/layout/reingold_tilford.c b/src/rigraph/core/layout/reingold_tilford.c new file mode 100644 index 0000000..7d10180 --- /dev/null +++ b/src/rigraph/core/layout/reingold_tilford.c @@ -0,0 +1,965 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2003-2020 The igraph development 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., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_layout.h" + +#include "igraph_adjlist.h" +#include "igraph_components.h" +#include "igraph_dqueue.h" +#include "igraph_interface.h" +#include "igraph_memory.h" +#include "igraph_paths.h" +#include "igraph_progress.h" +#include "igraph_structural.h" + +#include "core/math.h" + +static 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_NO_LOOPS, IGRAPH_NO_MULTIPLE)); + 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_to_left_contour; /* X offset when following the left contour */ + igraph_real_t offset_to_right_contour; /* X offset when following the right contour */ + long int left_extreme; /* Leftmost node on the deepest layer of the subtree rooted at this node */ + long int right_extreme; /* Rightmost node on the deepest layer of the subtree rooted at this node */ + igraph_real_t offset_to_left_extreme; /* X offset when jumping to the left extreme node */ + igraph_real_t offset_to_right_extreme; /* X offset when jumping to the right extreme node */ +}; + +static int igraph_i_layout_reingold_tilford_postorder(struct igraph_i_reingold_tilford_vertex *vdata, + long int node, long int vcount); +static 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); + +/* uncomment the next line for debugging the Reingold-Tilford layout */ +/* #define LAYOUT_RT_DEBUG 1 */ + +static 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_NO_LOOPS, IGRAPH_NO_MULTIPLE)); + 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_to_left_contour = 0.0; + vdata[i].offset_to_right_contour = 0.0; + vdata[i].left_extreme = i; + vdata[i].right_extreme = i; + vdata[i].offset_to_left_extreme = 0.0; + vdata[i].offset_to_right_extreme = 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); + +#ifdef LAYOUT_RT_DEBUG + for (i = 0; i < no_of_nodes; i++) { + printf( + "%3ld: offset = %.2f, contours = [%ld, %ld], contour offsets = [%.2f, %.2f]\n", + i, vdata[i].offset, + vdata[i].left_contour, vdata[i].right_contour, + vdata[i].offset_to_left_contour, vdata[i].offset_to_right_contour + ); + if (vdata[i].left_extreme != i || vdata[i].right_extreme != i) { + printf( + " extrema = [%ld, %ld], offsets to extrema = [%.2f, %.2f]\n", + vdata[i].left_extreme, vdata[i].right_extreme, + vdata[i].offset_to_left_extreme, vdata[i].offset_to_right_extreme + ); + } + } +#endif + + return 0; +} + +static 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; +} + +static 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; + const igraph_real_t minsep = 1; + igraph_real_t avg; + +#ifdef LAYOUT_RT_DEBUG + printf("Starting visiting node %ld\n", node); +#endif + + /* 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; +#ifdef LAYOUT_RT_DEBUG + printf("Visited node %ld and arranged its subtrees\n", node); +#endif + for (i = 0, j = 0; i < vcount; i++) { + if (i == node) { + continue; + } + if (vdata[i].parent == node) { + 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, auxnode; + igraph_real_t loffset, roffset, rootsep, newoffset; + +#ifdef LAYOUT_RT_DEBUG + printf(" Placing child %ld on level %ld, to the right of %ld\n", i, vdata[i].level, leftroot); +#endif + lnode = leftroot; rnode = i; + rootsep = vdata[leftroot].offset + minsep; + loffset = vdata[leftroot].offset; roffset = loffset + minsep; + + /* Keep on updating the right contour now that we have attached + * a new node to the subtree being built */ + vdata[node].right_contour = i; + vdata[node].offset_to_right_contour = rootsep; + +#ifdef LAYOUT_RT_DEBUG + printf(" Contour: [%ld, %ld], offsets: [%lf, %lf], rootsep: %lf\n", + lnode, rnode, loffset, roffset, rootsep); +#endif + 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_to_right_contour; + lnode = vdata[lnode].right_contour; + } else { + /* Left subtree ended there. The left and right contour + * of the left subtree will continue to the next step + * on the right subtree. */ + if (vdata[rnode].left_contour >= 0) { + auxnode = vdata[node].left_extreme; + + /* this is the "threading" step that the original + * paper is talking about */ + newoffset = (vdata[node].offset_to_right_extreme - vdata[node].offset_to_left_extreme) + minsep + vdata[rnode].offset_to_left_contour; + vdata[auxnode].left_contour = vdata[rnode].left_contour; + vdata[auxnode].right_contour = vdata[rnode].left_contour; + vdata[auxnode].offset_to_left_contour = vdata[auxnode].offset_to_right_contour = newoffset; + + /* since we attached a larger subtree to the + * already placed left subtree, we need to update + * the extrema of the subtree rooted at 'node' */ + vdata[node].left_extreme = vdata[i].left_extreme; + vdata[node].right_extreme = vdata[i].right_extreme; + vdata[node].offset_to_left_extreme = vdata[i].offset_to_left_extreme + rootsep; + vdata[node].offset_to_right_extreme = vdata[i].offset_to_right_extreme + rootsep; +#ifdef LAYOUT_RT_DEBUG + printf(" Left subtree ended earlier, continuing left subtree's left and right contour on right subtree (node %ld gets connected to node %ld)\n", auxnode, vdata[rnode].left_contour); + printf(" New contour following offset for node %ld is %lf\n", auxnode, vdata[auxnode].offset_to_left_contour); +#endif + } else { + /* Both subtrees are ending at the same time; the + * left extreme node of the subtree rooted at + * 'node' remains the same but the right extreme + * will change */ + vdata[node].right_extreme = vdata[i].right_extreme; + vdata[node].offset_to_right_extreme = vdata[i].offset_to_right_extreme + rootsep; + } + 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_to_left_contour; + rnode = vdata[rnode].left_contour; + } else { + /* Right subtree ended here. The right 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) { + auxnode = vdata[i].right_extreme; + + /* this is the "threading" step that the original + * paper is talking about */ + newoffset = loffset - rootsep - vdata[i].offset_to_right_extreme; + vdata[auxnode].left_contour = lnode; + vdata[auxnode].right_contour = lnode; + vdata[auxnode].offset_to_left_contour = vdata[auxnode].offset_to_right_contour = newoffset; + + /* no need to update the extrema of the subtree + * rooted at 'node' because the right subtree was + * smaller */ +#ifdef LAYOUT_RT_DEBUG + printf(" Right subtree ended earlier, continuing right subtree's left and right contour on left subtree (node %ld gets connected to node %ld)\n", auxnode, lnode); + printf(" New contour following offset for node %ld is %lf\n", auxnode, vdata[auxnode].offset_to_left_contour); +#endif + } + rnode = -1; + } +#ifdef LAYOUT_RT_DEBUG + printf(" Contour: [%ld, %ld], offsets: [%lf, %lf], rootsep: %lf\n", + lnode, rnode, loffset, roffset, rootsep); +#endif + + /* Push subtrees away if necessary */ + if ((lnode >= 0) && (rnode >= 0) && (roffset - loffset < minsep)) { +#ifdef LAYOUT_RT_DEBUG + printf(" Pushing right subtree away by %lf\n", minsep-roffset+loffset); +#endif + rootsep += minsep - roffset + loffset; + roffset = loffset + minsep; + vdata[node].offset_to_right_contour = rootsep; + } + } + +#ifdef LAYOUT_RT_DEBUG + printf(" Offset of subtree with root node %ld will be %lf\n", i, rootsep); +#endif + vdata[i].offset = rootsep; + vdata[node].offset_to_right_contour = rootsep; + avg = (avg * j) / (j + 1) + rootsep / (j + 1); + leftrootidx = j; + leftroot = i; + } else { + /* This is the first child of the node being considered so we + * can simply place the subtree on our virtual canvas */ +#ifdef LAYOUT_RT_DEBUG + printf(" Placing child %ld on level %ld as first child\n", i, vdata[i].level); +#endif + leftrootidx = j; + leftroot = i; + vdata[node].left_contour = i; + vdata[node].right_contour = i; + vdata[node].offset_to_left_contour = 0.0; + vdata[node].offset_to_right_contour = 0.0; + vdata[node].left_extreme = vdata[i].left_extreme; + vdata[node].right_extreme = vdata[i].right_extreme; + vdata[node].offset_to_left_extreme = vdata[i].offset_to_left_extreme; + vdata[node].offset_to_right_extreme = vdata[i].offset_to_right_extreme; + avg = vdata[i].offset; + } + j++; + } + } +#ifdef LAYOUT_RT_DEBUG + printf("Shifting node %ld to be centered above children. Shift amount: %lf\n", node, avg); +#endif + vdata[node].offset_to_left_contour -= avg; + vdata[node].offset_to_right_contour -= avg; + vdata[node].offset_to_left_extreme -= avg; + vdata[node].offset_to_right_extreme -= avg; + for (i = 0, j = 0; i < vcount; i++) { + if (i == node) { + continue; + } + if (vdata[i].parent == node) { + vdata[i].offset -= avg; + } + } + + return 0; +} + +/* This function computes the number of outgoing (or incoming) connections + * of clusters, represented as a membership vector. It only works with + * directed graphs. */ +int igraph_i_layout_reingold_tilford_cluster_degrees_directed( + const igraph_t *graph, + const igraph_vector_t *membership, + igraph_integer_t no_comps, + igraph_neimode_t mode, + igraph_vector_t *degrees) { + + igraph_eit_t eit; + + if (! igraph_is_directed(graph) || (mode != IGRAPH_OUT && mode != IGRAPH_IN)) { + IGRAPH_ERROR("Directed graph expected.", IGRAPH_EINVAL); + } + + IGRAPH_CHECK(igraph_vector_resize(degrees, no_comps)); + igraph_vector_null(degrees); + + IGRAPH_CHECK(igraph_eit_create(graph, igraph_ess_all(IGRAPH_EDGEORDER_ID), &eit)); + IGRAPH_FINALLY(igraph_eit_destroy, &eit); + + for (; !IGRAPH_EIT_END(eit); IGRAPH_EIT_NEXT(eit)) { + igraph_integer_t eid = IGRAPH_EIT_GET(eit); + + igraph_integer_t from = IGRAPH_FROM(graph, eid); + igraph_integer_t to = IGRAPH_TO(graph, eid); + + igraph_integer_t from_cl = VECTOR(*membership)[from]; + igraph_integer_t to_cl = VECTOR(*membership)[to]; + + igraph_integer_t cl = mode == IGRAPH_OUT ? from_cl : to_cl; + + if (from_cl != to_cl) { + VECTOR(*degrees)[cl] += 1; + } + } + + igraph_eit_destroy(&eit); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + +/* Heuristic method to choose "nice" roots for the Reingold-Tilford layout algorithm. + * + * The principle is to select a minimal set of roots so that all other vertices + * will be reachable from them. + * + * In the undirected case, one root is chosen from each connected component. + * In the directed case, one root is chosen from each strongly connected component + * that has no incoming (or outgoing) edges (depending on 'mode'). + * When more than one root choice is possible, nodes are prioritized based on + * either lowest ecccentricity (if 'use_ecccentricity' is true) or based on + * highest degree (out- or in-degree in directed mode). + */ +int igraph_i_layout_reingold_tilford_select_roots( + const igraph_t *graph, + igraph_neimode_t mode, + igraph_vector_t *roots, + igraph_bool_t use_eccentricity) { + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_vector_t order, membership; + igraph_integer_t no_comps; + long int i, j; + + if (! igraph_is_directed(graph)) { + mode = IGRAPH_ALL; + } + + IGRAPH_VECTOR_INIT_FINALLY(&order, no_of_nodes); + if (use_eccentricity) { + /* Sort vertices by decreasing eccenticity. */ + + igraph_vector_t ecc; + + IGRAPH_VECTOR_INIT_FINALLY(&ecc, no_of_nodes); + IGRAPH_CHECK(igraph_eccentricity(graph, &ecc, igraph_vss_all(), mode)); + IGRAPH_CHECK(igraph_vector_qsort_ind(&ecc, &order, /* descending= */ 0)); + + igraph_vector_destroy(&ecc); + IGRAPH_FINALLY_CLEAN(1); + } else { + /* Sort vertices by decreasing degree (out- or in-degree in directed case). */ + + IGRAPH_CHECK(igraph_sort_vertex_ids_by_degree(graph, &order, + igraph_vss_all(), mode, 0, IGRAPH_DESCENDING, 0)); + } + + IGRAPH_VECTOR_INIT_FINALLY(&membership, no_of_nodes); + IGRAPH_CHECK(igraph_clusters(graph, &membership, /*csize=*/ NULL, + &no_comps, mode == IGRAPH_ALL ? IGRAPH_WEAK : IGRAPH_STRONG)); + + IGRAPH_CHECK(igraph_vector_resize(roots, no_comps)); + igraph_vector_fill(roots, -1); /* -1 signifies a not-yet-determined root for a component */ + + if (mode != IGRAPH_ALL) { + /* Directed case: + * + * We break the graph into strongly-connected components and find those components + * which have no incoming (outgoing) edges. The largest out-degree (in-degree) + * nodes from these components will be chosen as roots. When the graph is a DAG, + * these will simply be the source (sink) nodes. */ + + igraph_vector_t cluster_degrees; + + IGRAPH_VECTOR_INIT_FINALLY(&cluster_degrees, no_of_nodes); + IGRAPH_CHECK(igraph_i_layout_reingold_tilford_cluster_degrees_directed( + graph, &membership, no_comps, + mode == IGRAPH_OUT ? IGRAPH_IN : IGRAPH_OUT, /* reverse direction */ + &cluster_degrees)); + + /* Iterate through nodes in decreasing out-degree (or in-degree) order + * and record largest degree node in each strongly-connected component + * which has no incoming (outgoing) edges. */ + for (i = 0; i < no_of_nodes; ++i) { + long int v = (long int) VECTOR(order)[i]; + long int cl = VECTOR(membership)[v]; + if (VECTOR(cluster_degrees)[cl] == 0 && VECTOR(*roots)[cl] == -1) { + VECTOR(*roots)[cl] = v; + } + } + + igraph_vector_destroy(&cluster_degrees); + IGRAPH_FINALLY_CLEAN(1); + + /* Remove remaining -1 indices. These correspond to components that + * did have some incoming edges. */ + for (i=0, j=0; i < no_comps; ++i) { + if (VECTOR(*roots)[i] == -1) { + continue; + } + VECTOR(*roots)[j++] = VECTOR(*roots)[i]; + } + igraph_vector_resize(roots, j); + + } else { + /* Undirected case: + * + * Select the highest degree node from each component. + */ + + long int no_seen = 0; + + for (i=0; i < no_of_nodes; ++i) { + long int v = VECTOR(order)[i]; + long int cl = VECTOR(membership)[v]; + if (VECTOR(*roots)[cl] == -1) { + no_seen += 1; + VECTOR(*roots)[cl] = v; + } + if (no_seen == no_comps) { + /* All components have roots now. */ + break; + } + } + } + + igraph_vector_destroy(&membership); + igraph_vector_destroy(&order); + IGRAPH_FINALLY_CLEAN(2); + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_layout_reingold_tilford + * \brief Reingold-Tilford layout for tree graphs + * + * + * 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: + * + * + * Reingold, E and Tilford, J: Tidier drawing of trees. + * IEEE Trans. Softw. Eng., SE-7(2):223--228, 1981. + * https://doi.org/10.1109/TSE.1981.234519 + * + * + * 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. The set of roots + * should be specified so that all vertices of the graph are reachable from them. + * Simply put, in the udirected case, one root should be given from each + * connected component. If \p roots is \c NULL or a pointer to an empty vector, + * then the roots will be selected automatically. Currently, automatic root + * selection prefers low ecccentricity vertices in graphs with fewer than + * 500 vertices, and high degree vertices (acording to \p mode) in larger graphs. + * The root selecton heuristic may change without notice. To ensure a consistent + * output, please specify the roots manually. + * \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; + + 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, perform automated root selection. */ + + if (!roots || igraph_vector_size(roots) == 0) { + + IGRAPH_VECTOR_INIT_FINALLY(&myroots, 0); + igraph_i_layout_reingold_tilford_select_roots(graph, mode, &myroots, no_of_nodes < 500); + proots = &myroots; + + } 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 + * + * + * 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. The set of roots + * should be specified so that all vertices of the graph are reachable from them. + * Simply put, in the udirected case, one root should be given from each + * connected component. If \p roots is \c NULL or a pointer to an empty vector, + * then the roots will be selected automatically. Currently, automatic root + * selection prefers low ecccentricity vertices in graphs with fewer than + * 500 vertices, and high degree vertices (acording to \p mode) in larger graphs. + * The root selecton heuristic may change without notice. To ensure a consistent + * output, please specify the roots manually. + * \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 or an empty vector). + * \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; + igraph_real_t minx, maxx; + + IGRAPH_CHECK(igraph_layout_reingold_tilford(graph, res, mode, roots, rootlevel)); + + if (no_of_nodes == 0) { + return IGRAPH_SUCCESS; + } + + ratio = 2 * M_PI * (no_of_nodes - 1.0) / no_of_nodes; + + 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 IGRAPH_SUCCESS; +} diff --git a/src/rigraph/core/layout/sugiyama.c b/src/rigraph/core/layout/sugiyama.c new file mode 100644 index 0000000..a046e11 --- /dev/null +++ b/src/rigraph/core/layout/sugiyama.c @@ -0,0 +1,1342 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2007-2012 Gabor Csardi + 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_centrality.h" +#include "igraph_components.h" +#include "igraph_constants.h" +#include "igraph_constructors.h" +#include "igraph_datatype.h" +#include "igraph_error.h" +#include "igraph_interface.h" +#include "igraph_memory.h" +#include "igraph_structural.h" +#include "igraph_types.h" + +#include "internal/glpk_support.h" +#include "misc/feedback_arc_set.h" + +#include "config.h" + +#include + +/* #define SUGIYAMA_DEBUG */ + +#ifdef _MSC_VER +/* MSVC does not support variadic macros */ +#include +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. + */ +static 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. + */ +static 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. + */ +static 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 + */ +static 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. + * + * + * 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. + * + * + * 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. + * + * + * 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. + * + * + * 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/src/rigraph/core/linalg/arpack.c b/src/rigraph/core/linalg/arpack.c new file mode 100644 index 0000000..e8156a9 --- /dev/null +++ b/src/rigraph/core/linalg/arpack.c @@ -0,0 +1,1438 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 noet: */ +/* + IGraph library. + Copyright (C) 2007-2012 Gabor Csardi + 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_memory.h" + +#include "linalg/arpack_internal.h" + +#include +#include +#include + +/* The ARPACK example file dssimp.f is used as a template */ + +static 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; + } +} + +static 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; + } + +} + +static 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; + } +} + +static 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. + * + * 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. + * + * 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. + */ +static 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. + */ +static 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. + */ +static 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. + */ +static 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); + unsigned int i; + +#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) { + 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; + unsigned int n = (unsigned int) options->n; + int nconv = options->nconv; + int nev = options->nev; + unsigned int nans = (unsigned int) (nconv < nev ? nconv : nev); + unsigned int i; + +#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. + */ +static 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. + */ +static 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; + igraph_real_t origtol = options->tol; + char origwhich[2]; + + origwhich[0] = options->which[0]; + origwhich[1] = options->which[1]; + + /* 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. + * + * 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; + igraph_real_t origtol = options->tol; + int d_size; + char origwhich[2]; + + origwhich[0] = options->which[0]; + origwhich[1] = options->which[1]; + + /* 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 (!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 + 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. + * + * + * 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. + * This will add the vectors for eigenvalues with a negative imaginary + * part and return all vectors as 2 columns, a real and imaginary part. + * \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; + long int new_vector_pos; + long int vector_pos; + igraph_matrix_t new_vectors; + + /* 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); + } + + for (i = no_evs -1; i >= nev; i--) { + IGRAPH_CHECK(igraph_matrix_remove_row(values, i)); + } + + IGRAPH_CHECK(igraph_matrix_init(&new_vectors, nodes, nev * 2)); + IGRAPH_FINALLY(igraph_matrix_destroy, &new_vectors); + + new_vector_pos = 0; + vector_pos = 0; + for (i = 0; i < nev && vector_pos < igraph_matrix_ncol(vectors); i++) { + if (MATRIX(*values, i, 1) == 0) { + /* Real eigenvalue */ + for (j = 0; j < nodes; j++) { + MATRIX(new_vectors, j, new_vector_pos) = MATRIX(*vectors, j, vector_pos); + } + new_vector_pos += 2; + vector_pos += 1; + } else { + /* complex eigenvalue */ + for (j = 0; j < nodes; j++) { + MATRIX(new_vectors, j, new_vector_pos) = MATRIX(*vectors, j, vector_pos); + MATRIX(new_vectors, j, new_vector_pos + 1) = MATRIX(*vectors, j, vector_pos + 1); + } + + /* handle the conjugate */ + + /* first check if the conjugate eigenvalue is there */ + i++; + if (i >= nev) { + break; + } + + if (MATRIX(*values, i, 1) != -MATRIX(*values, i-1, 1)) { + IGRAPH_ERROR("Complex eigenvalue not followed by its conjugate.", IGRAPH_EINVAL); + } + + /* then copy and negate */ + for (j = 0; j < nodes; j++) { + MATRIX(new_vectors, j, new_vector_pos + 2) = MATRIX(*vectors, j, vector_pos); + MATRIX(new_vectors, j, new_vector_pos + 3) = -MATRIX(*vectors, j, vector_pos + 1); + } + new_vector_pos += 4; + vector_pos += 2; + } + } + igraph_matrix_destroy(vectors); + IGRAPH_CHECK(igraph_matrix_copy(vectors, &new_vectors)); + igraph_matrix_destroy(&new_vectors); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} diff --git a/src/rigraph/core/linalg/arpack_internal.h b/src/rigraph/core/linalg/arpack_internal.h new file mode 100644 index 0000000..f318371 --- /dev/null +++ b/src/rigraph/core/linalg/arpack_internal.h @@ -0,0 +1,229 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2007-2012 Gabor Csardi + 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 + +/* GFortran-specific calling conventions, used when compiling the R interface. + * Derived with "gfortran -fc-prototypes-external", applied on the original + * Fortran sources of these functions. + * + * Caveats: + * + * 1) gfortran prints size_t for the "_len" arguments, but in fact they must be + * long int + * 2) gofrtran maps Fortran LOGICAL types to int_least32_t, but in fact they + * must be void* (anything else doesn't work, not even _Bool*) + * */ + +void 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, + long int bmat_len, long int which_len); + +void igraphdseupd_(void *rvec, char *howmny, void *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, + long int howmny_len, long int bmat_len, long int which_len); + +void 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, + long int bmat_len, long int which_len); + +void igraphdneupd_(void *rvec, char *howmny, void *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, + long int howmny_len, long int bmat_len, long int which_len); + +void igraphdsortr_(char *which, void *apply, int* n, igraph_real_t *x1, + igraph_real_t *x2, long int which_len); + +void igraphdsortc_(char *which, void *apply, int* n, igraph_real_t *xreal, + igraph_real_t *ximag, igraph_real_t *y, long 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/src/rigraph/core/linalg/blas.c b/src/rigraph/core/linalg/blas.c new file mode 100644 index 0000000..8a4d017 --- /dev/null +++ b/src/rigraph/core/linalg/blas.c @@ -0,0 +1,157 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2007-2012 Gabor Csardi + 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_error.h" +#include "igraph_blas.h" + +#include "linalg/blas_internal.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 non-symmetric). + * + * \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); + + IGRAPH_ASSERT(igraph_vector_size(x) == transpose ? m : n); + IGRAPH_ASSERT(igraph_vector_size(y) == transpose ? n : m); + +#ifdef HAVE_GFORTRAN + igraphdgemv_(&trans, &m, &n, &alpha, VECTOR(a->data), &m, + VECTOR(*x), &inc, &beta, VECTOR(*y), &inc, /* trans_len = */ 1); +#else + igraphdgemv_(&trans, &m, &n, &alpha, VECTOR(a->data), &m, + VECTOR(*x), &inc, &beta, VECTOR(*y), &inc); +#endif +} + +/** + * \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 non-symmetric). + * + * \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); + +#ifdef HAVE_GFORTRAN + igraphdgemv_(&trans, &m, &n, &alpha, VECTOR(a->data), &m, + (igraph_real_t*)x, &inc, &beta, y, &inc, /* trans_len = */ 1); +#else + igraphdgemv_(&trans, &m, &n, &alpha, VECTOR(a->data), &m, + (igraph_real_t*)x, &inc, &beta, y, &inc); +#endif +} + +/** + * \function igraph_blas_dnrm2 + * \brief Euclidean norm of a vector. + * + * \param v The vector. + * \return Real value, the norm of \p v. + * + * Time complexity: O(n) where n is the length of the vector. + */ +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); +} + +/** + * \function igraph_blas_ddot + * \brief Dot product of two vectors. + * + * \param v1 The first vector. + * \param v2 The second vector. + * \param res Pointer to a real, the result will be stored here. + * + * Time complexity: O(n) where n is the length of the vectors. + * + * \example examples/simple/blas.c + */ +int igraph_blas_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/src/rigraph/core/linalg/blas_internal.h b/src/rigraph/core/linalg/blas_internal.h new file mode 100644 index 0000000..71810b0 --- /dev/null +++ b/src/rigraph/core/linalg/blas_internal.h @@ -0,0 +1,90 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2007-2012 Gabor Csardi + 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_ + #define igraphdsymv_ dsymv_ + #define igraphdsyr2_ dsyr2_ + #define igraphdsyr2k_ dsyr2k_ + #define igraphdtrmv_ dtrmv_ + #define igraphdsyrk_ dsyrk_ +#endif + +#ifdef HAVE_GFORTRAN + +/* GFortran-specific calling conventions, used when compiling the R interface. + * Derived with "gfortran -fc-prototypes-external", applied on the original + * Fortran sources of these functions. */ + +void 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, long int trans_len); + +void 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, long int transa_len, long int transb_len); + +#else + +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); + +#endif + +double igraphdnrm2_(int *n, double *x, int *incx); + +double igraphddot_(int *n, double *dx, int *incx, double *dy, int *incy); + +#endif diff --git a/src/rigraph/core/linalg/eigen.c b/src/rigraph/core/linalg/eigen.c new file mode 100644 index 0000000..f42dc86 --- /dev/null +++ b/src/rigraph/core/linalg/eigen.c @@ -0,0 +1,1550 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2010-2012 Gabor Csardi + 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 +#include +#include + +static 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; +} + +static 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; +} + +static 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; +} + +static 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; +} + +static 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; +} + +static 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; +} + +static 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; +} + +static 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; +} + +static 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; +} + +static 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; + +static 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, from, n); + igraph_vector_null(&vto); + igraph_sparsemat_gaxpy(data->sA, &vfrom, &vto); + } + return 0; +} + +static 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; + + int low = (int) floor(which->howmany / 2.0), high = (int) ceil(which->howmany / 2.0); + int l1, l2, w; + + igraph_i_eigen_matrix_sym_arpack_data_t myextra; + myextra.A = A; + myextra.sA = sA; + + 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; +} + +static 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; + + myextra.A = A; + myextra.sA = 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 */ + +static 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 */ + +static 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 */ + +static 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 +*/ + +static 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 */ + +static 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 */ + +static 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; ipos) { + 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; +} + +static 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; + +} + +static 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); +} + +static 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); +} + +static 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); +} + + +static 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); +} + +static 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); +} + +static 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); +} + +static 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); +} + +static 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); +} + +static 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; +} + +static 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_UNUSED(options); + IGRAPH_UNUSED(storage); + + 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; +} + +static 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; +} + +static 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_UNUSED(cmplxvalues); + IGRAPH_UNUSED(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_LOOPS_ONCE, IGRAPH_MULTIPLE)); + 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_UNUSED(graph); + IGRAPH_UNUSED(algorithm); + IGRAPH_UNUSED(which); + IGRAPH_UNUSED(options); + IGRAPH_UNUSED(storage); + IGRAPH_UNUSED(values); + IGRAPH_UNUSED(vectors); + IGRAPH_UNUSED(cmplxvalues); + IGRAPH_UNUSED(cmplxvectors); + + /* TODO */ + + IGRAPH_ERROR("'igraph_eigen_laplacian'", IGRAPH_UNIMPLEMENTED); +} diff --git a/src/rigraph/core/linalg/lapack.c b/src/rigraph/core/linalg/lapack.c new file mode 100644 index 0000000..5652caf --- /dev/null +++ b/src/rigraph/core/linalg/lapack.c @@ -0,0 +1,954 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2010-2012 Gabor Csardi + 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 "linalg/lapack_internal.h" + +/** + * \function igraph_lapack_dgetrf + * \brief 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 < 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 \c i of the matrix was + * interchanged with row ipiv[i]. + * \param info LAPACK error code. Zero on successful exit. If its value is + * a positive number i, it indicates that 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; + } else { + IGRAPH_CHECK(igraph_vector_int_resize(ipiv, m < n ? m : n)); + } + + 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 + * \brief 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. L is expected to be unitriangular, + * diagonal entries are those of U. If A is singular, no warning or + * error wil be given and random output will be returned. + * \param ipiv An integer vector, the pivot indices from \ref + * igraph_lapack_dgetrf() must be given here. Row \c i of A was + * interchanged with row ipiv[i]. + * \param b The right hand side matrix must be given here. The solution + will also be placed here. + * \return Error code. + * + * Time complexity: TODO. + */ + +int igraph_lapack_dgetrs(igraph_bool_t transpose, const igraph_matrix_t *a, + const 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); + } + if (igraph_vector_int_size(ipiv) > 0) { + igraph_integer_t min, max; + igraph_vector_int_minmax(ipiv, &min, &max); + if (max > n || min < 1) { + IGRAPH_ERROR("Pivot index out of range.", IGRAPH_EINVAL); + } + } + if (igraph_vector_int_size(ipiv) != n) { + IGRAPH_ERROR("Pivot vector length must match number of matrix rows.", 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. + * + * 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. + * + * 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. + * + * + * 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. + * + * \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. + * + * + * Optionally also, it computes a balancing transformation to improve + * the conditioning of the eigenvalues and eigenvectors (\p ilo, \p ihi, + * \p scale, and \p abnrm), reciprocal condition numbers for the + * eigenvalues (\p rconde), and reciprocal condition numbers for the + * right eigenvectors (\p 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. + * + * \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 \p 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; +} diff --git a/src/rigraph/core/linalg/lapack_internal.h b/src/rigraph/core/linalg/lapack_internal.h new file mode 100644 index 0000000..0ab117e --- /dev/null +++ b/src/rigraph/core/linalg/lapack_internal.h @@ -0,0 +1,183 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2007-2012 Gabor Csardi + 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_ +#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/src/rigraph/core/math/bfgs.c b/src/rigraph/core/math/bfgs.c new file mode 100644 index 0000000..a272029 --- /dev/null +++ b/src/rigraph/core/math/bfgs.c @@ -0,0 +1,221 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2007-2012 Gabor Csardi + 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 "core/interruption.h" +#include "igraph_statusbar.h" + +#include + +/* 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/src/rigraph/core/math/complex.c b/src/rigraph/core/math/complex.c new file mode 100644 index 0000000..e47b168 --- /dev/null +++ b/src/rigraph/core/math/complex.c @@ -0,0 +1,391 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2010-2012 Gabor Csardi + 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 "core/math.h" +#include + +/** + * \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/src/rigraph/core/math/utils.c b/src/rigraph/core/math/utils.c new file mode 100644 index 0000000..abf175c --- /dev/null +++ b/src/rigraph/core/math/utils.c @@ -0,0 +1,339 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2007-2012 Gabor Csardi + 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 "core/math.h" + +#include "config.h" + +#include +#include + +#ifdef _MSC_VER +# ifndef isinf +# define isinf(x) (!_finite(x) && !_isnan(x)) +# endif +#endif + +int igraph_finite(double x) { +#if HAVE_ISFINITE + return isfinite(x); +#elif HAVE_FINITE + return finite(x); +#else + return (!isnan(x) & (x != IGRAPH_POSINFINITY) & (x != IGRAPH_NEGINFINITY)); +#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_WARNING("chebyshev_eval: argument out of domain"); + return IGRAPH_NAN; + } + + if (x < -1.1 || x > 1.1) { + IGRAPH_WARNING("chebyshev_eval: argument out of domain"); + return 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); +} + +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) && x > 0; +} + +int igraph_is_neginf(double x) { + return isinf(x) && x < 0; +} + +/** + * \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; +} + +/* Use value-safe floating point math for igraph_cmp_epsilon() with + * the Intel compiler. + * + * The Intel compiler rewrites arithmetic expressions for faster + * evaluation by default. In the below function, it will evaluate + * (eps * fabs(a) + eps * fabs(b)) as eps*(fabs(a) + fabs(b)). + * However, this code path is taken precisely when fabs(a) + fabs(b) + * overflows, thus this rearrangement of the expression causes + * the function to return incorrect results, and some test failures. + * To avoid this, we switch the Intel compiler to "precise" mode. + */ +#ifdef __INTEL_COMPILER +#pragma float_control(push) +#pragma float_control (precise, on) +#endif + +/** + * \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; + double sum; + + if (a == b) { + /* shortcut, handles infinities */ + return 0; + } + + diff = a - b; + abs_diff = fabs(diff); + sum = fabs(a) + fabs(b); + + if (a == 0 || b == 0 || sum < 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 if (!isfinite(sum)) { + /* addition overflow, so presumably |a| and |b| are both large; use a + * different formulation */ + return (abs_diff < (eps * fabs(a) + eps * fabs(b))) ? 0 : (diff < 0 ? -1 : 1); + } else { + return (abs_diff / sum < eps) ? 0 : (diff < 0 ? -1 : 1); + } +} + +#ifdef __INTEL_COMPILER +#pragma float_control(pop) +#endif diff --git a/src/rigraph/core/misc/bipartite.c b/src/rigraph/core/misc/bipartite.c new file mode 100644 index 0000000..e26ceb4 --- /dev/null +++ b/src/rigraph/core/misc/bipartite.c @@ -0,0 +1,1148 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2008-2012 Gabor Csardi + 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_adjlist.h" +#include "igraph_interface.h" +#include "igraph_constructors.h" +#include "igraph_dqueue.h" +#include "igraph_random.h" +#include "igraph_nongraph.h" + +#include "graph/attributes.h" + +/** + * \section about_bipartite Bipartite networks in igraph + * + * + * A bipartite network contains two kinds of vertices and connections + * are only possible between two vertices of different kinds. There are + * many natural examples, e.g. movies and actors as vertices and a + * movie is connected to all participating actors, etc. + * + * + * 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. + * + * + * Most functions creating bipartite networks are able to create this + * extra vector, you just need to supply an initialized boolean vector + * to them. + */ + +/** + * \function igraph_bipartite_projection_size + * \brief 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_LOOPS_TWICE, IGRAPH_MULTIPLE)); + 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; +} + +static 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_LOOPS_TWICE, IGRAPH_MULTIPLE)); + IGRAPH_FINALLY(igraph_adjlist_destroy, &adjlist); + + /* we won't need the 'mult' vector if 'multiplicity' is NULL, but MSVC will + * throw warnings in the compiler output if we initialize it conditionally */ + IGRAPH_VECTOR_INIT_FINALLY(&mult, multiplicity ? no_of_nodes : 1); + if (multiplicity) { + 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 */ + } + + igraph_vector_destroy(&mult); + igraph_adjlist_destroy(&adjlist); + igraph_vector_long_destroy(&added); + igraph_vector_destroy(&vertex_index); + IGRAPH_FINALLY_CLEAN(4); + + 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 + * \brief 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 + * \brief 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. + * + * + * 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. + * + * + * 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 + * \brief 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; + 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 or too large) vertex id", IGRAPH_EINVVID); + } + + /* 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 + * \brief Creates 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. + * + * + * 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. + * + * + * This function does not create multiple edges if \p multiple is + * \c FALSE, but might create some if it is \c 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 + * \brief 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. All vertices + * in one part of the graph should have type 0, the others type 1. + * \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. The rows will correspond to vertices with type 0, + * the columns correspond to vertices with type 1. + * \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; + long int ignored_edges = 0; + + if (igraph_vector_bool_size(types) != no_of_nodes) { + IGRAPH_ERRORF("Vertex type vector size (%ld) not equal to number of vertices (%ld).", + IGRAPH_EINVAL, igraph_vector_bool_size(types), no_of_nodes); + } + + 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] == VECTOR(*types)[to]) { + ignored_edges++; + } else if (! VECTOR(*types)[from]) { + MATRIX(*res, from2, to2 - n1) += 1; + } else { + MATRIX(*res, to2, from2 - n1) += 1; + } + } + if (ignored_edges) { + IGRAPH_WARNINGF("%ld edges running within partitions were ignored.", ignored_edges); + } + + 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 IGRAPH_SUCCESS; +} + +/** + * \function igraph_is_bipartite + * \brief Check whether a graph is bipartite. + * + * This function checks whether a graph is 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. + * + * + * The existence of such a mapping is equivalent of having no circuits of + * odd length in the graph. A graph with loop edges cannot be bipartite. + * + * + * 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 types 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 *types) { + + /* 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 (types && bi) { + IGRAPH_CHECK(igraph_vector_bool_resize(types, no_of_nodes)); + for (i = 0; i < no_of_nodes; i++) { + VECTOR(*types)[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 + * \brief Generate a bipartite random graph (similar to Erdős-Rényi). + * + * 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) { + + if (n1 < 0 || n2 < 0) { + IGRAPH_ERROR("Invalid number of vertices for bipartite game.", IGRAPH_EINVAL); + } + + if (type == IGRAPH_ERDOS_RENYI_GNP) { + return igraph_bipartite_game_gnp(graph, types, n1, n2, p, directed, mode); + } else if (type == IGRAPH_ERDOS_RENYI_GNM) { + return igraph_bipartite_game_gnm(graph, types, n1, n2, m, directed, mode); + } else { + IGRAPH_ERROR("Invalid bipartite game type.", IGRAPH_EINVAL); + } +} diff --git a/src/rigraph/core/misc/chordality.c b/src/rigraph/core/misc/chordality.c new file mode 100644 index 0000000..fe57ff1 --- /dev/null +++ b/src/rigraph/core/misc/chordality.c @@ -0,0 +1,487 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2008-2021 The igraph development 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, see . +*/ + +#include "igraph_structural.h" +#include "igraph_error.h" +#include "igraph_adjlist.h" +#include "igraph_interface.h" + +/** + * \function igraph_maximum_cardinality_search + * \brief Maximum cardinality search. + * + * This function implements the maximum cardinality search algorithm. + * It computes a rank \p alpha for each vertex, such that visiting + * vertices in decreasing rank order corresponds to always choosing + * the vertex with the most already visited neighbors as the next one + * to visit. + * + * + * Maximum cardinality search is useful in deciding the chordality + * of a graph. A graph is chordal if and only if any two neighbors + * of a vertex which are higher in rank than it are connected to + * each other. + * + * + * References: + * + * + * 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. + * https://doi.org/10.1137/0213035 + * + * \param graph The input graph. Edge directions will be 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 in the range 0 to n - 1, + * where \c n is the number of vertices. + * \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. In other words, the elements of \p alpham1 are vertex IDs + * in reverse maximum cardinality search order. + * \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; + + /***************/ + /* local j, v; */ + /***************/ + + long int j, v; + + 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_NO_LOOPS, IGRAPH_NO_MULTIPLE)); + 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 IGRAPH_SUCCESS; +} + +/** + * \function igraph_is_chordal + * \brief Decides whether a graph is chordal. + * + * A graph is chordal if each of its cycles of four or more nodes + * has a chord, i.e. 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 alpham1 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. Edge directions will be ignored. + * \param alpha Either an alpha vector coming from + * \ref igraph_maximum_cardinality_search() (on the same graph), or a + * \c NULL pointer. + * \param alpham1 Either an inverse alpha vector coming from \ref + * igraph_maximum_cardinality_search() (on the same graph) or a \c NULL + * pointer. + * \param chordal Pointer to a boolean. If not NULL the result is stored here. + * \param fill_in Pointer to an initialized vector, or a \c NULL + * pointer. If not a \c NULL pointer, then the fill-in, also called the + * chordal completion of the graph is stored here. + * The chordal completion is a set of edges that are needed to + * make the graph chordal. The vector is resized as needed. + * Note that the chordal completion returned by this function may not + * be minimal, i.e. some of the returned fill-in edges may not be needed + * to make the graph chordal. + * \param newgraph Pointer to an uninitialized graph, or a \c 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 (alpha && (igraph_vector_size(alpha) != no_of_nodes)) { + IGRAPH_ERRORF("Alpha vector size (%ld) not equal to number of nodes (%ld).", + IGRAPH_EINVAL, igraph_vector_size(alpha), no_of_nodes); + } + + if (alpham1 && (igraph_vector_size(alpham1) != no_of_nodes)) { + IGRAPH_ERRORF("Inverse alpha vector size (%ld) not equal to number of nodes (%ld).", + IGRAPH_EINVAL, igraph_vector_size(alpham1), no_of_nodes); + } + + if (!chordal && !calc_edges) { + /* Nothing to calculate */ + return IGRAPH_SUCCESS; + } + + 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_NO_LOOPS, IGRAPH_NO_MULTIPLE)); + 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) */ + /******************************************/ + + 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) */ + /********************/ + + 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 IGRAPH_SUCCESS; +} diff --git a/src/rigraph/core/misc/cocitation.c b/src/rigraph/core/misc/cocitation.c new file mode 100644 index 0000000..6f9c787 --- /dev/null +++ b/src/rigraph/core/misc/cocitation.c @@ -0,0 +1,779 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph R package. + Copyright (C) 2005-2012 Gabor Csardi + 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_interface.h" + +#include "core/interruption.h" + +#include + +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. + * + * + * 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. + * + * + * 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() + * + * \example examples/simple/igraph_cocitation.c + */ + +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. + * + * + * 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. + * + * + * Isolated vertices will have zero similarity to any other vertex. + * Self-similarities are not calculated. + * + * + * 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; +} + + +static int igraph_i_neisets_intersect(const igraph_vector_int_t *v1, + const igraph_vector_int_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_int_size(v1); jj0 = igraph_vector_int_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. + * + * + * 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_int_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_NO_LOOPS, IGRAPH_NO_MULTIPLE)); + 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_int_binsearch(v1, i, &k)) { + igraph_vector_int_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. + * + * + * 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_int_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_NO_LOOPS, IGRAPH_NO_MULTIPLE)); + 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(igraph_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_int_binsearch(v1, j, &u)) { + igraph_vector_int_insert(v1, u, j); + } + } + + IGRAPH_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. + * + * + * 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. + * + * + * 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. + * + * + * 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. + * + * + * 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/src/rigraph/core/misc/coloring.c b/src/rigraph/core/misc/coloring.c new file mode 100644 index 0000000..39b0ff9 --- /dev/null +++ b/src/rigraph/core/misc/coloring.c @@ -0,0 +1,161 @@ +/* + Heuristic graph coloring algorithms. + Copyright (C) 2017 Szabolcs Horvat + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public 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_coloring.h" +#include "igraph_interface.h" +#include "igraph_adjlist.h" + +#include "core/indheap.h" +#include "core/interruption.h" + +static 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_LOOPS_TWICE, IGRAPH_MULTIPLE)); + IGRAPH_FINALLY(igraph_adjlist_destroy, &adjlist); + + /* find maximum degree and a corresponding vertex */ + { + igraph_vector_t degree; + + IGRAPH_CHECK(igraph_vector_init(°ree, 0)); + IGRAPH_FINALLY(igraph_vector_destroy, °ree); + IGRAPH_CHECK(igraph_degree(graph, °ree, igraph_vss_all(), IGRAPH_ALL, 0)); + + vertex = igraph_vector_which_max(°ree); + maxdeg = VECTOR(degree)[vertex]; + + igraph_vector_destroy(°ree); + 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. + * + * + * 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. + * + * + * 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/src/rigraph/core/misc/conversion.c b/src/rigraph/core/misc/conversion.c new file mode 100644 index 0000000..b16a70e --- /dev/null +++ b/src/rigraph/core/misc/conversion.c @@ -0,0 +1,993 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2005-2012 Gabor Csardi + 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_sparsemat.h" +#include "igraph_random.h" + +#include "core/fixed_vectorlist.h" +#include "graph/attributes.h" +#include "misc/conversion_internal.h" + +/** + * \ingroup conversion + * \function igraph_get_adjacency + * \brief Returns the adjacency matrix of a graph + * + * + * The result is an adjacency matrix. Entry i, j of the matrix + * contains the number of edges connecting vertex i to vertex j. + * \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 + * if the graph is undirected. + * \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. + * + * + * The result is an adjacency matrix. Entry i, j of the matrix + * contains the number of edges connecting vertex i to vertex j. + * \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); + 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 IGRAPH_SUCCESS; +} + +/** + * \ingroup conversion + * \function igraph_get_edgelist + * \brief Returns the list of edges in a graph + * + * 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, e.g. the first edge is + * res[0]->res[|E|], the second is + * res[1]->res[|E|+1], etc. + * \return Error code. + * + * \sa \ref igraph_edges() to return the result only for some edge ids. + * + * 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 + * + * + * 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: + * \clist + * \cli IGRAPH_TO_DIRECTED_ARBITRARY + * The number of edges in the + * graph stays the same, an arbitrarily directed edge is + * created for each undirected edge. + * \cli IGRAPH_TO_DIRECTED_MUTUAL + * Two directed edges are + * created for each undirected edge, one in each direction. + * \cli IGRAPH_TO_DIRECTED_RANDOM + * Each undirected edge is converted to a randomly oriented + * directed one. + * \cli IGRAPH_TO_DIRECTED_ACYCLIC + * Each undirected edge is converted to a directed edge oriented + * from a lower index vertex to a higher index one. If no self-loops + * were present, then the result is a directed acyclic graph. + * \endclist + * \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) { + long int no_of_edges = igraph_ecount(graph); + long int no_of_nodes = igraph_vcount(graph); + + if (igraph_is_directed(graph)) { + return IGRAPH_SUCCESS; + } + + switch (mode) { + case IGRAPH_TO_DIRECTED_ARBITRARY: + case IGRAPH_TO_DIRECTED_RANDOM: + case IGRAPH_TO_DIRECTED_ACYCLIC: + { + igraph_t newgraph; + igraph_vector_t edges; + long int size = no_of_edges * 2; + long int i; + + IGRAPH_VECTOR_INIT_FINALLY(&edges, size); + IGRAPH_CHECK(igraph_get_edgelist(graph, &edges, 0)); + + if (mode == IGRAPH_TO_DIRECTED_RANDOM) { + RNG_BEGIN(); + + for (i=0; i < no_of_edges; ++i) { + if (RNG_INTEGER(0,1)) { + igraph_real_t temp = VECTOR(edges)[2*i]; + VECTOR(edges)[2*i] = VECTOR(edges)[2*i+1]; + VECTOR(edges)[2*i+1] = temp; + } + } + + RNG_END(); + } else if (mode == IGRAPH_TO_DIRECTED_ACYCLIC) { + /* Currently, the endpoints of undirected edges are ordered in the + internal graph datastructure, i.e. it is always true that from < to. + However, it is not guaranteed that this will not be changed in + the future, and this ordering should not be relied on outside of + the implementation of the minimal API in type_indexededgelist.c. + + Therefore, we order the edge endpoints anyway in the following loop: */ + for (i=0; i < no_of_edges; ++i) { + if (VECTOR(edges)[2*i] > VECTOR(edges)[2*i+1]) { + igraph_real_t temp = VECTOR(edges)[2*i]; + VECTOR(edges)[2*i] = VECTOR(edges)[2*i+1]; + VECTOR(edges)[2*i+1] = temp; + } + } + } + + 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, 1); + igraph_vector_destroy(&edges); + IGRAPH_FINALLY_CLEAN(2); + + igraph_destroy(graph); + *graph = newgraph; + + break; + } + case IGRAPH_TO_DIRECTED_MUTUAL: + { + igraph_t newgraph; + igraph_vector_t edges; + igraph_vector_t index; + 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_FINALLY_CLEAN(3); + + igraph_destroy(graph); + *graph = newgraph; + + break; + } + default: + IGRAPH_ERROR("Cannot direct graph, invalid mode", IGRAPH_EINVAL); + } + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_to_undirected + * \brief Convert a directed graph to an undirected one. + * + * + * 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 that 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; loop edges are kept unconditionally. + * 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. \c NULL means that + * the edge attributes are lost during the conversion, \em except + * when \c mode is \c IGRAPH_TO_UNDIRECTED_EACH, in which case the + * edge attributes are kept intact. + * \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, last; + 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); \ + } +#define ADD_NEW_EDGE() { \ + IGRAPH_CHECK(igraph_vector_push_back(&edges, i)); \ + IGRAPH_CHECK(igraph_vector_push_back(&edges, last)); \ +} +#define MERGE_INTO_CURRENT_EDGE(which) { \ + if (attr) { \ + VECTOR(mergeinto)[which] = actedge; \ + } \ +} + + STEPOUT(); + STEPIN(); + + while (p1 < n_out && n1 <= i && p2 < n_in && n2 <= i) { + last = (n1 <= n2) ? n1 : n2; + ADD_NEW_EDGE(); + while (p1 < n_out && last == n1) { + MERGE_INTO_CURRENT_EDGE(e1); + STEPOUT(); + } + while (p2 < n_in && last == n2) { + MERGE_INTO_CURRENT_EDGE(e2); + STEPIN(); + } + actedge++; + } + + while (p1 < n_out && n1 <= i) { + last = n1; + ADD_NEW_EDGE(); + while (p1 < n_out && last == n1) { + MERGE_INTO_CURRENT_EDGE(e1); + STEPOUT(); + } + actedge++; + } + + while (p2 < n_in && n2 <= i) { + last = n2; + ADD_NEW_EDGE(); + while (p2 < n_in && last == n2) { + MERGE_INTO_CURRENT_EDGE(e2); + STEPIN(); + } + actedge++; + } + } + +#undef MERGE_INTO_CURRENT_EDGE +#undef ADD_NEW_EDGE +#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= 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ü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(°rees, n); + IGRAPH_VECTOR_INIT_FINALLY(&neighbors, 1); + + IGRAPH_CHECK(igraph_degree(graph, °rees, 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(°rees); + igraph_vector_destroy(&neighbors); + IGRAPH_FINALLY_CLEAN(2); + + return IGRAPH_SUCCESS; +} diff --git a/src/rigraph/core/misc/conversion_internal.h b/src/rigraph/core/misc/conversion_internal.h new file mode 100644 index 0000000..e234a9b --- /dev/null +++ b/src/rigraph/core/misc/conversion_internal.h @@ -0,0 +1,28 @@ +/* + IGraph library. + Copyright (C) 2021 The igraph development 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, see . +*/ + +#ifndef IGRAPH_MISC_CONVERSION_INTERNAL_H +#define IGRAPH_MISC_CONVERSION_INTERNAL_H + +#include "igraph_sparsemat.h" +#include "igraph_types.h" + +int igraph_i_normalize_sparsemat(igraph_sparsemat_t *sparsemat, + igraph_bool_t column_wise); + +#endif diff --git a/src/rigraph/core/misc/degree_sequence.cpp b/src/rigraph/core/misc/degree_sequence.cpp new file mode 100644 index 0000000..fcc1130 --- /dev/null +++ b/src/rigraph/core/misc/degree_sequence.cpp @@ -0,0 +1,785 @@ +/* + IGraph library. + Constructing realizations of degree sequences and bi-degree sequences. + Copyright (C) 2018-2020 The igraph development 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, see . +*/ + +#include "igraph_constructors.h" +#include "igraph_interface.h" + +#include +#include +#include +#include + +#define IGRAPH_I_MULTI_EDGES_SW 0x02 /* 010, more than one edge allowed between distinct vertices */ +#define IGRAPH_I_MULTI_LOOPS_SW 0x04 /* 100, more than one self-loop allowed on the same vertex */ + +/******************************/ +/***** Helper constructs ******/ +/******************************/ + +// (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 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 inline bool degree_greater(const T &a, const T &b) { + return a.degree > b.degree; +} + +template inline bool degree_less(const T &a, const T &b) { + return a.degree < b.degree; +} + + +/*************************************/ +/***** Undirected simple graphs ******/ +/*************************************/ + +// Generate simple 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 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); + } else { + std::stable_sort(vertices.begin(), vertices.end(), degree_greater); + } + + // take the next vertex to be connected up + vd_pair vd = vertices.back(); + vertices.pop_back(); + + 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 cannot be realized as a simple graph.", 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 vlist; + vlist vertices; + for (int i = 0; i < n; ++i) { + vertices.push_back(vd_pair(i, VECTOR(*deg)[i])); + } + + std::vector pointers; + pointers.reserve(n); + for (vlist::iterator it = vertices.begin(); it != vertices.end(); ++it) { + pointers.push_back(it); + } + + for (std::vector::iterator pt = pointers.begin(); pt != pointers.end(); ++pt) { + vertices.sort(degree_greater); + + vd_pair vd = **pt; + vertices.erase(*pt); + + 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 cannot be realized as a simple graph.", IGRAPH_EINVAL); +} + + +/***********************************/ +/***** Undirected multigraphs ******/ +/***********************************/ + +// Given a sequence that is sorted, except for its first element, +// move the first element to the correct position fully sort the sequence. +template +static void bubble_up(It first, It last, Compare comp) { + if (first == last) + return; + It it = first; + it++; + while (it != last) { + if (comp(*first, *it)) { + break; + } else { + std::swap(*first, *it); + } + first = it; + it++; + } +} + +// In each step, choose a vertex (the largest degree one if largest=true, +// the smallest degree one otherwise) and connect it to the largest remaining degree vertex. +// This will create a connected loopless multigraph, if one exists. +// If loops=true, and a loopless multigraph does not exist, complete the procedure +// by adding loops on the last vertex. +// If largest=false, and the degree sequence was potentially connected, the resulting +// graph will be connected. +static int igraph_i_realize_undirected_multi(const igraph_vector_t *deg, igraph_vector_t *edges, bool loops, bool largest) { + long vcount = igraph_vector_size(deg); + + if (vcount == 0) + return IGRAPH_SUCCESS; + + std::vector vertices; + vertices.reserve(vcount); + for (int i = 0; i < vcount; ++i) { + long d = VECTOR(*deg)[i]; + vertices.push_back(vd_pair(i, d)); + } + + // Initial sort in non-increasing order. + std::stable_sort(vertices.begin(), vertices.end(), degree_greater); + + long ec = 0; + while (! vertices.empty()) { + // Remove any zero degrees, and error on negative ones. + + vd_pair &w = vertices.back(); + + if (w.degree == 0) { + vertices.pop_back(); + continue; + } + + // If only one vertex remains, then the degree sequence cannot be realized as + // a loopless multigraph. We either complete the graph by adding loops on this vertex + // or throw an error, depending on the 'loops' setting. + if (vertices.size() == 1) { + if (loops) { + for (long i=0; i < w.degree/2; ++i) { + VECTOR(*edges)[2*ec] = w.vertex; + VECTOR(*edges)[2*ec+1] = w.vertex; + ec++; + } + break; + } else { + IGRAPH_ERROR("The given degree sequence cannot be realized as a loopless multigraph.", IGRAPH_EINVAL); + } + } + + // At this point we are guaranteed to have at least two remaining vertices. + + vd_pair *u, *v; + if (largest) { + u = &vertices[0]; + v = &vertices[1]; + } else { + u = &vertices.front(); + v = &vertices.back(); + } + + u->degree -= 1; + v->degree -= 1; + + VECTOR(*edges)[2*ec] = u->vertex; + VECTOR(*edges)[2*ec+1] = v->vertex; + ec++; + + // Now the first element may be out of order. + // If largest=true, the first two elements may be out of order. + // Restore the sorted order using a single step of bubble sort. + if (largest) { + bubble_up(vertices.begin()+1, vertices.end(), degree_greater); + } + bubble_up(vertices.begin(), vertices.end(), degree_greater); + } + + return IGRAPH_SUCCESS; +} + + +static int igraph_i_realize_undirected_multi_index(const igraph_vector_t *deg, igraph_vector_t *edges, bool loops) { + long vcount = igraph_vector_size(deg); + + if (vcount == 0) + return IGRAPH_SUCCESS; + + typedef std::list vlist; + vlist vertices; + for (int i = 0; i < vcount; ++i) { + vertices.push_back(vd_pair(i, VECTOR(*deg)[i])); + } + + std::vector pointers; + pointers.reserve(vcount); + for (vlist::iterator it = vertices.begin(); it != vertices.end(); ++it) { + pointers.push_back(it); + } + + // Initial sort + vertices.sort(degree_greater); + + long ec = 0; + for (std::vector::iterator pt = pointers.begin(); pt != pointers.end(); ++pt) { + vd_pair vd = **pt; + vertices.erase(*pt); + + while (vd.degree > 0) { + vlist::iterator uit = vertices.begin(); + + if (vertices.empty() || uit->degree == 0) { + // We are out of non-zero degree vertices to connect to. + if (loops) { + for (long i=0; i < vd.degree/2; ++i) { + VECTOR(*edges)[2*ec] = vd.vertex; + VECTOR(*edges)[2*ec+1] = vd.vertex; + ec++; + } + return IGRAPH_SUCCESS; + } else { + IGRAPH_ERROR("The given degree sequence cannot be realized as a loopless multigraph.", IGRAPH_EINVAL); + } + } + + vd.degree -= 1; + uit->degree -= 1; + + VECTOR(*edges)[2*ec] = vd.vertex; + VECTOR(*edges)[2*ec+1] = uit->vertex; + ec++; + + // If there are at least two elements, and the first two are not in order, + // re-sort the list. A possible optimization would be a version of + // bubble_up() that can exchange list nodes instead of swapping their values. + if (vertices.size() > 1) { + vlist::iterator wit = uit; + ++wit; + + if (wit->degree > uit->degree) { + vertices.sort(degree_greater); + } + } + } + } + + return IGRAPH_SUCCESS; +} + + +/***********************************/ +/***** Directed simple graphs ******/ +/***********************************/ + +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 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); + + // 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 + // note: a vertex with non-zero out-degree is guaranteed to exist + // because there are _some_ non-zero degrees and the sum of in- and out-degrees + // is the same + 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); + } + + // are there a sufficient number of other vertices to connect to? + if (static_cast(vertices.size()) - 1 < vdp->degree.second) { + goto fail; + } + + // create the connections + int k = 0; + for (std::vector::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 sequences cannot be realized as a simple graph.", 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 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 pointers; + pointers.reserve(n); + for (vlist::iterator it = vertices.begin(); it != vertices.end(); ++it) { + pointers.push_back(it); + } + + for (std::vector::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); + + // choose a vertex the out-stubs of which will be connected + vbd_pair &vd = **pt; + + if (vd.degree.second == 0) { + continue; + } + + 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 sequences cannot be realized as a simple graph.", IGRAPH_EINVAL); +} + + +/**************************/ +/***** Main functions *****/ +/**************************/ + +static int igraph_i_realize_undirected_degree_sequence( + igraph_t *graph, + const igraph_vector_t *deg, + igraph_edge_type_sw_t allowed_edge_types, + 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); + } + + if (node_count > 0 && igraph_vector_min(deg) < 0) { + IGRAPH_ERROR("Vertex degrees must be non-negative.", IGRAPH_EINVAL); + } + + igraph_vector_t edges; + IGRAPH_CHECK(igraph_vector_init(&edges, deg_sum)); + IGRAPH_FINALLY(igraph_vector_destroy, &edges); + + if ( (allowed_edge_types & IGRAPH_LOOPS_SW) && (allowed_edge_types & IGRAPH_I_MULTI_EDGES_SW) && (allowed_edge_types & IGRAPH_I_MULTI_LOOPS_SW ) ) + { + switch (method) { + case IGRAPH_REALIZE_DEGSEQ_SMALLEST: + IGRAPH_CHECK(igraph_i_realize_undirected_multi(deg, &edges, true, false)); + break; + case IGRAPH_REALIZE_DEGSEQ_LARGEST: + IGRAPH_CHECK(igraph_i_realize_undirected_multi(deg, &edges, true, true)); + break; + case IGRAPH_REALIZE_DEGSEQ_INDEX: + IGRAPH_CHECK(igraph_i_realize_undirected_multi_index(deg, &edges, true)); + break; + default: + IGRAPH_ERROR("Invalid degree sequence realization method.", IGRAPH_EINVAL); + } + } + else if ( ! (allowed_edge_types & IGRAPH_LOOPS_SW) && (allowed_edge_types & IGRAPH_I_MULTI_EDGES_SW) ) + { + switch (method) { + case IGRAPH_REALIZE_DEGSEQ_SMALLEST: + IGRAPH_CHECK(igraph_i_realize_undirected_multi(deg, &edges, false, false)); + break; + case IGRAPH_REALIZE_DEGSEQ_LARGEST: + IGRAPH_CHECK(igraph_i_realize_undirected_multi(deg, &edges, false, true)); + break; + case IGRAPH_REALIZE_DEGSEQ_INDEX: + IGRAPH_CHECK(igraph_i_realize_undirected_multi_index(deg, &edges, false)); + break; + default: + IGRAPH_ERROR("Invalid degree sequence realization method.", IGRAPH_EINVAL); + } + } + else if ( (allowed_edge_types & IGRAPH_LOOPS_SW) && ! (allowed_edge_types & IGRAPH_I_MULTI_LOOPS_SW) && ! (allowed_edge_types & IGRAPH_I_MULTI_EDGES_SW) ) + { + IGRAPH_ERROR("Graph realization with at most one self-loop per vertex is not implemented.", IGRAPH_UNIMPLEMENTED); + } + else if ( ! (allowed_edge_types & IGRAPH_LOOPS_SW) && ! (allowed_edge_types & IGRAPH_I_MULTI_EDGES_SW) ) + { + 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); + } + } + else + { + /* Remainig cases: + * - At most one self-loop per vertex but multi-edges between distinct vertices allowed. + * - At most one edge between distinct vertices but multi-self-loops allowed. + * These cases cannot currently be requested through the documented API, + * so no explanatory error message for now. */ + return IGRAPH_UNIMPLEMENTED; + } + + 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_edge_type_sw_t allowed_edge_types, + 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); + } + + if (node_count > 0 && (igraph_vector_min(outdeg) < 0 || igraph_vector_min(indeg) < 0)) { + IGRAPH_ERROR("Vertex degrees must be non-negative.", IGRAPH_EINVAL); + } + + /* TODO implement loopless and loopy multigraph case */ + if (allowed_edge_types != IGRAPH_SIMPLE_SW) { + IGRAPH_ERROR("Realizing directed degree sequences as non-simple graphs is not implemented.", IGRAPH_UNIMPLEMENTED); + } + + 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 directed 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 generates an undirected graph that realizes a given degree sequence, + * or a directed graph that realized a given pair of out- and in-degree sequences. + * + * + * Simple undirected graphs are constructed using the Havel-Hakimi algorithm + * (undirected case), or the analogous Kleitman-Wang algorithm (directed case). + * These 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. This step is repeated until all degrees + * have been connected up. + * + * + * Loopless multigraphs are generated using an analogous algorithm: an arbitrary vertex is chosen, + * and it is connected with a single connection to a highest remaining degee vertex. If self-loops + * are also allowed, the same algorithm is used, but if a non-zero vertex remains at the end of the + * procedure, the graph is completed by adding self-loops to it. Thus, the result will contain at most + * one vertex with self-loops. + * + * + * The \c method parameter controls the order in which the vertices to be connected are chosen. + * + * + * References: + * + * + * V. Havel, + * Poznámka o existenci konečných grafů (A remark on the existence of finite graphs), + * Časopis pro pěstování matematiky 80, 477-480 (1955). + * http://eudml.org/doc/19050 + * + * + * S. L. Hakimi, + * On Realizability of a Set of Integers as Degrees of the Vertices of a Linear Graph, + * Journal of the SIAM 10, 3 (1962). + * https://www.jstor.org/stable/2098746 + * + * + * D. J. Kleitman and D. L. Wang, + * Algorithms for Constructing Graphs and Digraphs with Given Valences and Factors, + * Discrete Mathematics 6, 1 (1973). + * https://doi.org/10.1016/0012-365X%2873%2990037-X + * + * + * Sz. Horvát and C. D. Modes, + * Connectivity matters: Construction and exact random sampling of connected graphs (2020). + * https://arxiv.org/abs/2009.03747 + * + * \param graph Pointer to an uninitialized graph object. + * \param outdeg The degree sequence of an undirected graph + * (if \p indeg is NULL), or the out-degree sequence of + * a directed graph (if \p indeg is given). + * \param indeg The in-degree sequence of a directed graph. + * Pass \c NULL to generate an undirected graph. + * \param allowed_edge_types The types of edges to allow in the graph. For directed graphs, + * only \c IGRAPH_SIMPLE_SW is implemented at this moment. For undirected + * graphs, the following values are valid: + * \clist + * \cli IGRAPH_SIMPLE_SW + * simple graphs (i.e. no self-loops or multi-edges allowed). + * \cli IGRAPH_LOOPS_SW + * single self-loops are allowed, but not multi-edges; currently not implemented. + * \cli IGRAPH_MULTI_SW + * multi-edges are allowed, but not self-loops. + * \cli IGRAPH_LOOPS_SW | IGRAPH_MULTI_SW + * both self-loops and multi-edges are allowed. + * \endclist + * \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, regardless of whether multi-edges are allowed, + * provided that a connected realization exists (see Horvát and Modes, 2020, as well as + * http://szhorvat.net/pelican/hh-connected-graphs.html). + * 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_UNIMPLEMENTED + * The requested method is not implemented. + * \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() to test graphicality without generating a graph; + * \ref igraph_degree_sequence_game() to generate random graphs with a given degree sequence; + * \ref igraph_k_regular_game() to generate random regular graphs; + * \ref igraph_rewire() to randomly rewire the edges of a graph while preserving its degree sequence. + * + */ + +int igraph_realize_degree_sequence( + igraph_t *graph, + const igraph_vector_t *outdeg, const igraph_vector_t *indeg, + igraph_edge_type_sw_t allowed_edge_types, + 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 = indeg != 0; + + try { + if (directed) { + return igraph_i_realize_directed_degree_sequence(graph, outdeg, indeg, allowed_edge_types, method); + } else { + return igraph_i_realize_undirected_degree_sequence(graph, outdeg, allowed_edge_types, method); + } + } catch (const std::bad_alloc &) { + IGRAPH_ERROR("Cannot realize degree sequence due to insufficient memory.", IGRAPH_ENOMEM); + } +} diff --git a/src/rigraph/core/misc/embedding.c b/src/rigraph/core/misc/embedding.c new file mode 100644 index 0000000..04b6d63 --- /dev/null +++ b/src/rigraph/core/misc/embedding.c @@ -0,0 +1,1176 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2013 Gabor Csardi + 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_adjlist.h" +#include "igraph_blas.h" +#include "igraph_centrality.h" +#include "igraph_interface.h" +#include "igraph_structural.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 */ +static 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. */ +static 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. */ +static 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 */ +static 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. */ +static 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. */ +static 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. */ +static 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. */ +static 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. */ +static 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; +} + +static 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. */ +static 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; +} + +static 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. */ +static 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. */ +static 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. */ +static 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. */ +static 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; +} + +static 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; + igraph_vector_t tmpD; + + data.graph = graph; + data.cvec = cvec; + data.cvec2 = cvec2; + data.outlist = &outlist; + data.inlist = &inlist; + data.eoutlist = &eoutlist; + data.einlist = &einlist; + data.tmp = &tmp; + data.weights = weights; + + 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_LOOPS_ONCE, IGRAPH_MULTIPLE)); + IGRAPH_FINALLY(igraph_adjlist_destroy, &outlist); + if (!symmetric) { + IGRAPH_CHECK(igraph_adjlist_init(graph, &inlist, IGRAPH_IN, IGRAPH_LOOPS_ONCE, IGRAPH_MULTIPLE)); + IGRAPH_FINALLY(igraph_adjlist_destroy, &inlist); + } + } else { + IGRAPH_CHECK(igraph_inclist_init(graph, &eoutlist, IGRAPH_OUT, IGRAPH_LOOPS_ONCE)); + IGRAPH_FINALLY(igraph_inclist_destroy, &eoutlist); + if (!symmetric) { + IGRAPH_CHECK(igraph_inclist_init(graph, &einlist, IGRAPH_IN, IGRAPH_LOOPS_ONCE)); + 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 an n-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=U D V^T. In the case, + * where the graph is a random dot product graph generated using latent + * position vectors in R^n for each vertex, the embedding will + * provide an estimate of these latent vectors. + * + * + * For undirected graphs, the latent positions are calculated as + * X = U^n D^(1/2) where U^n 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. + * + * + * For directed graphs, the embedding is defined as the pair + * X = U^n D^(1/2), Y = V^n D^(1/2). + * (For undirected graphs U=V, so it is sufficient to keep one of them.) + * + * \param graph The input graph, can be directed or undirected. + * \param n An integer scalar. This value is the embedding dimension of + * the spectral embedding. Should be smaller than the number of + * vertices. The largest n-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, IGRAPH_EIGEN_LM and + * IGRAPH_EIGEN_LA are the same because singular + * values are used for the ordering instead of eigenvalues. + * \param scaled Whether to return X and Y (if \c scaled is true), 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 + * n (number of vertices), nev and + * which 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 n, + 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, n, weights, which, scaled, + X, Y, D, cvec, /* deg2=*/ 0, + options, callback, callback_right, + /*symmetric=*/ !directed, + /*eigen=*/ !directed, /*zapsmall=*/ 1); +} + +static 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_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(°, 0); + igraph_strength(graph, °, 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(°); + 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=*/ °, /*deg2=*/ 0, + options, callback, 0, /*symmetric=*/ 1, + /*eigen=*/ 1, /*zapsmall=*/ 1)); + + igraph_vector_destroy(°); + IGRAPH_FINALLY_CLEAN(1); + + return 0; +} + +static 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_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(°_in, n); + IGRAPH_VECTOR_INIT_FINALLY(°_out, n); + igraph_strength(graph, °_in, igraph_vss_all(), IGRAPH_IN, /*loops=*/ 1, + weights); + igraph_strength(graph, °_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=*/ °_in, + /*deg2=*/ °_out, options, callback, + callback_right, /*symmetric=*/ 0, /*eigen=*/ 0, + /*zapsmall=*/ 1)); + + igraph_vector_destroy(°_in); + igraph_vector_destroy(°_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 n 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, IGRAPH_EIGEN_LM and + * IGRAPH_EIGEN_LA 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 \c scaled is true), 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 + * n (number of vertices), nev and + * which 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 n, + const igraph_vector_t *weights, + igraph_eigen_which_position_t which, + 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, n, weights, which, type, scaled, + X, Y, D, options); + } else { + return igraph_i_lse_und(graph, n, weights, which, type, scaled, + X, Y, D, options); + } +} + +/** + * \function igraph_dim_select + * Dimensionality selection + * + * Dimensionality selection for singular values using + * profile likelihood. + * + * + * The input of the function is a numeric vector which contains + * the measure of "importance" for each dimension. + * + * + * 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. + * + * + * 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/src/rigraph/core/misc/feedback_arc_set.c b/src/rigraph/core/misc/feedback_arc_set.c new file mode 100644 index 0000000..5f753a3 --- /dev/null +++ b/src/rigraph/core/misc/feedback_arc_set.c @@ -0,0 +1,661 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2011-2012 Gabor Csardi + 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_dqueue.h" +#include "igraph_interface.h" +#include "igraph_memory.h" +#include "igraph_structural.h" +#include "igraph_visitor.h" + +#include "internal/glpk_support.h" +#include "misc/feedback_arc_set.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. + * + * + * 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. + * + * + * 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 > 0 ? no_of_nodes - 1 : 0); + 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(°rees, no_of_nodes); + IGRAPH_VECTOR_INIT_FINALLY(&roots, no_of_nodes); + + IGRAPH_CHECK(igraph_strength(graph, °rees, igraph_vss_all(), + IGRAPH_ALL, 0, weights)); + IGRAPH_CHECK((int) igraph_vector_qsort_ind(°rees, &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(°rees); + 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(igraph_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(igraph_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 */ + IGRAPH_GLPK_SETUP(); + 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 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/src/rigraph/core/misc/feedback_arc_set.h b/src/rigraph/core/misc/feedback_arc_set.h new file mode 100644 index 0000000..7091e1f --- /dev/null +++ b/src/rigraph/core/misc/feedback_arc_set.h @@ -0,0 +1,39 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2009-2021 The igraph development 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., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#ifndef IGRAPH_FEEDBACK_ARC_SET_INTERNAL_H +#define IGRAPH_FEEDBACK_ARC_SET_INTERNAL_H + +#include "igraph_decls.h" +#include "igraph_datatype.h" +#include "igraph_vector.h" + +__BEGIN_DECLS + +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/src/rigraph/core/misc/graphicality.c b/src/rigraph/core/misc/graphicality.c new file mode 100644 index 0000000..f468d34 --- /dev/null +++ b/src/rigraph/core/misc/graphicality.c @@ -0,0 +1,959 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2020 The igraph development 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, see . +*/ + +#include "igraph_graphicality.h" + +#include "igraph_qsort.h" + +#define IGRAPH_I_MULTI_EDGES_SW 0x02 /* 010, more than one edge allowed between distinct vertices */ +#define IGRAPH_I_MULTI_LOOPS_SW 0x04 /* 100, more than one self-loop allowed on the same vertex */ + +static int igraph_i_is_graphical_undirected_multi_loops(const igraph_vector_t *degrees, igraph_bool_t *res); +static int igraph_i_is_graphical_undirected_loopless_multi(const igraph_vector_t *degrees, igraph_bool_t *res); +static int igraph_i_is_graphical_undirected_loopy_simple(const igraph_vector_t *degrees, igraph_bool_t *res); +static int igraph_i_is_graphical_undirected_simple(const igraph_vector_t *degrees, igraph_bool_t *res); + +static int igraph_i_is_graphical_directed_loopy_multi(const igraph_vector_t *out_degrees, const igraph_vector_t *in_degrees, igraph_bool_t *res); +static int igraph_i_is_graphical_directed_loopless_multi(const igraph_vector_t *out_degrees, const igraph_vector_t *in_degrees, igraph_bool_t *res); +static int igraph_i_is_graphical_directed_loopy_simple(const igraph_vector_t *out_degrees, const igraph_vector_t *in_degrees, igraph_bool_t *res); +static int igraph_i_is_graphical_directed_simple(const igraph_vector_t *out_degrees, const igraph_vector_t *in_degrees, igraph_bool_t *res); + +static int igraph_i_is_bigraphical_multi(const igraph_vector_t *degrees1, const igraph_vector_t *degrees2, igraph_bool_t *res); +static int igraph_i_is_bigraphical_simple(const igraph_vector_t *degrees1, const igraph_vector_t *degrees2, igraph_bool_t *res); + + +/** + * \function igraph_is_graphical + * \brief Is there a graph with the given degree sequence? + * + * Determines whether a sequence of integers can be the degree sequence of some graph. + * The classical concept of graphicality assumes simple graphs. This function can perform + * the check also when either self-loops, multi-edge, or both are allowed in the graph. + * + * + * For simple undirected graphs, the Erdős-Gallai conditions are checked using the linear-time + * algorithm of Cloteaux. If both self-loops and multi-edges are allowed, + * it is sufficient to chek that that sum of degrees is even. If only multi-edges are allowed, but + * not self-loops, there is an additional condition that the sum of degrees be no smaller than twice + * the maximum degree. If at most one self-loop is allowed per vertex, but no multi-edges, a modified + * version of the Erdős-Gallai conditions are used (see Cairns & Mendan). + * + * + * For simple directed graphs, the Fulkerson-Chen-Anstee theorem is used with the relaxation by Berger. + * If both self-loops and multi-edges are allowed, then it is sufficient to check that the sum of + * in- and out-degrees is the same. If only multi-edges are allowed, but not self loops, there is an + * additional condition that the sum of out-degrees (or equivalently, in-degrees) is no smaller than + * the maximum total degree. If single self-loops are allowed, but not multi-edges, the problem is equivalent + * to realizability as a simple bipartite graph, thus the Gale-Ryser theorem can be used; see + * \ref igraph_is_bigraphical() for more information. + * + * + * References: + * + * + * P. Erdős and T. Gallai, Gráfok előírt fokú pontokkal, Matematikai Lapok 11, pp. 264–274 (1960). + * https://users.renyi.hu/~p_erdos/1961-05.pdf + * + * + * Z Király, Recognizing graphic degree sequences and generating all realizations. + * TR-2011-11, Egerváry Research Group, H-1117, Budapest, Hungary. ISSN 1587-4451 (2012). + * http://bolyai.cs.elte.hu/egres/tr/egres-11-11.pdf + * + * + * B. Cloteaux, Is This for Real? Fast Graphicality Testing, Comput. Sci. Eng. 17, 91 (2015). + * https://dx.doi.org/10.1109/MCSE.2015.125 + * + * + * A. Berger, A note on the characterization of digraphic sequences, Discrete Math. 314, 38 (2014). + * https://dx.doi.org/10.1016/j.disc.2013.09.010 + * + * + * G. Cairns and S. Mendan, Degree Sequence for Graphs with Loops (2013). + * https://arxiv.org/abs/1303.2145v1 + * + * \param out_degrees A vector of integers specifying the degree sequence for + * undirected graphs or the out-degree sequence for directed graphs. + * \param in_degrees A vector of integers specifying the in-degree sequence for + * directed graphs. For undirected graphs, it must be \c NULL. + * \param allowed_edge_types The types of edges to allow in the graph: + * \clist + * \cli IGRAPH_SIMPLE_SW + * simple graphs (i.e. no self-loops or multi-edges allowed). + * \cli IGRAPH_LOOPS_SW + * single self-loops are allowed, but not multi-edges. + * \cli IGRAPH_MULTI_SW + * multi-edges are allowed, but not self-loops. + * \cli IGRAPH_LOOPS_SW | IGRAPH_MULTI_SW + * both self-loops and multi-edges are allowed. + * \endclist + * \param res Pointer to a Boolean. The result will be stored here. + * + * \return Error code. + * + * \sa \ref igraph_is_bigraphical() to check if a bi-degree-sequence can be realized as a bipartite graph; + * \ref igraph_realize_degree_sequence() to construct a graph with a given degree sequence. + * + * Time complexity: O(n^2) for simple directed graphs, O(n log n) for graphs with self-loops, + * and O(n) for all other cases, where n is the length of the degree sequence(s). + */ +int igraph_is_graphical(const igraph_vector_t *out_degrees, + const igraph_vector_t *in_degrees, + const igraph_edge_type_sw_t allowed_edge_types, + igraph_bool_t *res) +{ + /* Undirected case: */ + if (in_degrees == NULL) + { + if ( (allowed_edge_types & IGRAPH_LOOPS_SW) && (allowed_edge_types & IGRAPH_I_MULTI_LOOPS_SW )) { + /* Typically this case is used when multiple edges are allowed both as self-loops and + * between distinct vertices. However, the conditions are the same even if multi-edges + * are not allowed between distinct vertices (only as self-loops). Therefore, we + * do not test IGRAPH_I_MULTI_EDGES_SW in the if (...). */ + return igraph_i_is_graphical_undirected_multi_loops(out_degrees, res); + } + else if ( ! (allowed_edge_types & IGRAPH_LOOPS_SW) && (allowed_edge_types & IGRAPH_I_MULTI_EDGES_SW) ) { + return igraph_i_is_graphical_undirected_loopless_multi(out_degrees, res); + } + else if ( (allowed_edge_types & IGRAPH_LOOPS_SW) && ! (allowed_edge_types & IGRAPH_I_MULTI_LOOPS_SW) && ! (allowed_edge_types & IGRAPH_I_MULTI_EDGES_SW) ) { + return igraph_i_is_graphical_undirected_loopy_simple(out_degrees, res); + } + else if ( ! (allowed_edge_types & IGRAPH_LOOPS_SW) && ! (allowed_edge_types & IGRAPH_I_MULTI_EDGES_SW) ) { + return igraph_i_is_graphical_undirected_simple(out_degrees, res); + } else { + /* Remainig case: + * - At most one self-loop per vertex but multi-edges between distinct vertices allowed. + * These cases cannot currently be requested through the documented API, + * so no explanatory error message for now. */ + return IGRAPH_UNIMPLEMENTED; + } + } + /* Directed case: */ + else + { + if ( (allowed_edge_types & IGRAPH_LOOPS_SW) && (allowed_edge_types & IGRAPH_I_MULTI_EDGES_SW) && (allowed_edge_types & IGRAPH_I_MULTI_LOOPS_SW ) ) { + return igraph_i_is_graphical_directed_loopy_multi(out_degrees, in_degrees, res); + } + else if ( ! (allowed_edge_types & IGRAPH_LOOPS_SW) && (allowed_edge_types & IGRAPH_I_MULTI_EDGES_SW) ) { + return igraph_i_is_graphical_directed_loopless_multi(out_degrees, in_degrees, res); + } + else if ( (allowed_edge_types & IGRAPH_LOOPS_SW) && ! (allowed_edge_types & IGRAPH_I_MULTI_LOOPS_SW) && ! (allowed_edge_types & IGRAPH_I_MULTI_EDGES_SW) ) { + return igraph_i_is_graphical_directed_loopy_simple(out_degrees, in_degrees, res); + } + else if ( ! (allowed_edge_types & IGRAPH_LOOPS_SW) && ! (allowed_edge_types & IGRAPH_I_MULTI_EDGES_SW) ) { + return igraph_i_is_graphical_directed_simple(out_degrees, in_degrees, res); + } else { + /* Remainig cases: + * - At most one self-loop per vertex but multi-edges between distinct vertices allowed. + * - At most one edge between distinct vertices but multi-self-loops allowed. + * These cases cannot currently be requested through the documented API, + * so no explanatory error message for now. */ + return IGRAPH_UNIMPLEMENTED; + } + } + + /* can't reach here */ +} + +/** + * \function igraph_is_bigraphical + * \brief Is there a bipartite graph with the given bi-degree-sequence? + * + * Determines whether two sequences of integers can be the degree sequences of + * a bipartite graph. Such a pair of degree sequence is called \em bigraphical. + * + * + * When multi-edges are allowed, it is sufficient to check that the sum of degrees is the + * same in the two partitions. For simple graphs, the Gale-Ryser theorem is used + * with Berger's relaxation. + * + * + * References: + * + * + * H. J. Ryser, Combinatorial Properties of Matrices of Zeros and Ones, Can. J. Math. 9, 371 (1957). + * https://dx.doi.org/10.4153/cjm-1957-044-3 + * + * + * D. Gale, A theorem on flows in networks, Pacific J. Math. 7, 1073 (1957). + * https://dx.doi.org/10.2140/pjm.1957.7.1073 + * + * + * A. Berger, A note on the characterization of digraphic sequences, Discrete Math. 314, 38 (2014). + * https://dx.doi.org/10.1016/j.disc.2013.09.010 + * + * \param degrees1 A vector of integers specifying the degrees in the first partition + * \param degrees2 A vector of integers specifying the degrees in the second partition + * \param allowed_edge_types The types of edges to allow in the graph: + * \clist + * \cli IGRAPH_SIMPLE_SW + * simple graphs (i.e. no multi-edges allowed). + * \cli IGRAPH_MULTI_SW + * multi-edges are allowed. + * \endclist + * \param res Pointer to a Boolean. The result will be stored here. + * + * \return Error code. + * + * \sa \ref igraph_is_graphical() + * + * Time complexity: O(n log n) for simple graphs, O(n) for multigraphs, + * where n is the length of the larger degree sequence. + */ +int igraph_is_bigraphical(const igraph_vector_t *degrees1, + const igraph_vector_t *degrees2, + const igraph_edge_type_sw_t allowed_edge_types, + igraph_bool_t *res) +{ + /* Note: Bipartite graphs can't have self-loops so we ignore the IGRAPH_LOOPS_SW bit. */ + if (allowed_edge_types & IGRAPH_I_MULTI_EDGES_SW) { + return igraph_i_is_bigraphical_multi(degrees1, degrees2, res); + } else { + return igraph_i_is_bigraphical_simple(degrees1, degrees2, res); + } +} + + +/***** Undirected case *****/ + +/* Undirected graph with multi-self-loops: + * - Degrees must be non-negative. + * - The sum of degrees must be even. + * + * These conditions are valid regardless of whether multi-edges are allowed between distinct vertices. + */ +static int igraph_i_is_graphical_undirected_multi_loops(const igraph_vector_t *degrees, igraph_bool_t *res) { + long int sum_parity = 0; /* 0 if the degree sum is even, 1 if it is odd */ + long int n = igraph_vector_size(degrees); + long int i; + + for (i=0; i < n; ++i) { + long int d = VECTOR(*degrees)[i]; + + if (d < 0) { + *res = 0; + return IGRAPH_SUCCESS; + } + sum_parity = (sum_parity + d) & 1; + } + + *res = (sum_parity == 0); + + return IGRAPH_SUCCESS; +} + + +/* Undirected loopless multigraph: + * - Degrees must be non-negative. + * - The sum of degrees must be even. + * - The sum of degrees must be no smaller than 2*d_max. + */ +static int igraph_i_is_graphical_undirected_loopless_multi(const igraph_vector_t *degrees, igraph_bool_t *res) { + long int i; + long int n = igraph_vector_size(degrees); + long int dsum, dmax; + + /* Zero-length sequences are considered graphical. */ + if (n == 0) { + *res = 1; + return IGRAPH_SUCCESS; + } + + dsum = 0; dmax = 0; + for (i=0; i < n; ++i) { + long int d = VECTOR(*degrees)[i]; + + if (d < 0) { + *res = 0; + return IGRAPH_SUCCESS; + } + dsum += d; + if (d > dmax) { + dmax = d; + } + } + + *res = (dsum % 2 == 0) && (dsum >= 2*dmax); + + return IGRAPH_SUCCESS; +} + + +/* Undirected graph with no multi-edges and at most one self-loop per vertex: + * - Degrees must be non-negative. + * - The sum of degrees must be even. + * - Use the modification of the Erdős-Gallai theorem due to Cairns and Mendan. + */ +static int igraph_i_is_graphical_undirected_loopy_simple(const igraph_vector_t *degrees, igraph_bool_t *res) { + igraph_vector_t work; + long int w, b, s, c, n, k; + + n = igraph_vector_size(degrees); + + /* Zero-length sequences are considered graphical. */ + if (n == 0) { + *res = 1; + return IGRAPH_SUCCESS; + } + + /* The conditions from the loopy multigraph case are necessary here as well. */ + IGRAPH_CHECK(igraph_i_is_graphical_undirected_multi_loops(degrees, res)); + if (! *res) { + return IGRAPH_SUCCESS; + } + + /* + * We follow this paper: + * + * G. Cairns & S. Mendan: Degree Sequences for Graphs with Loops, 2013 + * https://arxiv.org/abs/1303.2145v1 + * + * They give the following modification of the Erdős-Gallai theorem: + * + * A non-increasing degree sequence d_1 >= ... >= d_n has a realization as + * a simple graph with loops (i.e. at most one self-loop allowed on each vertex) + * iff + * + * \sum_{i=1}^k d_i <= k(k+1) + \sum_{i=k+1}^{n} min(d_i, k) + * + * for each k=1..n + * + * The difference from Erdős-Gallai is that here we have the term + * k(k+1) instead of k(k-1). + * + * The implementation is analogous to igraph_i_is_graphical_undirected_simple(), + * which in turn is based on Király 2012. See comments in that function for details. + * w and k are zero-based here, unlike in the statement of the theorem above. + */ + + IGRAPH_CHECK(igraph_vector_copy(&work, degrees)); + IGRAPH_FINALLY(igraph_vector_destroy, &work); + + igraph_vector_reverse_sort(&work); + + *res = 1; + w = n - 1; b = 0; s = 0; c = 0; + for (k = 0; k < n; k++) { + b += VECTOR(work)[k]; + c += w; + while (w > k && VECTOR(work)[w] <= k + 1) { + s += VECTOR(work)[w]; + c -= (k + 1); + w--; + } + if (b > c + s + 2*(k + 1)) { + *res = 0; + break; + } + if (w == k) { + break; + } + } + + igraph_vector_destroy(&work); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + + +/* Undirected simple graph: + * - Degrees must be non-negative. + * - The sum of degrees must be even. + * - Use the Erdős-Gallai theorem. + */ +static int igraph_i_is_graphical_undirected_simple(const igraph_vector_t *degrees, igraph_bool_t *res) { + igraph_vector_int_t num_degs; /* num_degs[d] is the # of vertices with degree d */ + const long int p = igraph_vector_size(degrees); + long int dmin, dmax, dsum; + long int n; /* number of non-zero degrees */ + long int k, sum_deg, sum_ni, sum_ini; + long int i, dk; + long int zverovich_bound; + + if (p == 0) { + *res = 1; + return IGRAPH_SUCCESS; + } + + /* The following implementation of the Erdős-Gallai test + * is mostly a direct translation of the Python code given in + * + * Brian Cloteaux, Is This for Real? Fast Graphicality Testing, + * Computing Prescriptions, pp. 91-95, vol. 17 (2015) + * https://dx.doi.org/10.1109/MCSE.2015.125 + * + * It uses counting sort to achieve linear runtime. + */ + + IGRAPH_VECTOR_INT_INIT_FINALLY(&num_degs, p); + + dmin = p; dmax = 0; dsum = 0; n = 0; + for (i=0; i < p; ++i) { + long int d = VECTOR(*degrees)[i]; + + if (d < 0 || d >= p) { + *res = 0; + goto finish; + } + + if (d > 0) { + dmax = d > dmax ? d : dmax; + dmin = d < dmin ? d : dmin; + dsum += d; + n++; + VECTOR(num_degs)[d] += 1; + } + } + + if (dsum % 2 != 0) { + *res = 0; + goto finish; + } + + if (n == 0) { + *res = 1; + goto finish; /* all degrees are zero => graphical */ + } + + /* According to: + * + * G. Cairns, S. Mendan, and Y. Nikolayevsky, A sharp refinement of a result of Zverovich-Zverovich, + * Discrete Math. 338, 1085 (2015). + * https://dx.doi.org/10.1016/j.disc.2015.02.001 + * + * a sufficient but not necessary condition of graphicality for a sequence of + * n strictly positive integers is that + * + * dmin * n >= floor( (dmax + dmin + 1)^2 / 4 ) - 1 + * if dmin is odd or (dmax + dmin) mod 4 == 1 + * + * or + * + * dmin * n >= floor( (dmax + dmin + 1)^2 / 4 ) + * otherwise. + */ + + zverovich_bound = ((dmax + dmin + 1) * (dmax + dmin + 1)) / 4; + if (dmin % 2 == 1 || (dmax + dmin) % 4 == 1) { + zverovich_bound -= 1; + } + + if (dmin*n >= zverovich_bound) { + *res = 1; + goto finish; + } + + k = 0; sum_deg = 0; sum_ni = 0; sum_ini = 0; + for (dk = dmax; dk >= dmin; --dk) { + long int run_size, v; + + if (dk < k+1) { + *res = 1; + goto finish; + } + + run_size = VECTOR(num_degs)[dk]; + if (run_size > 0) { + if (dk < k + run_size) { + run_size = dk - k; + } + sum_deg += run_size * dk; + for (v=0; v < run_size; ++v) { + sum_ni += VECTOR(num_degs)[k+v]; + sum_ini += (k+v) * VECTOR(num_degs)[k+v]; + } + k += run_size; + if (sum_deg > k*(n-1) - k*sum_ni + sum_ini) { + *res = 0; + goto finish; + } + } + } + + *res = 1; + +finish: + igraph_vector_int_destroy(&num_degs); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + + +/***** Directed case *****/ + +/* Directed loopy multigraph: + * - Degrees must be non-negative. + * - The sum of in- and out-degrees must be the same. + */ +static int igraph_i_is_graphical_directed_loopy_multi(const igraph_vector_t *out_degrees, const igraph_vector_t *in_degrees, igraph_bool_t *res) { + long int sumdiff; /* difference between sum of in- and out-degrees */ + long int n = igraph_vector_size(out_degrees); + long int i; + + if (igraph_vector_size(in_degrees) != n) { + IGRAPH_ERROR("The length of out- and in-degree sequences must be the same.", IGRAPH_EINVAL); + } + + sumdiff = 0; + for (i=0; i < n; ++i) { + long int dout = VECTOR(*out_degrees)[i]; + long int din = VECTOR(*in_degrees)[i]; + + if (dout < 0 || din < 0) { + *res = 0; + return IGRAPH_SUCCESS; + } + + sumdiff += din - dout; + } + + *res = sumdiff == 0; + + return IGRAPH_SUCCESS; +} + + +/* Directed loopless multigraph: + * - Degrees must be non-negative. + * - The sum of in- and out-degrees must be the same. + * - The sum of out-degrees must be no smaller than d_max, + * where d_max is the largest total degree. + */ +static int igraph_i_is_graphical_directed_loopless_multi(const igraph_vector_t *out_degrees, const igraph_vector_t *in_degrees, igraph_bool_t *res) { + long int i, sumin, sumout, dmax; + long int n = igraph_vector_size(out_degrees); + + if (igraph_vector_size(in_degrees) != n) { + IGRAPH_ERROR("The length of out- and in-degree sequences must be the same.", IGRAPH_EINVAL); + } + + sumin = 0; sumout = 0; + dmax = 0; + for (i=0; i < n; ++i) { + long int dout = VECTOR(*out_degrees)[i]; + long int din = VECTOR(*in_degrees)[i]; + long int d = dout + din; + + if (dout < 0 || din < 0) { + *res = 0; + return IGRAPH_SUCCESS; + } + + sumin += din; sumout += dout; + + if (d > dmax) { + dmax = d; + } + } + + *res = (sumin == sumout) && (sumout >= dmax); + + return IGRAPH_SUCCESS; +} + + +/* Directed graph with no multi-edges and at most one self-loop per vertex: + * - Degrees must be non-negative. + * - Equivalent to bipartite simple graph. + */ +static int igraph_i_is_graphical_directed_loopy_simple(const igraph_vector_t *out_degrees, const igraph_vector_t *in_degrees, igraph_bool_t *res) { + long int n = igraph_vector_size(out_degrees); + + if (igraph_vector_size(in_degrees) != n) { + IGRAPH_ERROR("The length of out- and in-degree sequences must be the same.", IGRAPH_EINVAL); + } + + return igraph_i_is_bigraphical_simple(out_degrees, in_degrees, res); +} + + +/* Directed simple graph: + * - Degrees must be non-negative. + * - The sum of in- and out-degrees must be the same. + * - Use the Fulkerson-Chen-Anstee theorem + */ + +typedef struct { + const igraph_vector_t* first; + const igraph_vector_t* second; +} igraph_i_qsort_dual_vector_cmp_data_t; + +static 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; +} + +static int igraph_i_is_graphical_directed_simple(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; + + /* The conditions from the loopy multigraph case are necessary here as well. */ + IGRAPH_CHECK(igraph_i_is_graphical_directed_loopy_multi(out_degrees, in_degrees, res)); + if (! *res) { + return IGRAPH_SUCCESS; + } + + vcount = igraph_vector_size(out_degrees); + if (vcount == 0) { + *res = 1; + return IGRAPH_SUCCESS; + } + + /* Create an index vector that sorts the vertices by decreasing in-degree */ + 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; +} + + + +/***** Bipartite case *****/ + +/* Bipartite graph with multi-eges: + * - Degrees must be non-negative. + * - Sum of degrees must be the same in the two partitions. + */ +static int igraph_i_is_bigraphical_multi(const igraph_vector_t *degrees1, const igraph_vector_t *degrees2, igraph_bool_t *res) { + long int i; + long int sum1, sum2; + long int n1 = igraph_vector_size(degrees1), n2 = igraph_vector_size(degrees2); + + sum1 = 0; + for (i=0; i < n1; ++i) { + long int d = VECTOR(*degrees1)[i]; + + if (d < 0) { + *res = 0; + return IGRAPH_SUCCESS; + } + + sum1 += d; + } + + sum2 = 0; + for (i=0; i < n2; ++i) { + long int d = VECTOR(*degrees2)[i]; + + if (d < 0) { + *res = 0; + return IGRAPH_SUCCESS; + } + + sum2 += d; + } + + *res = (sum1 == sum2); + + return IGRAPH_SUCCESS; +} + + +/* Bipartite simple graph: + * - Degrees must be non-negative. + * - Sum of degrees must be the same in the two partitions. + * - Use the Gale-Ryser theorem. + */ +static int igraph_i_is_bigraphical_simple(const igraph_vector_t *degrees1, const igraph_vector_t *degrees2, igraph_bool_t *res) { + igraph_vector_t sorted_deg1, sorted_deg2; + long int n1 = igraph_vector_size(degrees1), n2 = igraph_vector_size(degrees2); + long int i, k; + long lhs_sum, partial_rhs_sum; + + if (n1 == 0 && n2 == 0) { + *res = 1; + return IGRAPH_SUCCESS; + } + + /* The conditions from the multigraph case are necessary here as well. */ + IGRAPH_CHECK(igraph_i_is_bigraphical_multi(degrees1, degrees2, res)); + if (! *res) { + return IGRAPH_SUCCESS; + } + + /* Ensure that degrees1 is the shorter vector as a minor optimization: */ + if (n2 < n1) { + const igraph_vector_t *tmp; + long int n; + + tmp = degrees1; + degrees1 = degrees2; + degrees2 = tmp; + + n = n1; + n1 = n2; + n2 = n; + } + + /* Copy and sort both vectors: */ + + IGRAPH_CHECK(igraph_vector_copy(&sorted_deg1, degrees1)); + IGRAPH_FINALLY(igraph_vector_destroy, &sorted_deg1); + igraph_vector_reverse_sort(&sorted_deg1); /* decreasing sort */ + + IGRAPH_CHECK(igraph_vector_copy(&sorted_deg2, degrees2)); + IGRAPH_FINALLY(igraph_vector_destroy, &sorted_deg2); + igraph_vector_sort(&sorted_deg2); /* increasing sort */ + + /* + * We follow the description of the Gale-Ryser theorem in: + * + * A. Berger, A note on the characterization of digraphic sequences, Discrete Math. 314, 38 (2014). + * http://dx.doi.org/10.1016/j.disc.2013.09.010 + * + * Gale-Ryser condition with 0-based indexing: + * + * a_i and b_i denote the degree sequences of the two partitions. + * + * Assuming that a_0 >= a_1 >= ... >= a_{n_1 - 1}, + * + * \sum_{i=0}^k a_i <= \sum_{j=0}^{n_2} min(b_i, k+1) + * + * for all 0 <= k < n_1 + */ + + /* While this formulation does not require sorting degree2, + * doing so allows for a linear-time incremental computation + * of the inequality's right-hand-side. + */ + + *res = 1; /* be optimistic */ + lhs_sum = 0; + partial_rhs_sum = 0; /* the sum of those elements in sorted_deg2 which are <= (k+1) */ + i = 0; /* points past the first element of sorted_deg2 which > (k+1) */ + for (k=0; k < n1; ++k) { + lhs_sum += VECTOR(sorted_deg1)[k]; + + /* Based on Theorem 3 in [Berger 2014], it is sufficient to do the check + * for k such that a_k > a_{k+1} and for k=(n_1-1). + */ + if (k < n1-1 && VECTOR(sorted_deg1)[k] == VECTOR(sorted_deg1)[k+1]) + continue; + + while (i < n2 && VECTOR(sorted_deg2)[i] <= k+1) { + partial_rhs_sum += VECTOR(sorted_deg2)[i]; + i++; + } + + /* rhs_sum for a given k is partial_rhs_sum + (n2 - i) * (k+1) */ + if (lhs_sum > partial_rhs_sum + (n2 - i) * (k+1) ) { + *res = 0; + break; + } + } + + igraph_vector_destroy(&sorted_deg2); + igraph_vector_destroy(&sorted_deg1); + IGRAPH_FINALLY_CLEAN(2); + + return IGRAPH_SUCCESS; +} + + +/***** Legacy functions *****/ + +#define SUCCEED { \ + if (res) { \ + *res = 1; \ + } \ + return IGRAPH_SUCCESS; \ + } + +#define FAIL { \ + if (res) { \ + *res = 0; \ + } \ + return IGRAPH_SUCCESS; \ + } + +/** + * \function igraph_is_degree_sequence + * \brief Determines whether a degree sequence is valid. + * + * \deprecated-by igraph_is_graphical 0.9 + * + * + * 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. + * + * + * 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) { + IGRAPH_WARNING("igraph_is_degree_sequence is deprecated, use igraph_is_graphical."); + + /* 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; +} + +#undef SUCCEED +#undef FAIL + + +/** + * \function igraph_is_graphical_degree_sequence + * \brief Determines whether a sequence of integers can be the degree sequence of some simple graph. + * + * \deprecated-by igraph_is_graphical 0.9 + * + * + * References: + * + * + * 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. + * + * + * PL Erdős, I Miklós 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. + * https://dx.doi.org/10.1017/S0963548317000499 + * + * + * Z Kiraly: Recognizing graphic degree sequences and generating all + * realizations. TR-2011-11, Egervary Research Group, H-1117, Budapest, + * Hungary. ISSN 1587-4451, 2012. + * https://www.cs.elte.hu/egres/tr/egres-11-11.pdf + * + * \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_WARNING("igraph_is_graphical_degree_sequence is deprecated, use igraph_is_graphical."); + return igraph_is_graphical(out_degrees, in_degrees, IGRAPH_SIMPLE_SW, res); +} diff --git a/src/rigraph/core/misc/matching.c b/src/rigraph/core/misc/matching.c new file mode 100644 index 0000000..7e58bd0 --- /dev/null +++ b/src/rigraph/core/misc/matching.c @@ -0,0 +1,1033 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + 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 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public 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_matching.h" + +#include "igraph_adjlist.h" +#include "igraph_constructors.h" +#include "igraph_conversion.h" +#include "igraph_dqueue.h" +#include "igraph_interface.h" +#include "igraph_structural.h" + +#include + +/* #define MATCHING_DEBUG */ + +#ifdef _MSC_VER +/* MSVC does not support variadic macros */ +#include +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; +} + +static 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); +static 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. + * + * + * 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. + * + * + * 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. + * + * + * 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; + } +} + +static 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 + */ +static 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; +} + +static 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. + */ +static 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_UNUSED(dual); /* We mark it as unused to prevent warnings about unused-but-set-variables. */ + 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_LOOPS_TWICE)); + 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; + + /* mark min_slack_u as unused; it is actually used when debugging, but + * gcc complains when we are not debugging */ + IGRAPH_UNUSED(min_slack_u); + + /* (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/src/rigraph/core/misc/microscopic_update.c b/src/rigraph/core/misc/microscopic_update.c new file mode 100644 index 0000000..80fc7c2 --- /dev/null +++ b/src/rigraph/core/misc/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 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public 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_microscopic_update.h" + +#include "igraph_iterators.h" +#include "igraph_interface.h" +#include "igraph_random.h" +#include "igraph_error.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 initialized 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. + */ + +static int igraph_i_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_CHECK(igraph_vector_resize(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 A and es from the finally stack -- that's three items */ + IGRAPH_FINALLY_CLEAN(2); + + 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 initialized 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. + */ + +static int igraph_i_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_CHECK(igraph_vector_resize(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 A and vs from the finally stack -- that's two items */ + IGRAPH_FINALLY_CLEAN(2); + + 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 + */ + +static int igraph_i_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(°v, 1); + IGRAPH_CHECK(igraph_degree(graph, °v, igraph_vss_1(vid), + mode, IGRAPH_NO_LOOPS)); + if (VECTOR(degv)[0] < 1) { + *updates = 0; + } + igraph_vector_destroy(°v); + 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_i_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. + * + * + * 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. + * + * + * 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 + */ + +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_i_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); + } + + IGRAPH_VECTOR_INIT_FINALLY(&V, 0); + + /* Cumulative proportionate quantities. We are using the global */ + /* perspective, hence islocal = 0, vid = -1 and mode = IGRAPH_ALL. */ + IGRAPH_CHECK(igraph_i_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(°, 1); + while (!IGRAPH_VIT_END(vA)) { + u = (igraph_integer_t)IGRAPH_VIT_GET(vA); + IGRAPH_CHECK(igraph_degree(graph, °, 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. */ + IGRAPH_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_CHECK(igraph_i_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; + } + IGRAPH_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. */ + IGRAPH_ASSERT(b >= 0); + VECTOR(*quantities)[b] = VECTOR(*quantities)[a]; + VECTOR(*strategies)[b] = VECTOR(*strategies)[a]; + + igraph_eit_destroy(&eA); + igraph_es_destroy(&es); + igraph_vector_destroy(°); + igraph_vit_destroy(&vA); + igraph_vs_destroy(&vs); + igraph_vector_destroy(&V); + 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. + * + * + * Reference: + * \clist + * \cli (Yu & 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_i_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_VECTOR_INIT_FINALLY(&V, 0); + + IGRAPH_CHECK(igraph_i_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_i_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/src/rigraph/core/misc/mixing.c b/src/rigraph/core/misc/mixing.c new file mode 100644 index 0000000..d6da467 --- /dev/null +++ b/src/rigraph/core/misc/mixing.c @@ -0,0 +1,307 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2009-2012 Gabor Csardi + 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. + * + * 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 (no_of_nodes == 0) { + *res = IGRAPH_NAN; + return IGRAPH_SUCCESS; + } + + /* 'types' length > 0 here, safe to call vector_min() */ + 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. + * + * 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(°ree, 0); + igraph_degree(graph, °ree, igraph_vss_all(), IGRAPH_ALL, /*loops=*/ 1); + igraph_vector_add_constant(°ree, -1); + igraph_assortativity(graph, °ree, 0, res, /*directed=*/ 0); + igraph_vector_destroy(°ree); + } + + return 0; +} diff --git a/src/rigraph/core/misc/motifs.c b/src/rigraph/core/misc/motifs.c new file mode 100644 index 0000000..5b96f76 --- /dev/null +++ b/src/rigraph/core/misc/motifs.c @@ -0,0 +1,1218 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2006-2012 Gabor Csardi + 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_interface.h" +#include "igraph_nongraph.h" +#include "igraph_stack.h" + +#include "core/interruption.h" +#include "isomorphism/isoclasses.h" +#include "graph/neighbors.h" + +/** + * Callback function for igraph_motifs_randesu that counts the motifs by + * isomorphism class in a histogram. + */ +static 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. + * + * + * Motifs are small weakly connected induced subgraphs of a given structure in a + * graph. It is argued that the motif profile (i.e. 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. + * + * + * This function is able to find directed motifs of sizes three + * and four and undirected motifs of sizes three to six + * (i.e. the number of different subgraphs with three to six + * vertices in the network). + * + * + * 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 + * \p 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. + * https://doi.org/10.1093/bioinformatics/btl038 + * + * + * Set the \p cut_prob argument to a zero vector for finding all + * motifs. + * + * + * 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. For directed graphs, + * only 3 and 4 are implemented, for undirected, 3 to 6. + * 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 \p 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 \p 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 (directed) or 6 (undirected) 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) { + igraph_bool_t directed = igraph_is_directed(graph); + int histlen; + + if (directed) { + switch (size) { + case 3: + histlen = 16; + break; + case 4: + histlen = 218; + break; + default: + IGRAPH_ERROR("In directed graphs, only 3 and 4 vertex motifs are supported.", + IGRAPH_UNIMPLEMENTED); + } + } else { + switch (size) { + case 3: + histlen = 4; + break; + case 4: + histlen = 11; + break; + case 5: + histlen = 34; + break; + case 6: + histlen = 156; + break; + default: + IGRAPH_ERROR("In undirected graphs, only 3 to 6 vertex motifs are supported.", + IGRAPH_UNIMPLEMENTED); + } + } + + if (igraph_vector_size(cut_prob) != size) { + IGRAPH_ERRORF("Cut probability vector size (%ld) must agree with motif size (%" IGRAPH_PRId ").", + IGRAPH_EINVAL, igraph_vector_size(cut_prob), size); + } + + 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 (directed) { + 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 (directed) { + 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; + } + } else if (size == 5) { + /* undirected only */ + int not_connected[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 19 }; + int i, n = sizeof(not_connected) / sizeof(int); + for (i = 0; i < n; i++) { + VECTOR(*hist)[not_connected[i]] = IGRAPH_NAN; + } + } else if (size == 6) { + /* undirected only */ + int not_connected[] = {0, 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, 35, 38, + 44, 50, 51, 54, 74, 77, 89, 120}; + int i, n = sizeof(not_connected) / sizeof(int); + for (i = 0; i < n; i++) { + VECTOR(*hist)[not_connected[i]] = IGRAPH_NAN; + } + } + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_motifs_randesu_callback + * \brief Finds motifs in a graph and calls a function for each of them. + * + * + * Similarly to \ref igraph_motifs_randesu(), this function is able to find + * directed motifs of sizes three and four and undirected motifs of sizes + * three to six (i.e. the number of different subgraphs with three to six + * 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. + * + * + * The \p cut_prob argument also allows sampling the motifs, just like for + * \ref igraph_motifs_randesu(). Set the \p 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_UNUSED(motifs); /* We mark it as unused to prevent warnings about unused-but-set-variables. */ + + 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; + + const unsigned int *arr_idx, *arr_code; + unsigned int code = 0; + unsigned int mul, idx; + + igraph_bool_t terminate = 0; + + if (igraph_is_directed(graph)) { + switch (size) { + case 3: + arr_idx = igraph_i_isoclass_3_idx; + arr_code = igraph_i_isoclass2_3; + mul = 3; + break; + case 4: + arr_idx = igraph_i_isoclass_4_idx; + arr_code = igraph_i_isoclass2_4; + mul = 4; + break; + default: + IGRAPH_ERROR("In directed graphs, only 3 and 4 vertex motifs are supported.", + IGRAPH_UNIMPLEMENTED); + } + } else { + switch (size) { + case 3: + arr_idx = igraph_i_isoclass_3u_idx; + arr_code = igraph_i_isoclass2_3u; + mul = 3; + break; + case 4: + arr_idx = igraph_i_isoclass_4u_idx; + arr_code = igraph_i_isoclass2_4u; + mul = 4; + break; + case 5: + arr_idx = igraph_i_isoclass_5u_idx; + arr_code = igraph_i_isoclass2_5u; + mul = 5; + break; + case 6: + arr_idx = igraph_i_isoclass_6u_idx; + arr_code = igraph_i_isoclass2_6u; + mul = 6; + break; + default: + IGRAPH_ERROR("In undirected graphs, only 3 to 6 vertex motifs are supported.", + IGRAPH_UNIMPLEMENTED); + } + } + + if (igraph_vector_size(cut_prob) != size) { + IGRAPH_ERRORF("Cut probability vector size (%ld) must agree with motif size (%" IGRAPH_PRId ").", + IGRAPH_EINVAL, igraph_vector_size(cut_prob), size); + } + + 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_LOOPS_TWICE, IGRAPH_MULTIPLE)); + IGRAPH_FINALLY(igraph_adjlist_destroy, &allneis); + IGRAPH_CHECK(igraph_adjlist_init(graph, &alloutneis, IGRAPH_OUT, IGRAPH_LOOPS_ONCE, IGRAPH_MULTIPLE)); + 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 IGRAPH_SUCCESS; +} + +/** + * \function igraph_motifs_randesu_estimate + * \brief Estimate the total number of motifs in a graph. + * + * This function estimates the total number of weakly connected induced + * subgraphs, called motifs, of a fixed number of vertices. For + * example, an undirected complete graph on \c n vertices + * will have one motif of size \c n, and \c n motifs + * of \p size n - 1. As another example, one triangle + * and a separate vertex will have zero motifs of size four. + * + * + * This function is useful for large graphs for which it is not + * feasible to count all the different motifs, because there are very + * many of them. + * + * + * 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 \p cut_prob parameter which gives the + * probabilities to cut a branch of the search tree.) + * + * + * 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 motifs 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 \p 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 \p 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 \p 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; + + if (size < 3) { + IGRAPH_ERRORF("Motif size must be at least 3, received %" IGRAPH_PRId ".", + IGRAPH_EINVAL, (igraph_integer_t) size); + } + + if (igraph_vector_size(cut_prob) != size) { + IGRAPH_ERRORF("Cut probability vector size (%ld) must agree with motif size (%" IGRAPH_PRId ").", + IGRAPH_EINVAL, igraph_vector_size(cut_prob), size); + } + + if (parsample && igraph_vector_size(parsample) != 0) { + igraph_real_t min, max; + igraph_vector_minmax(parsample, &min, &max); + if (min < 0 || max >= no_of_nodes) { + IGRAPH_ERROR("Sample vertex id out of range.", IGRAPH_EINVAL); + } + } + + if (no_of_nodes == 0) { + *est = 0; + return IGRAPH_SUCCESS; + } + + 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 == NULL) { + sample = IGRAPH_CALLOC(1, igraph_vector_t); + if (sample == NULL) { + 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. + * + * + * This function counts the total number of motifs in a graph, + * i.e. the number of of (weakly) connected triplets or quadruplets, + * without assigning isomorphism classes to them. + * + * \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; + + if (size < 3) { + IGRAPH_ERRORF("Motif size must be at least 3, received %" IGRAPH_PRId ".", + IGRAPH_EINVAL, (igraph_integer_t) size); + } + + if (igraph_vector_size(cut_prob) != size) { + IGRAPH_ERRORF("Cut probability vector size (%ld) must agree with motif size (%" IGRAPH_PRId ").", + IGRAPH_EINVAL, igraph_vector_size(cut_prob), size); + } + 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 IGRAPH_SUCCESS; +} + +/** + * \function igraph_dyad_census + * \brief Calculating the dyad census as defined by Holland and Leinhardt. + * + * + * Dyad census means classifying each pair of vertices of a directed + * graph into three categories: mutual (there is at least one edge from + * \c a to \c b and also from \c b to \c a); asymmetric (there is at least + * one 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 in either direction). + * + * + * 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. For an undirected graph, there are no + * asymmetric connections. + * \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; + + IGRAPH_VECTOR_INIT_FINALLY(&inneis, 0); + IGRAPH_VECTOR_INIT_FINALLY(&outneis, 0); + + for (i = 0; i < vc; i++) { + long int ideg, odeg; + long int ip, op; + + IGRAPH_CHECK(igraph_i_neighbors(graph, &inneis, i, IGRAPH_IN, IGRAPH_NO_LOOPS, IGRAPH_NO_MULTIPLE)); + IGRAPH_CHECK(igraph_i_neighbors(graph, &outneis, i, IGRAPH_OUT, IGRAPH_NO_LOOPS, IGRAPH_NO_MULTIPLE)); + + ideg = igraph_vector_size(&inneis); + odeg = igraph_vector_size(&outneis); + + ip = op = 0; + while (ip < ideg && op < odeg) { + 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 += (ideg - ip) + (odeg - 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 && vc > 2) { + IGRAPH_WARNING("Integer overflow, returning -1."); + *null = -1; + } else { + *null = *null - (*mut) - (*asym); + } + + return IGRAPH_SUCCESS; +} + +/** + * \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_LOOPS_TWICE, IGRAPH_MULTIPLE)); + 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 + * + * + * 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<->B, C, a graph with a mutual connection between two vertices. + * \cli 021D + * A<-B->C, the binary out-tree. + * \cli 021U + * A->B<-C, the binary in-tree. + * \cli 021C + * A->B->C, the directed line. + * \cli 111D + * A<->B<-C. + * \cli 111U + * A<->B->C. + * \cli 030T + * A->B<-C, A->C. + * \cli 030C + * A<-B<-C, A->C. + * \cli 201 + * A<->B<->C. + * \cli 120D + * A<-B->C, A<->C. + * \cli 120U + * A->B<-C, A<->C. + * \cli 120C + * A->B->C, A<->C. + * \cli 210 + * A->B<->C, A<->C. + * \cli 300 + * A<->B<->C, A<->C, the complete graph. + * \endclist + * + * + * 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. + * + * + * 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/src/rigraph/core/misc/other.c b/src/rigraph/core/misc/other.c new file mode 100644 index 0000000..45dbcbb --- /dev/null +++ b/src/rigraph/core/misc/other.c @@ -0,0 +1,434 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2005-2012 Gabor Csardi + 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_random.h" +#include "igraph_types.h" + +#include "core/interruption.h" +#include "plfit/plfit_error.h" +#include "plfit/plfit.h" + +#include + +/** + * \ingroup nongraph + * \function igraph_running_mean + * \brief Calculates the running mean of a vector. + * + * + * 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_ERRORF("Data vector length (%ld) smaller than bin width (%" IGRAPH_PRId ").", IGRAPH_EINVAL, igraph_vector_size(data), binwidth); + } + if (binwidth < 1) { + IGRAPH_ERRORF("Bin width for running mean should be at least 1, got %" IGRAPH_PRId ".", IGRAPH_EINVAL, binwidth); + } + + /* 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 IGRAPH_SUCCESS; +} + + +/** + * \ingroup nongraph + * \function igraph_convex_hull + * \brief Determines the convex hull of a given set of points in the 2D plane + * + * + * The convex hull is determined by the Graham scan algorithm. + * See the following reference for details: + * + * + * 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= 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_UNUSED(file); + IGRAPH_UNUSED(line); + IGRAPH_UNUSED(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-alpha, 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. + * + * + * 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: + * + * + * 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 algorithm. 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; + } + } + } + + RNG_BEGIN(); + + plfit_stored_error_handler = plfit_set_error_handler(igraph_i_plfit_error_handler_store); + if (discrete) { + plfit_discrete_options_init(&disc_options); + /* TODO: approximation method should be switched to PLFIT_P_VALUE_EXACT in igraph 0.9 */ + disc_options.p_value_method = PLFIT_P_VALUE_APPROXIMATE; + 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); + /* TODO: approximation method should be switched to PLFIT_P_VALUE_EXACT in igraph 0.9 */ + cont_options.p_value_method = PLFIT_P_VALUE_APPROXIMATE; + /* TODO: xmin method should be switched to PLFIT_STRATIFIED_SAMPLING in igraph 0.9 */ + cont_options.xmin_method = PLFIT_GSS_OR_LINEAR; + 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); + + RNG_END(); + + 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; +} diff --git a/src/rigraph/core/misc/scan.c b/src/rigraph/core/misc/scan.c new file mode 100644 index 0000000..f9d1a3b --- /dev/null +++ b/src/rigraph/core/misc/scan.c @@ -0,0 +1,879 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2013 Gabor Csardi + 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_adjlist.h" +#include "igraph_arpack.h" +#include "igraph_centrality.h" +#include "igraph_dqueue.h" +#include "igraph_eigen.h" +#include "igraph_interface.h" +#include "igraph_memory.h" +#include "igraph_operators.h" +#include "igraph_stack.h" +#include "igraph_structural.h" + +#include "core/interruption.h" +#include "properties/properties_internal.h" + +/** + * \section about_local_scan + * + * + * 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. + * + */ + +/** + * \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; +} + +/* This removes loop, multiple edges and edges that point + "backwards" according to the rank vector. It works on + edge lists */ + +static 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 */ + +static 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_LOOPS)); + 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); + if (nei == node) { + break; + } + 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; +} + +static 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_LOOPS_TWICE)); + 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; +} + +static 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 = °ree; /* 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(°ree, no_of_nodes); + + IGRAPH_CHECK(igraph_degree(graph, °ree, igraph_vss_all(), IGRAPH_ALL, + IGRAPH_LOOPS)); + maxdegree = (long int) igraph_vector_max(°ree) + 1; + igraph_vector_order1_int(°ree, &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_LOOPS_TWICE)); + 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(°ree); + 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 { + return igraph_local_scan_k_ecount(graph, 1, res, weights, mode); + } + } + + return 0; +} + +static 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_CHECK(igraph_adjlist_init( + us, &adj_us, mode, IGRAPH_NO_LOOPS, IGRAPH_NO_MULTIPLE + )); + IGRAPH_FINALLY(igraph_adjlist_destroy, &adj_us); + IGRAPH_CHECK(igraph_inclist_init(them, &incs_them, mode, IGRAPH_LOOPS)); + IGRAPH_FINALLY(igraph_inclist_destroy, &incs_them); + + IGRAPH_CHECK(igraph_vector_int_init(&neis, no_of_nodes)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &neis); + + 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 *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 + * \brief Sum the number of edges or the weights in k-neighborhood of every vertex. + * + * \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_ERRORF("The weight vector length (%ld) in k-scan should equal " + "the number of edges of the graph (%d).", + IGRAPH_EINVAL, igraph_vector_size(weights), + igraph_ecount(graph)); + } + + if (k == 0) { + return igraph_local_scan_0(graph, res, weights, mode); + } + if (k == 1 && igraph_is_directed(graph)) { + 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_LOOPS)); + 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 IGRAPH_SUCCESS; +} + +/** + * \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_LOOPS)); + IGRAPH_FINALLY(igraph_inclist_destroy, &incs_us); + IGRAPH_CHECK(igraph_inclist_init(them, &incs_them, mode, IGRAPH_LOOPS)); + 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 igraph_vector_int_t + * 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_LOOPS_ONCE)); + 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/src/rigraph/core/misc/sir.c b/src/rigraph/core/misc/sir.c new file mode 100644 index 0000000..4adb12a --- /dev/null +++ b/src/rigraph/core/misc/sir.c @@ -0,0 +1,262 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2014 Gabor Csardi + 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" + +#include "core/interruption.h" + +int igraph_sir_init(igraph_sir_t *sir) { + IGRAPH_CHECK(igraph_vector_init(&sir->times, 1)); + IGRAPH_FINALLY(igraph_vector_destroy, &sir->times); + IGRAPH_CHECK(igraph_vector_int_init(&sir->no_s, 1)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &sir->no_s); + IGRAPH_CHECK(igraph_vector_int_init(&sir->no_i, 1)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &sir->no_i); + IGRAPH_CHECK(igraph_vector_int_init(&sir->no_r, 1)); + IGRAPH_FINALLY_CLEAN(3); + return 0; +} + +/** + * \function igraph_sir_destroy + * \brief Deallocates 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); +} + +static void igraph_i_sir_destroy(igraph_vector_ptr_t *v) { + int i, n = igraph_vector_ptr_size(v); + for (i = 0; i < n; i++) { + if ( VECTOR(*v)[i] ) { + igraph_sir_destroy( VECTOR(*v)[i]) ; + IGRAPH_FREE( VECTOR(*v)[i] ); /* this also sets the vector_ptr element to NULL */ + } + } +} + +#define S_S 0 +#define S_I 1 +#define S_R 2 + +/** + * \function igraph_sir + * \brief Performs 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. + * + * + * 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 + * using \ref igraph_vector_ptr_destroy_all(). + * \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("The infection rate beta must be non-negative in SIR model.", IGRAPH_EINVAL); + } + /* With a recovery rate of zero, the simulation would never stop. */ + if (gamma <= 0) { + IGRAPH_ERROR("The recovery rate gamma must be positive in SIR model.", IGRAPH_EINVAL); + } + if (no_sim <= 0) { + IGRAPH_ERROR("Number of SIR simulations must be positive.", IGRAPH_EINVAL); + } + + IGRAPH_CHECK(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_LOOPS_TWICE, IGRAPH_MULTIPLE)); + 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_CHECK(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_CHECK(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_CHECK(igraph_psumtree_update(&tree, nei, beta)); + } + + while (ni > 0) { + igraph_real_t tt; + igraph_real_t r; + long int vchange; + + IGRAPH_ALLOW_INTERRUPTION(); + + 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_CHECK(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_CHECK(igraph_psumtree_update(&tree, nei, rate - beta)); + } + } + + } else { /* S_S */ + VECTOR(status)[vchange] = S_I; + ns--; ni++; + IGRAPH_CHECK(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_CHECK(igraph_psumtree_update(&tree, nei, rate + beta)); + } + } + } + + IGRAPH_CHECK(igraph_vector_push_back(times_v, tt + igraph_vector_tail(times_v))); + IGRAPH_CHECK(igraph_vector_int_push_back(no_s_v, ns)); + IGRAPH_CHECK(igraph_vector_int_push_back(no_i_v, ni)); + IGRAPH_CHECK(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 IGRAPH_SUCCESS; +} diff --git a/src/rigraph/core/misc/spanning_trees.c b/src/rigraph/core/misc/spanning_trees.c new file mode 100644 index 0000000..1b2fa25 --- /dev/null +++ b/src/rigraph/core/misc/spanning_trees.c @@ -0,0 +1,513 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2011 Gabor Csardi + 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_adjlist.h" +#include "igraph_components.h" +#include "igraph_dqueue.h" +#include "igraph_interface.h" +#include "igraph_memory.h" +#include "igraph_operators.h" +#include "igraph_progress.h" +#include "igraph_random.h" +#include "igraph_structural.h" + +#include "core/indheap.h" +#include "core/interruption.h" + +static int igraph_i_minimum_spanning_tree_unweighted(const igraph_t *graph, + igraph_vector_t *result); +static 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. + * + * + * 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. + * + * + * Directed graphs are considered as undirected for this computation. + * + * + * 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. + * + * + * 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. + * + * + * Directed graphs are considered as undirected for this computation. + * + * + * 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. + * + * + * 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. + * + * + * If the graph has more than one minimum spanning tree, the current + * implementation returns always the same one. + * + * + * Directed graphs are considered as undirected for this computation. + * + * + * 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; +} + + +static 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 tmp_size; + long int act_node = (long int) igraph_dqueue_pop(&q); + IGRAPH_CHECK(igraph_incident(graph, &tmp, (igraph_integer_t) act_node, + IGRAPH_ALL)); + tmp_size = igraph_vector_size(&tmp); + for (j = 0; j < tmp_size; j++) { + long int edge = (long int) VECTOR(tmp)[j]; + if (added_edges[edge] == 0) { + igraph_integer_t to = IGRAPH_OTHER(graph, edge, act_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)); + 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; +} + +static 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++) { + long int adj_size; + 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); + adj_size = igraph_vector_size(&adj); + for (j = 0; j < adj_size; j++) { + igraph_integer_t edgeno = (long int) VECTOR(adj)[j]; + igraph_integer_t neighbor = IGRAPH_OTHER(graph, edgeno, i); + if (already_added[(long int) 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_d_indheap_max_index(&heap, &from, &edge); + + /* Erase it */ + igraph_d_indheap_delete_max(&heap); + + /* Is this edge already included? */ + if (added_edges[edge] == 0) { + igraph_integer_t to = IGRAPH_OTHER(graph, edge, from); + + /* 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); + adj_size = igraph_vector_size(&adj); + for (j = 0; j < adj_size; j++) { + long int edgeno = (long int) VECTOR(adj)[j]; + long int neighbor = IGRAPH_OTHER(graph, edgeno, to); + 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. + * + * + * Multi-graphs are supported, and edge multiplicities will affect the sampling + * frequency. For example, consider the 3-cycle graph 1=2-3-1, with two edges + * between vertices 1 and 2. Due to these parallel edges, the trees 1-2-3 + * and 3-1-2 will be sampled with multiplicity 2, while the tree + * 2-3-1 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_LOOPS_TWICE)); + 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/src/rigraph/core/operators/add_edge.c b/src/rigraph/core/operators/add_edge.c new file mode 100644 index 0000000..2f5bbce --- /dev/null +++ b/src/rigraph/core/operators/add_edge.c @@ -0,0 +1,64 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2005-2020 The igraph development 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., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_operators.h" + +#include "igraph_interface.h" + +/** + * \function igraph_add_edge + * \brief Adds a single edge to a graph. + * + * + * For directed graphs the edge points from \p from to \p to. + * + * + * 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; +} diff --git a/src/rigraph/core/operators/complementer.c b/src/rigraph/core/operators/complementer.c new file mode 100644 index 0000000..fe71188 --- /dev/null +++ b/src/rigraph/core/operators/complementer.c @@ -0,0 +1,104 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2006-2020 The igraph development 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., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_operators.h" + +#include "igraph_constructors.h" +#include "igraph_interface.h" + +#include "graph/attributes.h" +#include "core/interruption.h" + +/** + * \function igraph_complementer + * \brief Create the complementer of a graph + * + * 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; +} diff --git a/src/rigraph/core/operators/compose.c b/src/rigraph/core/operators/compose.c new file mode 100644 index 0000000..eada9af --- /dev/null +++ b/src/rigraph/core/operators/compose.c @@ -0,0 +1,135 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2006-2020 The igraph development 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., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_operators.h" + +#include "igraph_constructors.h" +#include "igraph_interface.h" + +#include "core/interruption.h" + +/** + * \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. + * + * This is of course exactly the composition of two + * binary relations. + * + * 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/src/rigraph/core/operators/connect_neighborhood.c b/src/rigraph/core/operators/connect_neighborhood.c new file mode 100644 index 0000000..1ab6f2a --- /dev/null +++ b/src/rigraph/core/operators/connect_neighborhood.c @@ -0,0 +1,163 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2005-2021 The igraph development 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., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_operators.h" + +#include "igraph_dqueue.h" +#include "igraph_interface.h" +#include "igraph_memory.h" + +/** + * \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. + * + * 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. + * + * 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; +} diff --git a/src/rigraph/core/operators/contract.c b/src/rigraph/core/operators/contract.c new file mode 100644 index 0000000..af2fbe2 --- /dev/null +++ b/src/rigraph/core/operators/contract.c @@ -0,0 +1,167 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2006-2021 The igraph development 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., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_operators.h" + +#include "igraph_constructors.h" +#include "igraph_interface.h" +#include "igraph_memory.h" + +#include "graph/attributes.h" + +static 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); +} + +/** + * \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. + * \c NULL means that vertex attributes are not kept + * after the contraction (not even for unaffected + * vertices). 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; +} diff --git a/src/rigraph/core/operators/difference.c b/src/rigraph/core/operators/difference.c new file mode 100644 index 0000000..c229068 --- /dev/null +++ b/src/rigraph/core/operators/difference.c @@ -0,0 +1,183 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2006-2020 The igraph development 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., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_operators.h" + +#include "igraph_adjlist.h" +#include "igraph_constructors.h" +#include "igraph_interface.h" + +#include "graph/attributes.h" +#include "core/interruption.h" + +/** + * \function igraph_difference + * \brief Calculate the difference of two graphs + * + * + * The number of vertices in the result is the number of vertices in + * the original graph, i.e. the left, first operand. In the results + * graph only edges will be included from \p orig which are not + * present in \p 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_LOOPS_ONCE)); + IGRAPH_FINALLY(igraph_inclist_destroy, &inc_orig); + IGRAPH_CHECK(igraph_inclist_init(sub, &inc_sub, IGRAPH_OUT, IGRAPH_LOOPS_ONCE)); + 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--; + /* handle loop edges properly in undirected graphs */ + if (!directed && i == 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)); + + /* handle loop edges properly in undirected graphs */ + if (!directed && v1 == i) { + n1--; + } + } + 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)); + + /* handle loop edges properly in undirected graphs */ + if (!directed && v1 == i) { + n1--; + } + } + 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; +} diff --git a/src/rigraph/core/operators/disjoint_union.c b/src/rigraph/core/operators/disjoint_union.c new file mode 100644 index 0000000..0ea11a9 --- /dev/null +++ b/src/rigraph/core/operators/disjoint_union.c @@ -0,0 +1,176 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2006-2020 The igraph development 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., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_operators.h" + +#include "igraph_constructors.h" +#include "igraph_interface.h" + +#include "operators/misc_internal.h" + +/** + * \function igraph_disjoint_union + * \brief Creates the union of two disjoint graphs + * + * + * 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. + * + * + * Both graphs need to have the same directedness, i.e. either both + * directed or both undirected. + * + * + * 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. + * + * + * 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. + * + * + * All graphs need to have the same directedness, i.e. either all + * directed or all undirected. If the graph list has length zero, + * the result will be a \em directed graph with no vertices. + * + * + * 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; +} diff --git a/src/rigraph/core/operators/intersection.c b/src/rigraph/core/operators/intersection.c new file mode 100644 index 0000000..610c3cd --- /dev/null +++ b/src/rigraph/core/operators/intersection.c @@ -0,0 +1,296 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2006-2020 The igraph development 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., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_operators.h" + +#include "igraph_constructors.h" +#include "igraph_conversion.h" +#include "igraph_interface.h" +#include "igraph_memory.h" +#include "igraph_qsort.h" + +#include "operators/misc_internal.h" + +#include + +/** + * \function igraph_intersection + * \brief Collect the common edges from two graphs. + * + * + * 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_MERGE_MODE_INTERSECTION, left, right, + edge_map1, edge_map2); +} + +/** + * \function igraph_intersection_many + * \brief The intersection of more than two graphs. + * + * + * This function calculates the intersection of the graphs stored in + * the \p graphs argument. Only those edges will be included in the + * result graph which are part of every graph in \p graphs. + * + * + * 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 (i.e. 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_intersection_destroy_vectors, 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_intersection_destroy_vectors, &edge_vects); + IGRAPH_CHECK(igraph_vector_ptr_init(&order_vects, no_of_graphs)); + IGRAPH_FINALLY(igraph_i_union_intersection_destroy_vector_longs, &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_intersection_destroy_vector_longs(&order_vects); + igraph_i_union_intersection_destroy_vectors(&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; +} diff --git a/src/rigraph/core/operators/misc_internal.c b/src/rigraph/core/operators/misc_internal.c new file mode 100644 index 0000000..d2a4863 --- /dev/null +++ b/src/rigraph/core/operators/misc_internal.c @@ -0,0 +1,259 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2006-2020 The igraph development 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., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "operators/misc_internal.h" + +#include "igraph_constructors.h" +#include "igraph_conversion.h" +#include "igraph_interface.h" +#include "igraph_memory.h" +#include "igraph_qsort.h" + +void igraph_i_union_intersection_destroy_vectors(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_intersection_destroy_vector_longs(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); +} + +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; + } + } +} + +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_MERGE_MODE_UNION: + IGRAPH_CHECK(igraph_vector_resize(edge_map1, no_edges_left)); + break; + case IGRAPH_MERGE_MODE_INTERSECTION: + igraph_vector_clear(edge_map1); + break; + } + } + if (edge_map2) { + switch (mode) { + case IGRAPH_MERGE_MODE_UNION: + IGRAPH_CHECK(igraph_vector_resize(edge_map2, no_edges_right)); + break; + case IGRAPH_MERGE_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_MERGE_MODE_UNION: \ + l = idx1 < no_edges_left || idx2 < no_edges_right; \ + break; \ + case IGRAPH_MERGE_MODE_INTERSECTION: \ + l = idx1 < no_edges_left && idx2 < no_edges_right; \ + break; \ + default: \ + IGRAPH_ASSERT(! "Invalid merge mode."); \ + } + + 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_MERGE_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_MERGE_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_MERGE_MODE_UNION) { + if (edge_map1) { + VECTOR(*edge_map1)[edge1] = eptr; + } + if (edge_map2) { + VECTOR(*edge_map2)[edge2] = eptr; + } + } else if (mode == IGRAPH_MERGE_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; +} diff --git a/src/rigraph/core/operators/misc_internal.h b/src/rigraph/core/operators/misc_internal.h new file mode 100644 index 0000000..2701fcc --- /dev/null +++ b/src/rigraph/core/operators/misc_internal.h @@ -0,0 +1,46 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2020 The igraph development team + 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_MISC_INTERNAL_H +#define IGRAPH_OPERATORS_MISC_INTERNAL_H + +#include "igraph_decls.h" +#include "igraph_datatype.h" +#include "igraph_vector.h" +#include "igraph_vector_ptr.h" + +__BEGIN_DECLS + +#define IGRAPH_MERGE_MODE_UNION 1 +#define IGRAPH_MERGE_MODE_INTERSECTION 2 + +int igraph_i_order_edgelist_cmp(void *edges, const void *e1, const void *e2); +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); +void igraph_i_union_intersection_destroy_vectors(igraph_vector_ptr_t *v); +void igraph_i_union_intersection_destroy_vector_longs(igraph_vector_ptr_t *v); + +__END_DECLS + +#endif diff --git a/src/rigraph/core/operators/permute.c b/src/rigraph/core/operators/permute.c new file mode 100644 index 0000000..9057eb8 --- /dev/null +++ b/src/rigraph/core/operators/permute.c @@ -0,0 +1,96 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2006-2020 The igraph development 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., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_operators.h" + +#include "igraph_constructors.h" +#include "igraph_interface.h" + +#include "graph/attributes.h" + +/** + * \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; +} diff --git a/src/rigraph/core/operators/rewire.c b/src/rigraph/core/operators/rewire.c new file mode 100644 index 0000000..2ac97ee --- /dev/null +++ b/src/rigraph/core/operators/rewire.c @@ -0,0 +1,268 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2005-2021 The igraph development 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., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_operators.h" + +#include "igraph_adjlist.h" +#include "igraph_conversion.h" +#include "igraph_interface.h" +#include "igraph_iterators.h" +#include "igraph_progress.h" +#include "igraph_random.h" +#include "igraph_structural.h" + +#include "core/interruption.h" +#include "operators/rewire_internal.h" + +/* Threshold that defines when to switch over to using adjacency lists during + * rewiring */ +#define REWIRE_ADJLIST_THRESHOLD 10 + +/* Not declared static so that the testsuite can use it, but not part of the public API. */ +int igraph_i_rewire(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_LOOPS_ONCE, IGRAPH_MULTIPLE)); + 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. + * + * + * 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. + */ +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_i_rewire(graph, n, mode, use_adjlist); +} diff --git a/src/rigraph/core/operators/rewire_edges.c b/src/rigraph/core/operators/rewire_edges.c new file mode 100644 index 0000000..48211e2 --- /dev/null +++ b/src/rigraph/core/operators/rewire_edges.c @@ -0,0 +1,394 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2003-2021 The igraph development 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., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_games.h" + +#include "igraph_conversion.h" +#include "igraph_constructors.h" +#include "igraph_interface.h" +#include "igraph_random.h" + +#include "graph/attributes.h" + +static 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 Rewires 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. + * + * 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 Rewires 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. + * + * Note that this function modifies the input \p graph, + * call \ref igraph_copy() if you want to keep it. + * + * This function can produce multiple edges between two vertices. + * + * \param graph The input graph, this will be rewired, it can be + * directed or undirected. If it is undirected or \p mode is set to + * IGRAPH_ALL, \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 = 0; + 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 = */ 1)); + } + + return 0; +} diff --git a/src/rigraph/core/operators/rewire_internal.h b/src/rigraph/core/operators/rewire_internal.h new file mode 100644 index 0000000..c9e5d7e --- /dev/null +++ b/src/rigraph/core/operators/rewire_internal.h @@ -0,0 +1,8 @@ +#ifndef IGRAPH_OPERATORS_REWIRE_INTERNAL_H +#define IGRAPH_OPERATORS_REWIRE_INTERNAL_H + +#include "igraph_interface.h" + +IGRAPH_PRIVATE_EXPORT int igraph_i_rewire(igraph_t *graph, igraph_integer_t n, igraph_rewiring_t mode, igraph_bool_t use_adjlist); + +#endif diff --git a/src/rigraph/core/operators/simplify.c b/src/rigraph/core/operators/simplify.c new file mode 100644 index 0000000..6924f18 --- /dev/null +++ b/src/rigraph/core/operators/simplify.c @@ -0,0 +1,179 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2006-2020 The igraph development 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., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_operators.h" + +#include "igraph_constructors.h" +#include "igraph_interface.h" + +#include "core/fixed_vectorlist.h" +#include "graph/attributes.h" + +/** + * \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. \c NULL means to + * discard the edge attributes after the operation, even for edges + * that were unaffeccted. 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; +} diff --git a/src/rigraph/core/operators/subgraph.c b/src/rigraph/core/operators/subgraph.c new file mode 100644 index 0000000..f798f5d --- /dev/null +++ b/src/rigraph/core/operators/subgraph.c @@ -0,0 +1,477 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2006-2021 The igraph development 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., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_operators.h" + +#include "igraph_constructors.h" +#include "igraph_interface.h" +#include "igraph_memory.h" + +#include "core/interruption.h" +#include "graph/attributes.h" +#include "operators/subgraph.h" + +/** + * Subgraph creation, old version: it copies the graph and then deletes + * unneeded vertices. + */ +static int igraph_i_induced_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(igraph_free, remain); + 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. + * + * map_is_prepared is an indicator that the caller has already prepared the + * 'map' vector and that this function should not resize or clear it. This + * is used to spare an O(n) operation (where n is the number of vertices in + * the _original_ graph) in cases when induced_subgraph() is repeatedly + * called on the same graph; one example is igraph_decompose(). + */ +static int igraph_i_induced_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 map_is_prepared +) { + 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; + if (!map_is_prepared) { + 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_bool_t skip_loop_edge; + + IGRAPH_CHECK(igraph_incident(graph, &nei_edges, old_vid, IGRAPH_OUT)); + n = igraph_vector_size(&nei_edges); + + if (directed) { + /* directed graph; this is easier */ + 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 { + /* undirected graph. We need to be careful with loop edges as each + * loop edge will appear twice. We use a boolean flag to skip every + * second loop edge */ + skip_loop_edge = 0; + 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; + } + to -= 1; + + if (new_vid == to) { + /* this is a loop edge; check whether we need to skip it */ + skip_loop_edge = !skip_loop_edge; + if (skip_loop_edge) { + continue; + } + } + + IGRAPH_CHECK(igraph_vector_push_back(&new_edges, new_vid)); + IGRAPH_CHECK(igraph_vector_push_back(&new_edges, to)); + 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_induced_subgraph + * \brief Creates a subgraph induced by the specified vertices. + * + * + * 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); +} + +static 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_i_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, + igraph_bool_t map_is_prepared) { + + 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_induced_subgraph_copy_and_delete(graph, res, vids, map, invmap); + + case IGRAPH_SUBGRAPH_CREATE_FROM_SCRATCH: + return igraph_i_induced_subgraph_create_from_scratch(graph, res, vids, map, + invmap, /* map_is_prepared = */ map_is_prepared); + + default: + IGRAPH_ERROR("unknown subgraph implementation type", IGRAPH_EINVAL); + } +} + +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) { + return igraph_i_induced_subgraph_map(graph, res,vids, impl, map, invmap, /* map_is_prepared = */ 0); +} + +/** + * \ingroup structural + * \function igraph_subgraph_edges + * \brief Creates a subgraph with the specified edges and their endpoints. + * + * + * 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); + } + IGRAPH_FINALLY(igraph_free, vremain); + eremain = IGRAPH_CALLOC(no_of_edges, char); + if (eremain == 0) { + IGRAPH_ERROR("subgraph_edges failed", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, eremain); + 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; +} diff --git a/src/rigraph/core/operators/subgraph.h b/src/rigraph/core/operators/subgraph.h new file mode 100644 index 0000000..a27aa8e --- /dev/null +++ b/src/rigraph/core/operators/subgraph.h @@ -0,0 +1,35 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2003-2021 The igraph development 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., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#ifndef IGRAPH_OPERATORS_SUBGRAPH_INTERNAL_H +#define IGRAPH_OPERATORS_SUBGRAPH_INTERNAL_H + +#include "igraph_interface.h" + +IGRAPH_PRIVATE_EXPORT int igraph_i_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, igraph_bool_t map_is_prepared +); + +#endif diff --git a/src/rigraph/core/operators/union.c b/src/rigraph/core/operators/union.c new file mode 100644 index 0000000..d55390d --- /dev/null +++ b/src/rigraph/core/operators/union.c @@ -0,0 +1,261 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2006-2020 The igraph development 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., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include + +#include "igraph_operators.h" + +#include "igraph_constructors.h" +#include "igraph_conversion.h" +#include "igraph_interface.h" +#include "igraph_memory.h" +#include "igraph_qsort.h" + +#include "operators/misc_internal.h" + +/** + * \function igraph_union + * \brief Calculates the union of two graphs. + * + * + * 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_MERGE_MODE_UNION, left, right, + edge_map1, edge_map2); +} + +/** + * \function igraph_union_many + * \brief Creates the union of many graphs. + * + * + * 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. + * + * + * The directedness of the operand graphs must be the same. + * If the graph list has length zero, the result will be a \em directed + * graph with no vertices. + * + * \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_intersection_destroy_vectors, 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_intersection_destroy_vectors, &edge_vects); + IGRAPH_CHECK(igraph_vector_ptr_init(&order_vects, no_of_graphs)); + IGRAPH_FINALLY(igraph_i_union_intersection_destroy_vector_longs, &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_intersection_destroy_vector_longs(&order_vects); + igraph_i_union_intersection_destroy_vectors(&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; +} diff --git a/src/rigraph/core/paths/all_shortest_paths.c b/src/rigraph/core/paths/all_shortest_paths.c new file mode 100644 index 0000000..0bf5f78 --- /dev/null +++ b/src/rigraph/core/paths/all_shortest_paths.c @@ -0,0 +1,318 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2005-2021 The igraph development 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., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_paths.h" + +#include "igraph_adjlist.h" +#include "igraph_dqueue.h" +#include "igraph_interface.h" +#include "igraph_memory.h" + +#include "core/interruption.h" + +#include /* memset */ + +static 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 All shortest paths (geodesics) from a vertex. + * + * When there is more than one shortest path between two vertices, + * all of them will be returned. + * + * \param graph The graph object. + * \param res Pointer to an initialized pointer vector, the result + * will be stored here in \ref 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 \ref igraph_vector_t object or + * \c NULL. If not \c 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. + * + * 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; +} diff --git a/src/rigraph/core/paths/bellman_ford.c b/src/rigraph/core/paths/bellman_ford.c new file mode 100644 index 0000000..9e93f80 --- /dev/null +++ b/src/rigraph/core/paths/bellman_ford.c @@ -0,0 +1,570 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2005-2021 The igraph development 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, see . +*/ + +#include "igraph_paths.h" + +#include "igraph_adjlist.h" +#include "igraph_dqueue.h" +#include "igraph_interface.h" +#include "igraph_memory.h" +#include "igraph_stack.h" + +#include "core/indheap.h" +#include "core/interruption.h" + +#include + +/** + * \function igraph_shortest_paths_bellman_ford + * \brief Weighted shortest path lengths between vertices, allowing negative weights. + * + * This function implements the Bellman-Ford algorithm to find the weighted + * shortest paths to all vertices from a single source, allowing negative weights. + * 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 to The target vertices. It is not allowed to include a + * vertex twice or more. + * \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). Additionally, no edge weight may + * be NaN. If either case does not hold, an error is returned. 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); + } + if (no_of_edges > 0 && igraph_vector_is_any_nan(weights)) { + IGRAPH_ERROR("Weight vector must not contain NaN values", 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_LOOPS)); + IGRAPH_FINALLY(igraph_lazy_inclist_destroy, &inclist); + + all_to = igraph_vs_is_all(&to); + if (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_int_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_int_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; +} + + +/** + * \ingroup structural + * \function igraph_get_shortest_paths_bellman_ford + * \brief Weighted shortest paths from a vertex, allowing negative weights. + * + * This function calculates weighted shortest paths from or to a single vertex, + * and allows negative weights. When there is more than one shortest path between + * two vertices, only one of them is returned. + * + * If there are no negative weights, you are better off with + * \ref igraph_get_shortest_paths_dijkstra() . + * + * \param graph The input graph, can be directed. + * \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 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. + * \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_EINVAL + * The weight vector doesn't math the number of edges. + * \cli IGRAPH_EINVVID + * \p from is invalid vertex id, or the length of \p to is + * not the same as the length of \p vertices or \p edges. + * \cli IGRAPH_ENEGLOOP + * Bellman-ford algorithm encounted a negative loop. + * \endclist + * + * Time complexity: O(|E|*|V|), where |V| is the number of + * vertices, |E| the number of edges. + * + * \sa \ref igraph_shortest_paths() for a faster unweighted version + * or \ref igraph_shortest_paths_dijkstra() if you do not have negative + * edge weights. + */ + +int igraph_get_shortest_paths_bellman_ford(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) { + long int no_of_nodes = igraph_vcount(graph); + long int no_of_edges = igraph_ecount(graph); + long int *parents; + igraph_lazy_inclist_t inclist; + long int i, j, k; + igraph_dqueue_t Q; + igraph_vector_t clean_vertices; + igraph_vector_t num_queued; + igraph_vit_t tovit; + igraph_real_t my_infinity = IGRAPH_INFINITY; + igraph_vector_t dist; + + 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 must match number of edges.", IGRAPH_EINVAL); + } + + 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_LOOPS)); + IGRAPH_FINALLY(igraph_lazy_inclist_destroy, &inclist); + + IGRAPH_CHECK(igraph_vit_create(graph, to, &tovit)); + IGRAPH_FINALLY(igraph_vit_destroy, &tovit); + + if (vertices && IGRAPH_VIT_SIZE(tovit) != igraph_vector_ptr_size(vertices)) { + IGRAPH_ERROR("Size of `vertices' and `to' should match.", IGRAPH_EINVAL); + } + if (edges && IGRAPH_VIT_SIZE(tovit) != igraph_vector_ptr_size(edges)) { + IGRAPH_ERROR("Size of `edges' and `to' should match.", IGRAPH_EINVAL); + } + + parents = IGRAPH_CALLOC(no_of_nodes, long int); + if (parents == 0) { + IGRAPH_ERROR("Insufficient memory for shortest paths with Bellman-Ford.", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, parents); + IGRAPH_VECTOR_INIT_FINALLY(&dist, no_of_nodes); + + igraph_vector_fill(&dist, my_infinity); + VECTOR(dist)[from] = 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_int_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_int_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]; + parents[target] = nei + 1; + if (VECTOR(clean_vertices)[target]) { + VECTOR(clean_vertices)[target] = 0; + IGRAPH_CHECK(igraph_dqueue_push(&Q, target)); + } + } + } + } + + /* 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(tovit), i = 0; !IGRAPH_VIT_END(tovit); IGRAPH_VIT_NEXT(tovit), i++) { + long int node = IGRAPH_VIT_GET(tovit); + 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 && (size > 0 || node == from)) { + 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_vector_destroy(&dist); + IGRAPH_FINALLY_CLEAN(1); + + igraph_vit_destroy(&tovit); + IGRAPH_FINALLY_CLEAN(1); + + IGRAPH_FREE(parents); + igraph_dqueue_destroy(&Q); + igraph_vector_destroy(&clean_vertices); + igraph_vector_destroy(&num_queued); + igraph_lazy_inclist_destroy(&inclist); + IGRAPH_FINALLY_CLEAN(5); + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_get_shortest_path_bellman_ford + * \brief Weighted shortest path from one vertex to another one. + * + * Calculates a single (positively) weighted shortest path from + * a single vertex to another one, using Bellman-Ford algorithm. + * + * + * This function is a special case (and a wrapper) to + * \ref igraph_get_shortest_paths_bellman_ford(). + * + * \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 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 is called. + * \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_bellman_ford() for the version with + * more target vertices. + */ + +int igraph_get_shortest_path_bellman_ford(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 = NULL; + } + if (edges) { + IGRAPH_CHECK(igraph_vector_ptr_init(&edges2, 1)); + IGRAPH_FINALLY(igraph_vector_ptr_destroy, &edges2); + VECTOR(edges2)[0] = edges; + } else { + ep = NULL; + } + + IGRAPH_CHECK(igraph_get_shortest_paths_bellman_ford(graph, vp, ep, + from, igraph_vss_1(to), + weights, mode, NULL, NULL)); + + if (edges) { + igraph_vector_ptr_destroy(&edges2); + IGRAPH_FINALLY_CLEAN(1); + } + if (vertices) { + igraph_vector_ptr_destroy(&vertices2); + IGRAPH_FINALLY_CLEAN(1); + } + + return IGRAPH_SUCCESS; +} diff --git a/src/rigraph/core/paths/dijkstra.c b/src/rigraph/core/paths/dijkstra.c new file mode 100644 index 0000000..0dd7ccb --- /dev/null +++ b/src/rigraph/core/paths/dijkstra.c @@ -0,0 +1,1068 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2005-2021 The igraph development 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., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_paths.h" + +#include "igraph_adjlist.h" +#include "igraph_dqueue.h" +#include "igraph_interface.h" +#include "igraph_memory.h" +#include "igraph_stack.h" + +#include "core/indheap.h" +#include "core/interruption.h" +#include "core/math.h" + +#include /* memset */ + +/** + * \function igraph_shortest_paths_dijkstra + * \brief Weighted shortest path lengths between vertices. + * + * This function implements Dijkstra's algorithm to find the weighted + * shortest path lengths 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. All edge weights must be + * non-negative for Dijkstra's algorithm to work. Additionally, no + * edge weight may be NaN. If either case does not hold, an error + * is returned. 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 (no_of_edges > 0) { + igraph_real_t min = igraph_vector_min(weights); + if (min < 0) { + IGRAPH_ERROR("Weight vector must be non-negative", IGRAPH_EINVAL); + } + else if (igraph_is_nan(min)) { + IGRAPH_ERROR("Weight vector must not contain NaN values", 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_LOOPS)); + IGRAPH_FINALLY(igraph_lazy_inclist_destroy, &inclist); + + all_to = igraph_vs_is_all(&to); + if (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_int_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_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) { + /* 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 Weighted shortest paths from a vertex. + * + * + * 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 The edge weights. All edge weights must be + * non-negative for Dijkstra's algorithm to work. Additionally, no + * edge weight may be NaN. If either case does not hold, an error + * is returned. If this is a null pointer, then the unweighted + * version, \ref igraph_get_shortest_paths() is called. + * \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 (no_of_edges > 0) { + igraph_real_t min = igraph_vector_min(weights); + if (min < 0) { + IGRAPH_ERROR("Weight vector must be non-negative", IGRAPH_EINVAL); + } + else if (igraph_is_nan(min)) { + IGRAPH_ERROR("Weight vector must not contain NaN values", 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_LOOPS)); + 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_int_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_int_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 && (size > 0 || node == from)) { + 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 + * \brief 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. + * + * 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 The edge weights. All edge weights must be + * non-negative for Dijkstra's algorithm to work. Additionally, no + * edge weight may be NaN. If either case does not hold, an error + * is returned. If this is a null pointer, then the unweighted + * version, \ref igraph_get_shortest_paths() is called. + * \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; +} + +/* 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 + */ +static 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 All weighted shortest paths (geodesics) from a vertex. + * + * \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 The edge weights. All edge weights must be + * non-negative for Dijkstra's algorithm to work. Additionally, no + * edge weight may be NaN. If either case does not hold, an error + * is returned. If this is a null pointer, then the unweighted + * version, \ref igraph_get_all_shortest_paths() is called. + * \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; + igraph_finally_func_t *res_item_destructor; + unsigned char *is_target; + long int i, n, to_reach; + int cmp_result; + const double eps = IGRAPH_SHORTEST_PATH_EPSILON; + + 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 (no_of_edges > 0) { + igraph_real_t min = igraph_vector_min(weights); + if (min < 0) { + IGRAPH_ERROR("Weight vector must be non-negative", IGRAPH_EINVAL); + } + else if (igraph_is_nan(min)) { + IGRAPH_ERROR("Weight vector must not contain NaN values", 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_LOOPS)); + 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_int_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_int_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; + + cmp_result = igraph_cmp_epsilon(curdist, altdist, eps); + 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 (cmp_result == 0 /* 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 (cmp_result > 0 /* 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); + res_item_destructor = igraph_vector_ptr_get_item_destructor(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); + */ + IGRAPH_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 path vector's original item destructor */ + igraph_vector_ptr_set_item_destructor(res, res_item_destructor); + + /* 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; +} diff --git a/src/rigraph/core/paths/distances.c b/src/rigraph/core/paths/distances.c new file mode 100644 index 0000000..1e5a91e --- /dev/null +++ b/src/rigraph/core/paths/distances.c @@ -0,0 +1,214 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2011-2012 Gabor Csardi + 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_datatype.h" +#include "igraph_dqueue.h" +#include "igraph_iterators.h" +#include "igraph_vector.h" +#include "igraph_interface.h" +#include "igraph_adjlist.h" + +#include "core/interruption.h" + +static 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 + * \brief 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. + * + * + * 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 + * \brief 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_LOOPS, IGRAPH_MULTIPLE)); + 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/src/rigraph/core/paths/eulerian.c b/src/rigraph/core/paths/eulerian.c new file mode 100644 index 0000000..f1d15e0 --- /dev/null +++ b/src/rigraph/core/paths/eulerian.c @@ -0,0 +1,676 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2005-2020 The igraph development 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., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA +*/ + +#include "igraph_eulerian.h" + +#include "igraph_adjlist.h" +#include "igraph_interface.h" +#include "igraph_components.h" +#include "igraph_stack.h" + +/** + * \section about_eulerian + * + * These functions calculate whether an Eulerian path or cycle exists + * and if so, can find them. + */ + + +/* solution adapted from https://www.geeksforgeeks.org/eulerian-path-and-circuit/ +The function returns one of the following values +has_path is set to 1 if a path exists, 0 otherwise +has_cycle is set to 1 if a cycle exists, 0 otherwise +*/ +static int igraph_i_is_eulerian_undirected(const igraph_t *graph, igraph_bool_t *has_path, igraph_bool_t *has_cycle, igraph_integer_t *start_of_path) { + igraph_integer_t odd; + igraph_vector_t degree, csize; + /* boolean vector to mark singletons: */ + igraph_vector_t nonsingleton; + long int i, n, vsize; + long int cluster_count; + /* number of self-looping singletons: */ + long int es; + /* will be set to 1 if there are non-isolated vertices, otherwise 0: */ + long int ens; + + n = igraph_vcount(graph); + + if (igraph_ecount(graph) == 0 || n <= 1) { + start_of_path = 0; /* in case the graph has one vertex with self-loops */ + *has_path = 1; + *has_cycle = 1; + return IGRAPH_SUCCESS; + } + + /* check for connectedness, but singletons are special since they affect + * the Eulerian nature only if there is a self-loop AND another edge + * somewhere else in the graph */ + IGRAPH_VECTOR_INIT_FINALLY(&csize, 0); + IGRAPH_CHECK(igraph_clusters(graph, NULL, &csize, NULL, IGRAPH_WEAK)); + cluster_count = 0; + vsize = igraph_vector_size(&csize); + for (i = 0; i < vsize; i++) { + if (VECTOR(csize)[i] > 1) { + cluster_count++; + if (cluster_count > 1) { + /* disconnected edges, they'll never reach each other */ + *has_path = 0; + *has_cycle = 0; + igraph_vector_destroy(&csize); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; + } + } + } + + igraph_vector_destroy(&csize); + IGRAPH_FINALLY_CLEAN(1); + + /* the graph is connected except for singletons */ + /* find singletons (including those with self-loops) */ + IGRAPH_VECTOR_INIT_FINALLY(&nonsingleton, 0); + IGRAPH_CHECK(igraph_degree(graph, &nonsingleton, igraph_vss_all(), IGRAPH_ALL, IGRAPH_NO_LOOPS)); + + /* check the degrees for odd/even: + * - >= 2 odd means no cycle (1 odd is impossible) + * - > 2 odd means no path + * plus there are a few corner cases with singletons + */ + IGRAPH_VECTOR_INIT_FINALLY(°ree, 0); + IGRAPH_CHECK(igraph_degree(graph, °ree, igraph_vss_all(), IGRAPH_ALL, IGRAPH_LOOPS)); + odd = 0; + es = 0; + ens = 0; + for (i = 0; i < n; i++) { + long int deg = (long int) VECTOR(degree)[i]; + /* Eulerian is about edges, so skip free vertices */ + if (deg == 0) continue; + + if (!VECTOR(nonsingleton)[i]) { + /* singleton with self loops */ + es++; + } else { + /* at least one non-singleton */ + ens = 1; + /* note: self-loops count for two (in and out) */ + if (deg % 2) odd++; + } + + if (es + ens > 1) { + /* 2+ singletons with self loops or singleton with self-loops and + * 1+ edges in the non-singleton part of the graph. */ + *has_path = 0; + *has_cycle = 0; + igraph_vector_destroy(&nonsingleton); + igraph_vector_destroy(°ree); + IGRAPH_FINALLY_CLEAN(2); + + return IGRAPH_SUCCESS; + } + } + + igraph_vector_destroy(&nonsingleton); + IGRAPH_FINALLY_CLEAN(1); + + /* this is the usual algorithm on the connected part of the graph */ + if (odd > 2) { + *has_path = 0; + *has_cycle = 0; + } else if (odd == 2) { + *has_path = 1; + *has_cycle = 0; + } else { + *has_path = 1; + *has_cycle = 1; + } + + /* set start of path if there is one but there is no cycle */ + /* note: we cannot do this in the previous loop because at that time we are + * not sure yet if a path exists */ + for (i = 0; i < n; i++) { + if ((*has_cycle && ((long int) VECTOR(degree)[i]) > 0) || (!*has_cycle && ((long int) VECTOR(degree)[i]) %2 == 1)) { + *start_of_path = i; + break; + } + } + + igraph_vector_destroy(°ree); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + + +static int igraph_i_is_eulerian_directed(const igraph_t *graph, igraph_bool_t *has_path, igraph_bool_t *has_cycle, igraph_integer_t *start_of_path) { + igraph_integer_t incoming_excess, outgoing_excess, n; + long int i, vsize; + long int cluster_count; + igraph_vector_t out_degree, in_degree, csize; + /* boolean vector to mark singletons: */ + igraph_vector_t nonsingleton; + /* number of self-looping singletons: */ + long int es; + /* will be set to 1 if there are non-isolated vertices, otherwise 0: */ + long int ens; + + n = igraph_vcount(graph); + + if (igraph_ecount(graph) == 0 || n <= 1) { + start_of_path = 0; /* in case the graph has one vertex with self-loops */ + *has_path = 1; + *has_cycle = 1; + return IGRAPH_SUCCESS; + } + + incoming_excess = 0; + outgoing_excess = 0; + + /* check for weak connectedness, but singletons are special since they affect + * the Eulerian nature only if there is a self-loop AND another edge + * somewhere else in the graph */ + IGRAPH_VECTOR_INIT_FINALLY(&csize, 0); + + IGRAPH_CHECK(igraph_clusters(graph, NULL, &csize, NULL, IGRAPH_WEAK)); + cluster_count = 0; + vsize = igraph_vector_size(&csize); + for (i = 0; i < vsize; i++) { + if (VECTOR(csize)[i] > 1) { + cluster_count++; + if (cluster_count > 1) { + /* weakly disconnected edges, they'll never reach each other */ + *has_path = 0; + *has_cycle = 0; + igraph_vector_destroy(&csize); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; + } + } + } + + igraph_vector_destroy(&csize); + IGRAPH_FINALLY_CLEAN(1); + + /* the graph is weakly connected except for singletons */ + /* find the singletons (including those with self-loops) */ + IGRAPH_VECTOR_INIT_FINALLY(&nonsingleton, 0); + IGRAPH_CHECK(igraph_degree(graph, &nonsingleton, igraph_vss_all(), IGRAPH_ALL, IGRAPH_NO_LOOPS)); + + + /* checking if no. of incoming edges == outgoing edges + * plus there are a few corner cases with singletons */ + IGRAPH_VECTOR_INIT_FINALLY(&out_degree, 0); + IGRAPH_CHECK(igraph_degree(graph, &out_degree, igraph_vss_all(), IGRAPH_OUT, IGRAPH_LOOPS)); + + IGRAPH_VECTOR_INIT_FINALLY(&in_degree, 0); + IGRAPH_CHECK(igraph_degree(graph, &in_degree, igraph_vss_all(), IGRAPH_IN, IGRAPH_LOOPS)); + es = 0; + ens = 0; + *start_of_path = -1; + for (i = 0; i < n; i++) { + long int degin = VECTOR(in_degree)[i]; + long int degout = VECTOR(out_degree)[i]; + + /* Eulerian is about edges, so skip free vertices */ + if (degin + degout == 0) continue; + + if (!VECTOR(nonsingleton)[i]) { + /* singleton with self loops */ + es++; + /* if we ever want a path, it has to be this self-loop */ + *start_of_path = i; + } else { + /* at least one non-singleton */ + ens = 1; + } + + if (es + ens > 1) { + /* 2+ singletons with self loops or singleton with self-loops and + * 1+ edges in the non-singleton part of the graph. */ + *has_path = 0; + *has_cycle = 0; + igraph_vector_destroy(&nonsingleton); + igraph_vector_destroy(&in_degree); + igraph_vector_destroy(&out_degree); + IGRAPH_FINALLY_CLEAN(3); + + return IGRAPH_SUCCESS; + } + + /* as long as we have perfect balance, you can start + * anywhere with an edge */ + if (*start_of_path == -1 && incoming_excess == 0 && outgoing_excess == 0) { + *start_of_path = i; + } + + /* same in and out (including self-loops, even in singletons) */ + if (degin == degout) { + continue; + } + + /* non-singleton, in != out */ + if (degin > degout) { + incoming_excess += degin - degout; + } else { + outgoing_excess += degout - degin; + if (outgoing_excess == 1) { + *start_of_path = i; + } + } + + /* too much imbalance, either of the following: + * 1. 1+ vertices have 2+ in/out + * 2. 2+ nodes have 1+ in/out */ + if (incoming_excess > 1 || outgoing_excess > 1) { + *has_path = 0; + *has_cycle = 0; + igraph_vector_destroy(&nonsingleton); + igraph_vector_destroy(&in_degree); + igraph_vector_destroy(&out_degree); + IGRAPH_FINALLY_CLEAN(3); + + return IGRAPH_SUCCESS; + } + } + + *has_path = 1; + /* perfect edge balance -> strong connectivity */ + *has_cycle = (incoming_excess == 0) && (outgoing_excess == 0); + /* either way, the start was set already */ + + igraph_vector_destroy(&nonsingleton); + igraph_vector_destroy(&in_degree); + igraph_vector_destroy(&out_degree); + IGRAPH_FINALLY_CLEAN(3); + + return IGRAPH_SUCCESS; +} + + +/** + * \ingroup Eulerian + * \function igraph_is_eulerian + * \brief Checks whether an Eulerian path or cycle exists + * + * An Eulerian path traverses each edge of the graph precisely once. A closed + * Eulerian path is referred to as an Eulerian cycle. + * + * \param graph The graph object. + * \param has_path Pointer to a Boolean, will be set to true if an Eulerian path exists. + * \param has_cycle Pointer to a Boolean, will be set to true if an Eulerian cycle exists. + * \return Error code: + * \c IGRAPH_ENOMEM, not enough memory for + * temporary data. + * + * Time complexity: O(|V|+|E|), the number of vertices plus the number of edges. + * + */ + +int igraph_is_eulerian(const igraph_t *graph, igraph_bool_t *has_path, igraph_bool_t *has_cycle) { + igraph_integer_t start_of_path = 0; + + if (igraph_is_directed(graph)) { + IGRAPH_CHECK(igraph_i_is_eulerian_directed(graph, has_path, has_cycle, &start_of_path)); + } else { + IGRAPH_CHECK(igraph_i_is_eulerian_undirected(graph, has_path, has_cycle, &start_of_path)); + } + return IGRAPH_SUCCESS; +} + + +static int igraph_i_eulerian_path_undirected(const igraph_t *graph, igraph_vector_t *edge_res, igraph_vector_t *vertex_res, igraph_integer_t start_of_path) { + long int curr; + igraph_integer_t n, m; + igraph_inclist_t il; + igraph_stack_t path, tracker, edge_tracker, edge_path; + igraph_vector_bool_t visited_list; + igraph_vector_t degree; + + n = igraph_vcount(graph); + m = igraph_ecount(graph); + + if (edge_res) { + igraph_vector_clear(edge_res); + } + + if (vertex_res) { + igraph_vector_clear(vertex_res); + } + + if (m == 0 || n == 0) { + return IGRAPH_SUCCESS; + } + + IGRAPH_VECTOR_INIT_FINALLY(°ree, 0); + IGRAPH_CHECK(igraph_degree(graph, °ree, igraph_vss_all(), IGRAPH_ALL, IGRAPH_LOOPS)); + + IGRAPH_CHECK(igraph_stack_init(&path, n)); + IGRAPH_FINALLY(igraph_stack_destroy, &path); + + IGRAPH_CHECK(igraph_stack_init(&tracker, n)); + IGRAPH_FINALLY(igraph_stack_destroy, &tracker); + + IGRAPH_CHECK(igraph_stack_init(&edge_path, n)); + IGRAPH_FINALLY(igraph_stack_destroy, &edge_path); + + IGRAPH_CHECK(igraph_stack_init(&edge_tracker, n)); + IGRAPH_FINALLY(igraph_stack_destroy, &edge_tracker); + + IGRAPH_VECTOR_BOOL_INIT_FINALLY(&visited_list, m); + + IGRAPH_CHECK(igraph_stack_push(&tracker, start_of_path)); + + IGRAPH_CHECK(igraph_inclist_init(graph, &il, IGRAPH_OUT, IGRAPH_LOOPS_ONCE)); + IGRAPH_FINALLY(igraph_inclist_destroy, &il); + + curr = start_of_path; + + while (!igraph_stack_empty(&tracker)) { + + if (VECTOR(degree)[curr] != 0) { + igraph_vector_int_t *incedges; + long nc, edge = -1; + long int j, next; + IGRAPH_CHECK(igraph_stack_push(&tracker, curr)); + + incedges = igraph_inclist_get(&il, curr); + nc = igraph_vector_int_size(incedges); + IGRAPH_ASSERT(nc > 0); + + for (j = 0; j < nc; j++) { + edge = (long) VECTOR(*incedges)[j]; + if (!VECTOR(visited_list)[edge]) { + break; + } + } + + next = IGRAPH_OTHER(graph, edge, curr); + + IGRAPH_CHECK(igraph_stack_push(&edge_tracker, edge)); + + /* remove edge here */ + VECTOR(degree)[curr]--; + VECTOR(degree)[next]--; + VECTOR(visited_list)[edge] = 1; + + curr = next; + } else { /* back track to find remaining circuit */ + igraph_integer_t curr_e; + IGRAPH_CHECK(igraph_stack_push(&path, curr)); + curr = igraph_stack_pop(&tracker); + if (!igraph_stack_empty(&edge_tracker)) { + curr_e = igraph_stack_pop(&edge_tracker); + IGRAPH_CHECK(igraph_stack_push(&edge_path, curr_e)); + } + } + } + + if (edge_res) { + IGRAPH_CHECK(igraph_vector_reserve(edge_res, m)); + while (!igraph_stack_empty(&edge_path)) { + IGRAPH_CHECK(igraph_vector_push_back(edge_res, igraph_stack_pop(&edge_path))); + } + } + if (vertex_res) { + IGRAPH_CHECK(igraph_vector_reserve(vertex_res, m+1)); + while (!igraph_stack_empty(&path)) { + IGRAPH_CHECK(igraph_vector_push_back(vertex_res, igraph_stack_pop(&path))); + } + } + + igraph_stack_destroy(&path); + igraph_stack_destroy(&tracker); + igraph_stack_destroy(&edge_path); + igraph_stack_destroy(&edge_tracker); + igraph_vector_bool_destroy(&visited_list); + igraph_inclist_destroy(&il); + igraph_vector_destroy(°ree); + IGRAPH_FINALLY_CLEAN(7); + + return IGRAPH_SUCCESS; +} + +/* solution adapted from https://www.geeksforgeeks.org/hierholzers-algorithm-directed-graph/ */ +static int igraph_i_eulerian_path_directed(const igraph_t *graph, igraph_vector_t *edge_res, igraph_vector_t *vertex_res, igraph_integer_t start_of_path) { + long int curr; + igraph_integer_t n, m; + igraph_inclist_t il; + igraph_stack_t path, tracker, edge_tracker, edge_path; + igraph_vector_bool_t visited_list; + igraph_vector_t remaining_out_edges; + + n = igraph_vcount(graph); + m = igraph_ecount(graph); + + if (edge_res) { + igraph_vector_clear(edge_res); + } + + if (vertex_res) { + igraph_vector_clear(vertex_res); + } + + if (m == 0 || n == 0) { + return IGRAPH_SUCCESS; + } + + IGRAPH_CHECK(igraph_stack_init(&path, n)); + IGRAPH_FINALLY(igraph_stack_destroy, &path); + + IGRAPH_CHECK(igraph_stack_init(&tracker, n)); + IGRAPH_FINALLY(igraph_stack_destroy, &tracker); + + IGRAPH_CHECK(igraph_stack_init(&edge_path, n)); + IGRAPH_FINALLY(igraph_stack_destroy, &edge_path); + + IGRAPH_CHECK(igraph_stack_init(&edge_tracker, n)); + IGRAPH_FINALLY(igraph_stack_destroy, &edge_tracker); + + IGRAPH_VECTOR_BOOL_INIT_FINALLY(&visited_list, m); + + IGRAPH_CHECK(igraph_stack_push(&tracker, start_of_path)); + + IGRAPH_CHECK(igraph_inclist_init(graph, &il, IGRAPH_OUT, IGRAPH_LOOPS_ONCE)); + IGRAPH_FINALLY(igraph_inclist_destroy, &il); + + IGRAPH_VECTOR_INIT_FINALLY(&remaining_out_edges, 0); + IGRAPH_CHECK(igraph_degree(graph, &remaining_out_edges, igraph_vss_all(), IGRAPH_OUT, IGRAPH_LOOPS)); + + curr = start_of_path; + + while (!igraph_stack_empty(&tracker)) { + + if (VECTOR(remaining_out_edges)[curr] != 0) { + igraph_vector_int_t *incedges; + long nc, edge = -1; + long int j, next; + IGRAPH_CHECK(igraph_stack_push(&tracker, curr)); + + incedges = igraph_inclist_get(&il, curr); + nc = igraph_vector_int_size(incedges); + IGRAPH_ASSERT(nc > 0); + + for (j = 0; j < nc; j++) { + edge = (long) VECTOR(*incedges)[j]; + if (!VECTOR(visited_list)[edge]) { + break; + } + } + + next = IGRAPH_TO(graph, edge); + + IGRAPH_CHECK(igraph_stack_push(&edge_tracker, edge)); + + /* remove edge here */ + VECTOR(remaining_out_edges)[curr]--; + VECTOR(visited_list)[edge] = 1; + + curr = next; + } else { /* back track to find remaining circuit */ + igraph_integer_t curr_e; + IGRAPH_CHECK(igraph_stack_push(&path, curr)); + curr = igraph_stack_pop(&tracker); + if (!igraph_stack_empty(&edge_tracker)) { + curr_e = igraph_stack_pop(&edge_tracker); + IGRAPH_CHECK(igraph_stack_push(&edge_path, curr_e)); + } + } + } + + if (edge_res) { + IGRAPH_CHECK(igraph_vector_reserve(edge_res, m)); + while (!igraph_stack_empty(&edge_path)) { + IGRAPH_CHECK(igraph_vector_push_back(edge_res, igraph_stack_pop(&edge_path))); + } + } + if (vertex_res) { + IGRAPH_CHECK(igraph_vector_reserve(vertex_res, m+1)); + while (!igraph_stack_empty(&path)) { + IGRAPH_CHECK(igraph_vector_push_back(vertex_res, igraph_stack_pop(&path))); + } + } + + igraph_stack_destroy(&path); + igraph_stack_destroy(&tracker); + igraph_stack_destroy(&edge_path); + igraph_stack_destroy(&edge_tracker); + igraph_vector_bool_destroy(&visited_list); + igraph_inclist_destroy(&il); + igraph_vector_destroy(&remaining_out_edges); + IGRAPH_FINALLY_CLEAN(7); + + return IGRAPH_SUCCESS; +} + + +/** + * \ingroup Eulerian + * \function igraph_eulerian_cycle + * \brief Finds an Eulerian cycle + * + * Finds an Eulerian cycle, if it exists. An Eulerian cycle is a closed path + * that traverses each edge precisely once. + * + * + * This function uses Hierholzer's algorithm. + * + * \param graph The graph object. + * \param edge_res Pointer to an initialised vector. The indices of edges + * belonging to the cycle will be stored here. May be \c NULL + * if it is not needed by the caller. + * \param vertex_res Pointer to an initialised vector. The indices of vertices + * belonging to the cycle will be stored here. May be \c NULL + * if it is not needed by the caller. + * \return Error code: + * \clist + * \cli IGRAPH_ENOMEM + * not enough memory for temporary data. + * \cli IGRAPH_EINVVID + * graph does not have an Eulerian cycle. + * \endclist + * + * Time complexity: O(|V|+|E|), the number of vertices plus the number of edges. + * + */ + +int igraph_eulerian_cycle(const igraph_t *graph, igraph_vector_t *edge_res, igraph_vector_t *vertex_res) { + igraph_bool_t has_cycle; + igraph_bool_t has_path; + igraph_integer_t start_of_path = 0; + + if (igraph_is_directed(graph)) { + IGRAPH_CHECK(igraph_i_is_eulerian_directed(graph, &has_path, &has_cycle, &start_of_path)); + + if (!has_cycle) { + IGRAPH_ERROR("The graph does not have an Eulerian cycle.", IGRAPH_EINVAL); + } + + IGRAPH_CHECK(igraph_i_eulerian_path_directed(graph, edge_res, vertex_res, start_of_path)); + } else { + IGRAPH_CHECK(igraph_i_is_eulerian_undirected(graph, &has_path, &has_cycle, &start_of_path)); + + if (!has_cycle) { + IGRAPH_ERROR("The graph does not have an Eulerian cycle.", IGRAPH_EINVAL); + } + + IGRAPH_CHECK(igraph_i_eulerian_path_undirected(graph, edge_res, vertex_res, start_of_path)); + } + + return IGRAPH_SUCCESS; +} + + +/** + * \ingroup Eulerian + * \function igraph_eulerian_path + * \brief Finds an Eulerian path + * + * Finds an Eulerian path, if it exists. An Eulerian path traverses + * each edge precisely once. + * + * + * This function uses Hierholzer's algorithm. + * + * \param graph The graph object. + * \param edge_res Pointer to an initialised vector. The indices of edges + * belonging to the path will be stored here. May be \c NULL + * if it is not needed by the caller. + * \param vertex_res Pointer to an initialised vector. The indices of vertices + * belonging to the path will be stored here. May be \c NULL + * if it is not needed by the caller. + * \return Error code: + * \clist + * \cli IGRAPH_ENOMEM + * not enough memory for temporary data. + * \cli IGRAPH_EINVVID + * graph does not have an Eulerian path. + * \endclist + * + * Time complexity: O(|V|+|E|), the number of vertices plus the number of edges. + * + */ + +int igraph_eulerian_path(const igraph_t *graph, igraph_vector_t *edge_res, igraph_vector_t *vertex_res) { + igraph_bool_t has_cycle; + igraph_bool_t has_path; + igraph_integer_t start_of_path = 0; + + if (igraph_is_directed(graph)) { + IGRAPH_CHECK(igraph_i_is_eulerian_directed(graph, &has_path, &has_cycle, &start_of_path)); + + if (!has_path) { + IGRAPH_ERROR("The graph does not have an Eulerian path.", IGRAPH_EINVAL); + } + IGRAPH_CHECK(igraph_i_eulerian_path_directed(graph, edge_res, vertex_res, start_of_path)); + } else { + IGRAPH_CHECK(igraph_i_is_eulerian_undirected(graph, &has_path, &has_cycle, &start_of_path)); + + if (!has_path) { + IGRAPH_ERROR("The graph does not have an Eulerian path.", IGRAPH_EINVAL); + } + + IGRAPH_CHECK(igraph_i_eulerian_path_undirected(graph, edge_res, vertex_res, start_of_path)); + } + + return IGRAPH_SUCCESS; +} diff --git a/src/rigraph/core/paths/histogram.c b/src/rigraph/core/paths/histogram.c new file mode 100644 index 0000000..a84858d --- /dev/null +++ b/src/rigraph/core/paths/histogram.c @@ -0,0 +1,149 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2005-2021 The igraph development 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., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA +*/ + +#include "igraph_paths.h" + +#include "igraph_adjlist.h" +#include "igraph_dqueue.h" +#include "igraph_interface.h" +#include "igraph_progress.h" + +#include "core/interruption.h" + +/** + * \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_LOOPS, IGRAPH_MULTIPLE)); + 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 length histogram: ", 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 + * If no edge weights are supplied, then the unweighted + * version, \ref igraph_shortest_paths() is called. + * + * + * 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. If negative weights are present, it + * should be 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 edges, then we can just run the unweighted version */ + if (no_of_edges == 0) { + return igraph_shortest_paths(graph, res, from, to, IGRAPH_OUT); + } + + /* If no negative weights, then we can run Dijkstra's algorithm */ + { + igraph_real_t min_weight = igraph_vector_min(weights); + if (igraph_is_nan(min_weight)) { + IGRAPH_ERROR("Weight vector must not contain NaN values", IGRAPH_EINVAL); + } + if (min_weight >= 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, j) -= sub; + } + igraph_vit_destroy(&tovit); + IGRAPH_FINALLY_CLEAN(1); + } + } + + igraph_vit_destroy(&fromvit); + igraph_matrix_destroy(&bfres); + IGRAPH_FINALLY_CLEAN(2); + + return IGRAPH_SUCCESS; +} diff --git a/src/rigraph/core/paths/random_walk.c b/src/rigraph/core/paths/random_walk.c new file mode 100644 index 0000000..a629d01 --- /dev/null +++ b/src/rigraph/core/paths/random_walk.c @@ -0,0 +1,294 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2014 Gabor Csardi + 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 "core/interruption.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 directed 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_LOOPS, IGRAPH_MULTIPLE)); + 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_int_t *neis; + igraph_integer_t nn; + neis = igraph_lazy_adjlist_get(&adj, start); + nn = igraph_vector_int_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 IGRAPH_SUCCESS; +} + + +/* 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. Additionally, no edge weight may + * be NaN. If either case does not hold, an error is returned. 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 directed 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 */ + + 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 (ec > 0) { + igraph_real_t min = igraph_vector_min(weights); + if (min < 0) { + IGRAPH_ERROR("Weights must be non-negative.", IGRAPH_EINVAL); + } + else if (igraph_is_nan(min)) { + IGRAPH_ERROR("Weights must not contain NaN values.", IGRAPH_EINVAL); + } + } + } + + IGRAPH_CHECK(igraph_vector_resize(edgewalk, steps)); + + IGRAPH_CHECK(igraph_inclist_init(graph, &il, mode, IGRAPH_LOOPS)); + 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_CALLOC(1, 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/src/rigraph/core/paths/shortest_paths.c b/src/rigraph/core/paths/shortest_paths.c new file mode 100644 index 0000000..0c967e6 --- /dev/null +++ b/src/rigraph/core/paths/shortest_paths.c @@ -0,0 +1,1270 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2005-2020 The igraph development 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, see . +*/ + +#include "igraph_paths.h" + +#include "igraph_adjlist.h" +#include "igraph_interface.h" +#include "igraph_dqueue.h" +#include "igraph_memory.h" +#include "igraph_progress.h" + +#include "core/indheap.h" +#include "core/interruption.h" + +#include + +/*****************************************************/ +/***** Average path length and global efficiency *****/ +/*****************************************************/ + +/* Computes the average of pairwise distances (used for igraph_average_path_length), + * or of inverse pairwise distances (used for igraph_global_efficiency), in an unweighted graph. */ +static int igraph_i_average_path_length_unweighted( + const igraph_t *graph, + igraph_real_t *res, + igraph_real_t *unconnected_pairs, /* if not NULL, will be set to the no. of non-connected ordered vertex pairs */ + const igraph_bool_t directed, + const igraph_bool_t invert, /* average inverse distances instead of distances */ + const igraph_bool_t unconn /* average over connected pairs instead of all pairs */) +{ + long int no_of_nodes = igraph_vcount(graph); + long int source, j, n; + long int *already_added; + igraph_real_t no_of_pairs = no_of_nodes > 0 ? no_of_nodes * (no_of_nodes - 1.0) : 0.0; /* no. of ordered vertex pairs */ + igraph_real_t no_of_conn_pairs = 0.0; /* no. of ordered pairs between which there is a path */ + + igraph_dqueue_t q = IGRAPH_DQUEUE_NULL; + igraph_vector_int_t *neis; + igraph_adjlist_t allneis; + + *res = 0; + already_added = IGRAPH_CALLOC(no_of_nodes, long int); + if (already_added == 0) { + IGRAPH_ERROR("Average path length calculation failed", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, already_added); + IGRAPH_DQUEUE_INIT_FINALLY(&q, 100); + + IGRAPH_CHECK(igraph_adjlist_init( + graph, &allneis, + directed ? IGRAPH_OUT : IGRAPH_ALL, + IGRAPH_LOOPS, IGRAPH_MULTIPLE + )); + IGRAPH_FINALLY(igraph_adjlist_destroy, &allneis); + + for (source = 0; source < no_of_nodes; source++) { + IGRAPH_CHECK(igraph_dqueue_push(&q, source)); + IGRAPH_CHECK(igraph_dqueue_push(&q, 0)); + already_added[source] = source + 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] == source + 1) { + continue; + } + already_added[neighbor] = source + 1; + if (invert) { + *res += 1.0/(actdist + 1.0); + } else { + *res += actdist + 1.0; + } + no_of_conn_pairs += 1; + IGRAPH_CHECK(igraph_dqueue_push(&q, neighbor)); + IGRAPH_CHECK(igraph_dqueue_push(&q, actdist + 1)); + } + } /* while !igraph_dqueue_empty */ + } /* for source < no_of_nodes */ + + + if (no_of_pairs == 0) { + *res = IGRAPH_NAN; /* can't average zero items */ + } else { + if (unconn) { /* average over connected pairs */ + if (no_of_conn_pairs == 0) { + *res = IGRAPH_NAN; /* can't average zero items */ + } else { + *res /= no_of_conn_pairs; + } + } else { /* average over all pairs */ + /* no_of_conn_pairs < no_of_pairs implies that the graph is disconnected */ + if (no_of_conn_pairs < no_of_pairs && ! invert) { + /* When invert=false, assume the distance between non-connected pairs to be infinity */ + *res = IGRAPH_INFINITY; + } else { + /* When invert=true, assume the inverse distance between non-connected pairs + * to be zero. Therefore, no special treatment is needed for disconnected graphs. */ + *res /= no_of_pairs; + } + } + } + + if (unconnected_pairs) + *unconnected_pairs = no_of_pairs - no_of_conn_pairs; + + /* clean */ + IGRAPH_FREE(already_added); + igraph_dqueue_destroy(&q); + igraph_adjlist_destroy(&allneis); + IGRAPH_FINALLY_CLEAN(3); + + return IGRAPH_SUCCESS; +} + + +/* Computes the average of pairwise distances (used for igraph_average_path_length_dijkstra), + * or of inverse pairwise distances (used for igraph_global_efficiency), in an unweighted graph. + * Uses Dijkstra's algorithm, therefore all weights must be non-negative. + */ +static int igraph_i_average_path_length_dijkstra( + const igraph_t *graph, + igraph_real_t *res, + igraph_real_t *unconnected_pairs, + const igraph_vector_t *weights, + const igraph_bool_t directed, + const igraph_bool_t invert, /* average inverse distances instead of distances */ + const igraph_bool_t unconn /* average over connected pairs instead of all pairs */) +{ + + /* 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_lazy_inclist_t inclist; + long int source, j; + igraph_real_t no_of_pairs; + igraph_real_t no_of_conn_pairs = 0.0; /* no. of ordered pairs between which there is a path */ + + if (!weights) { + return igraph_i_average_path_length_unweighted(graph, res, unconnected_pairs, directed, invert, unconn); + } + + if (igraph_vector_size(weights) != no_of_edges) { + IGRAPH_ERRORF("Weight vector length (%ld) does not match the number of edges (%ld).", + IGRAPH_EINVAL, igraph_vector_size(weights), no_of_edges); + } + if (no_of_edges > 0) { + igraph_real_t min = igraph_vector_min(weights); + if (min < 0) { + IGRAPH_ERRORF("Weight vector must be non-negative, got %g.", IGRAPH_EINVAL, min); + } + else if (igraph_is_nan(min)) { + IGRAPH_ERROR("Weight vector must not contain NaN values.", IGRAPH_EINVAL); + } + } + + /* Avoid returning a negative zero, which would be printed as -0 in tests. */ + if (no_of_nodes > 0) { + no_of_pairs = no_of_nodes * (no_of_nodes - 1.0); + } else { + no_of_pairs = 0; + } + + IGRAPH_CHECK(igraph_2wheap_init(&Q, no_of_nodes)); + IGRAPH_FINALLY(igraph_2wheap_destroy, &Q); + IGRAPH_CHECK(igraph_lazy_inclist_init( + graph, &inclist, directed ? IGRAPH_OUT : IGRAPH_ALL, IGRAPH_LOOPS + )); + IGRAPH_FINALLY(igraph_lazy_inclist_destroy, &inclist); + + *res = 0.0; + + for (source = 0; source < no_of_nodes; ++source) { + + IGRAPH_ALLOW_INTERRUPTION(); + + 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_int_t *neis; + long int nlen; + + if (minnei != source) { + if (invert) { + *res += 1.0/(mindist - 1.0); + } else { + *res += mindist - 1.0; + } + no_of_conn_pairs += 1; + } + + /* Now check all neighbors of 'minnei' for a shorter path */ + neis = igraph_lazy_inclist_get(&inclist, (igraph_integer_t) 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) { + /* 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) */ + } /* for source < no_of_nodes */ + + if (no_of_pairs == 0) { + *res = IGRAPH_NAN; /* can't average zero items */ + } else { + if (unconn) { /* average over connected pairs */ + if (no_of_conn_pairs == 0) { + *res = IGRAPH_NAN; /* can't average zero items */ + } else { + *res /= no_of_conn_pairs; + } + } else { /* average over all pairs */ + /* no_of_conn_pairs < no_of_pairs implies that the graph is disconnected */ + if (no_of_conn_pairs < no_of_pairs && ! invert) { + *res = IGRAPH_INFINITY; + } else { + *res /= no_of_pairs; + } + } + } + + if (unconnected_pairs) + *unconnected_pairs = no_of_pairs - no_of_conn_pairs; + + igraph_lazy_inclist_destroy(&inclist); + igraph_2wheap_destroy(&Q); + IGRAPH_FINALLY_CLEAN(2); + + return IGRAPH_SUCCESS; +} + + +/** + * \ingroup structural + * \function igraph_average_path_length + * \brief Calculates the average unweighted shortest path length between all vertex pairs. + * + * + * If no vertex pairs can be included in the calculation, for example because the graph + * has fewer than two vertices, or if the graph has no edges and \c unconn is set to \c TRUE, + * NaN is returned. + * + * \param graph The graph object. + * \param res Pointer to a real number, this will contain the result. + * \param unconn_pairs Pointer to a real number. If not a null pointer, the number of + * ordered vertex pairs where the second vertex is unreachable from the first one + * will be stored here. + * \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, \c IGRAPH_INFINITY is returned + * for disconnected graphs. + * \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. + * + * \sa \ref igraph_average_path_length_dijkstra() for the weighted version. + * + * \example examples/simple/igraph_average_path_length.c + */ + +int igraph_average_path_length(const igraph_t *graph, + igraph_real_t *res, igraph_real_t *unconn_pairs, + igraph_bool_t directed, igraph_bool_t unconn) +{ + return igraph_i_average_path_length_unweighted(graph, res, unconn_pairs, directed, /* invert= */ 0, unconn); +} + + +/** + * \ingroup structural + * \function igraph_average_path_length_dijkstra + * \brief Calculates the average weighted shortest path length between all vertex pairs. + * + * + * If no vertex pairs can be included in the calculation, for example because the graph + * has fewer than two vertices, or if the graph has no edges and \c unconn is set to \c TRUE, + * NaN is returned. + * + * + * All distinct ordered vertex pairs are taken into account. + * + * \param graph The graph object. + * \param res Pointer to a real number, this will contain the result. + * \param unconn_pairs Pointer to a real number. If not a null pointer, the number of + * ordered vertex pairs where the second vertex is unreachable from the first one + * will be stored here. + * \param weights The edge weights. All edge weights must be + * non-negative for Dijkstra's algorithm to work. Additionally, no + * edge weight may be NaN. If either case does not hold, an error + * is returned. If this is a null pointer, then the unweighted + * version, \ref igraph_average_path_length() is called. + * \param directed Boolean, whether to consider directed paths. + * Ignored for undirected graphs. + * \param unconn If \c TRUE, only those pairs are considered for the calculation + * between which there is a path. If \c FALSE, \c IGRAPH_INFINITY is returned + * for disconnected graphs. + * \return Error code: + * \clist + * \cli IGRAPH_ENOMEM + * not enough memory for data structures + * \cli IGRAPH_EINVAL + * invalid weight vector + * \endclist + * + * Time complexity: O(|V| |E| log|E| + |V|), where |V| is the number of + * vertices and |E| is the number of edges. + * + * \sa \ref igraph_average_path_length() for a slightly faster unweighted version. + * + * \example examples/simple/igraph_grg_game.c + */ + +int igraph_average_path_length_dijkstra(const igraph_t *graph, + igraph_real_t *res, igraph_real_t *unconn_pairs, + const igraph_vector_t *weights, + igraph_bool_t directed, igraph_bool_t unconn) +{ + return igraph_i_average_path_length_dijkstra(graph, res, unconn_pairs, weights, directed, /* invert= */ 0, unconn); +} + + +/** + * \ingroup structural + * \function igraph_global_efficiency + * \brief Calculates the global efficiency of a network. + * + * + * The global efficiency of a network is defined as the average of inverse distances + * between all pairs of vertices: E_g = 1/(N*(N-1)) sum_{i!=j} 1/d_ij, + * where N is the number of vertices. + * The inverse distance between pairs that are not reachable from each other is considered + * to be zero. For graphs with fewer than 2 vertices, NaN is returned. + * + * + * Reference: + * V. Latora and M. Marchiori, + * Efficient Behavior of Small-World Networks, + * Phys. Rev. Lett. 87, 198701 (2001). + * https://dx.doi.org/10.1103/PhysRevLett.87.198701 + * + * \param graph The graph object. + * \param res Pointer to a real number, this will contain the result. + * \param weights The edge weights. All edge weights must be + * non-negative for Dijkstra's algorithm to work. Additionally, no + * edge weight may be NaN. If either case does not hold, an error + * is returned. If this is a null pointer, then the unweighted + * version, \ref igraph_average_path_length() is used in calculating + * the global efficiency. + * \param directed Boolean, whether to consider directed paths. + * Ignored for undirected graphs. + * \return Error code: + * \clist + * \cli IGRAPH_ENOMEM + * not enough memory for data structures + * \cli IGRAPH_EINVAL + * invalid weight vector + * \endclist + * + * Time complexity: O(|V| |E| log|E| + |V|) for weighted graphs and + * O(|V| |E|) for unweighted ones. |V| denotes the number of + * vertices and |E| denotes the number of edges. + * + */ + +int igraph_global_efficiency(const igraph_t *graph, igraph_real_t *res, + const igraph_vector_t *weights, + igraph_bool_t directed) +{ + return igraph_i_average_path_length_dijkstra(graph, res, NULL, weights, directed, /* invert= */ 1, /* unconn= */ 0); +} + + +/****************************/ +/***** Local efficiency *****/ +/****************************/ + +static int igraph_i_local_efficiency_unweighted( + const igraph_t *graph, + const igraph_adjlist_t *adjlist, + igraph_dqueue_t *q, + long int *already_counted, + igraph_vector_t *vertex_neis, + igraph_vector_char_t *nei_mask, + igraph_real_t *res, + igraph_integer_t vertex, + igraph_neimode_t mode) +{ + + long int no_of_nodes = igraph_vcount(graph); + long int vertex_neis_size; + long int neighbor_count; /* unlike 'vertex_neis_size', 'neighbor_count' does not count self-loops and multi-edges */ + long int i, j; + + igraph_dqueue_clear(q); + + /* already_counted[i] is 0 iff vertex i was not reached so far, otherwise + * it is the index of the source vertex in vertex_neis that it was reached + * from, plus 1 */ + memset(already_counted, 0, no_of_nodes * sizeof(long int)); + + IGRAPH_CHECK(igraph_neighbors(graph, vertex_neis, vertex, mode)); + vertex_neis_size = igraph_vector_size(vertex_neis); + + igraph_vector_char_fill(nei_mask, 0); + neighbor_count = 0; + for (i=0; i < vertex_neis_size; ++i) { + long int v = VECTOR(*vertex_neis)[i]; + if (v != vertex && ! VECTOR(*nei_mask)[v]) { + VECTOR(*nei_mask)[v] = 1; /* mark as unprocessed neighbour */ + neighbor_count++; + } + } + + *res = 0.0; + + /* when the neighbor count is smaller than 2, we return 0.0 */ + if (neighbor_count < 2) { + return IGRAPH_SUCCESS; + } + + for (i=0; i < vertex_neis_size; ++i) { + long int source = VECTOR(*vertex_neis)[i]; + long int reached = 0; + + IGRAPH_ALLOW_INTERRUPTION(); + + if (source == vertex) + continue; + + if (VECTOR(*nei_mask)[source] == 2) + continue; + + VECTOR(*nei_mask)[source] = 2; /* mark neighbour as already processed */ + + IGRAPH_CHECK(igraph_dqueue_push(q, source)); + IGRAPH_CHECK(igraph_dqueue_push(q, 0)); + already_counted[source] = i + 1; + + while (!igraph_dqueue_empty(q)) { + igraph_vector_int_t *act_neis; + long int act_neis_size; + long int act = (long int) igraph_dqueue_pop(q); + long int actdist = (long int) igraph_dqueue_pop(q); + + if (act != source && VECTOR(*nei_mask)[act]) { + *res += 1.0 / actdist; + reached++; + if (reached == neighbor_count) { + igraph_dqueue_clear(q); + break; + } + } + + act_neis = igraph_adjlist_get(adjlist, act); + act_neis_size = igraph_vector_int_size(act_neis); + for (j = 0; j < act_neis_size; j++) { + long int neighbor = (long int) VECTOR(*act_neis)[j]; + + if (neighbor == vertex || 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)); + } + } + } + + *res /= neighbor_count * (neighbor_count - 1.0); + + return IGRAPH_SUCCESS; +} + +static int igraph_i_local_efficiency_dijkstra( + const igraph_t *graph, + igraph_lazy_inclist_t *inclist, + igraph_2wheap_t *Q, + igraph_vector_t *vertex_neis, + igraph_vector_char_t *nei_mask, /* true if the corresponding node is a neighbour of 'vertex' */ + igraph_real_t *res, + igraph_integer_t vertex, + igraph_neimode_t mode, + const igraph_vector_t *weights) +{ + + /* 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 i, j; + long int vertex_neis_size; + long int neighbor_count; /* unlike 'inc_edges_size', 'neighbor_count' does not count self-loops or multi-edges */ + + IGRAPH_CHECK(igraph_neighbors(graph, vertex_neis, vertex, mode)); + vertex_neis_size = igraph_vector_size(vertex_neis); + + igraph_vector_char_fill(nei_mask, 0); + neighbor_count = 0; + for (i=0; i < vertex_neis_size; ++i) { + long int v = VECTOR(*vertex_neis)[i]; + if (v != vertex && ! VECTOR(*nei_mask)[v]) { + VECTOR(*nei_mask)[v] = 1; /* mark as unprocessed neighbour */ + neighbor_count++; + } + } + + *res = 0.0; + + /* when the neighbor count is smaller than 2, we return 0.0 */ + if (neighbor_count < 2) { + return IGRAPH_SUCCESS; + } + + for (i=0; i < vertex_neis_size; ++i) { + long int source = VECTOR(*vertex_neis)[i]; + long int reached = 0; + + IGRAPH_ALLOW_INTERRUPTION(); + + if (source == vertex) + continue; + + /* avoid processing a neighbour twice in multigraphs */ + if (VECTOR(*nei_mask)[source] == 2) + continue; + VECTOR(*nei_mask)[source] = 2; /* mark as already processed */ + + 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_int_t *neis; + long int nlen; + + if (minnei != source && VECTOR(*nei_mask)[minnei]) { + *res += 1.0/(mindist - 1.0); + reached++; + if (reached == neighbor_count) { + 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_int_size(neis); + for (j = 0; j < nlen; j++) { + igraph_real_t altdist, curdist; + igraph_bool_t active, has; + long int edge = (long int) VECTOR(*neis)[j]; + long int tto = IGRAPH_OTHER(graph, edge, minnei); + + if (tto == vertex) + continue; + + altdist = mindist + VECTOR(*weights)[edge]; + active = igraph_2wheap_has_active(Q, tto); + has = igraph_2wheap_has_elem(Q, tto); + 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) */ + + } + + *res /= neighbor_count * (neighbor_count - 1.0); + + return IGRAPH_SUCCESS; +} + + +/** + * \ingroup structural + * \function igraph_local_efficiency + * \brief Calculates the local efficiency around each vertex in a network. + * + * + * The local efficiency of a network around a vertex is defined as follows: + * We remove the vertex and compute the distances (shortest path lengths) between + * its neighbours through the rest of the network. The local efficiency around the + * removed vertex is the average of the inverse of these distances. + * + * + * The inverse distance between two vertices which are not reachable from each other + * is considered to be zero. The local efficiency around a vertex with fewer than two + * neighbours is taken to be zero by convention. + * + * + * Reference: + * I. Vragović, E. Louis, and A. Díaz-Guilera, + * Efficiency of informational transfer in regular and complex networks, + * Phys. Rev. E 71, 1 (2005). + * http://dx.doi.org/10.1103/PhysRevE.71.036122 + * + * \param graph The graph object. + * \param res Pointer to an initialized vector, this will contain the result. + * \param vids The vertices around which the local efficiency will be calculated. + * \param weights The edge weights. All edge weights must be + * non-negative. Additionally, no edge weight may be NaN. If either + * case does not hold, an error is returned. If this is a null + * pointer, then the unweighted version, + * \ref igraph_average_path_length() is called. + * \param directed Boolean, whether to consider directed paths. + * Ignored for undirected graphs. + * \param mode How to determine the local neighborhood of each vertex + * in directed graphs. Ignored in undirected graphs. + * \clist + * \cli IGRAPH_ALL + * take both in- and out-neighbours; + * this is a reasonable default for high-level interfaces. + * \cli IGRAPH_OUT + * take only out-neighbours + * \cli IGRAPH_IN + * take only in-neighbours + * \endclist + * \return Error code: + * \clist + * \cli IGRAPH_ENOMEM + * not enough memory for data structures + * \cli IGRAPH_EINVAL + * invalid weight vector + * \endclist + * + * Time complexity: O(|E|^2 log|E|) for weighted graphs and + * O(|E|^2) for unweighted ones. |E| denotes the number of edges. + * + * \sa \ref igraph_average_local_efficiency() + * + */ + +int igraph_local_efficiency(const igraph_t *graph, igraph_vector_t *res, + const igraph_vs_t vids, + const igraph_vector_t *weights, + igraph_bool_t directed, igraph_neimode_t mode) +{ + long int no_of_nodes = igraph_vcount(graph); + long int no_of_edges = igraph_ecount(graph); + long int nodes_to_calc; /* no. of vertices includes in computation */ + igraph_vit_t vit; + igraph_vector_t vertex_neis; + igraph_vector_char_t nei_mask; + long int i; + + /* 'nei_mask' is a vector indexed by vertices. The meaning of its values is as follows: + * 0: not a neighbour of 'vertex' + * 1: a not-yet-processed neighbour of 'vertex' + * 2: an already processed neighbour of 'vertex' + * + * Marking neighbours of already processed is necessary to avoid processing them more + * than once in multigraphs. + */ + IGRAPH_CHECK(igraph_vector_char_init(&nei_mask, no_of_nodes)); + IGRAPH_FINALLY(igraph_vector_char_destroy, &nei_mask); + IGRAPH_VECTOR_INIT_FINALLY(&vertex_neis, 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(res, nodes_to_calc)); + + if (! weights) /* unweighted case */ + { + long int *already_counted; + igraph_adjlist_t adjlist; + igraph_dqueue_t q = IGRAPH_DQUEUE_NULL; + + already_counted = IGRAPH_CALLOC(no_of_nodes, long int); + if (already_counted == 0) { + IGRAPH_ERROR("Local efficiency calculation failed", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, already_counted); + + IGRAPH_CHECK(igraph_adjlist_init( + graph, &adjlist, + directed ? IGRAPH_OUT : IGRAPH_ALL, + IGRAPH_LOOPS, IGRAPH_MULTIPLE + )); + IGRAPH_FINALLY(igraph_adjlist_destroy, &adjlist); + + IGRAPH_DQUEUE_INIT_FINALLY(&q, 100); + + for (IGRAPH_VIT_RESET(vit), i=0; + ! IGRAPH_VIT_END(vit); + IGRAPH_VIT_NEXT(vit), i++) + { + IGRAPH_CHECK(igraph_i_local_efficiency_unweighted( + graph, &adjlist, + &q, already_counted, &vertex_neis, &nei_mask, + &(VECTOR(*res)[i]), IGRAPH_VIT_GET(vit), mode)); + } + + igraph_dqueue_destroy(&q); + igraph_adjlist_destroy(&adjlist); + IGRAPH_FREE(already_counted); + IGRAPH_FINALLY_CLEAN(3); + } + else /* weighted case */ + { + igraph_lazy_inclist_t inclist; + igraph_2wheap_t Q; + + if (igraph_vector_size(weights) != no_of_edges) { + IGRAPH_ERROR("Weight vector length does not match the number of edges", IGRAPH_EINVAL); + } + if (no_of_edges > 0) { + igraph_real_t min = igraph_vector_min(weights); + if (min < 0) { + IGRAPH_ERROR("Weight vector must be non-negative", IGRAPH_EINVAL); + } + else if (igraph_is_nan(min)) { + IGRAPH_ERROR("Weight vector must not contain NaN values", IGRAPH_EINVAL); + } + } + + IGRAPH_CHECK(igraph_lazy_inclist_init( + graph, &inclist, directed ? IGRAPH_OUT : IGRAPH_ALL, IGRAPH_LOOPS + )); + IGRAPH_FINALLY(igraph_lazy_inclist_destroy, &inclist); + IGRAPH_CHECK(igraph_2wheap_init(&Q, no_of_nodes)); + IGRAPH_FINALLY(igraph_2wheap_destroy, &Q); + + for (IGRAPH_VIT_RESET(vit), i=0; + ! IGRAPH_VIT_END(vit); + IGRAPH_VIT_NEXT(vit), i++) + { + IGRAPH_CHECK(igraph_i_local_efficiency_dijkstra( + graph, &inclist, + &Q, &vertex_neis, &nei_mask, + &(VECTOR(*res)[i]), IGRAPH_VIT_GET(vit), mode, weights)); + } + + igraph_2wheap_destroy(&Q); + igraph_lazy_inclist_destroy(&inclist); + IGRAPH_FINALLY_CLEAN(2); + } + + igraph_vit_destroy(&vit); + igraph_vector_destroy(&vertex_neis); + igraph_vector_char_destroy(&nei_mask); + IGRAPH_FINALLY_CLEAN(3); + + return IGRAPH_SUCCESS; +} + + +/** + * \ingroup structural + * \function igraph_average_local_efficiency + * \brief Calculates the average local efficiency in a network. + * + * For the null graph, zero is returned by convention. + * + * \param graph The graph object. + * \param res Pointer to a real number, this will contain the result. + * \param weights The edge weights. They must be all non-negative. + * If a null pointer is given, all weights are assumed to be 1. + * \param directed Boolean, whether to consider directed paths. + * Ignored for undirected graphs. + * \param mode How to determine the local neighborhood of each vertex + * in directed graphs. Ignored in undirected graphs. + * \clist + * \cli IGRAPH_ALL + * take both in- and out-neighbours; + * this is a reasonable default for high-level interfaces. + * \cli IGRAPH_OUT + * take only out-neighbours + * \cli IGRAPH_IN + * take only in-neighbours + * \endclist + * \return Error code: + * \clist + * \cli IGRAPH_ENOMEM + * not enough memory for data structures + * \cli IGRAPH_EINVAL + * invalid weight vector + * \endclist + * + * Time complexity: O(|E|^2 log|E|) for weighted graphs and + * O(|E|^2) for unweighted ones. |E| denotes the number of edges. + * + * \sa \ref igraph_local_efficiency() + * + */ + +int igraph_average_local_efficiency(const igraph_t *graph, igraph_real_t *res, + const igraph_vector_t *weights, + igraph_bool_t directed, igraph_neimode_t mode) +{ + long int no_of_nodes = igraph_vcount(graph); + igraph_vector_t local_eff; + + /* If there are fewer than 3 vertices, no vertex has more than one neighbour, thus all + local efficiencies are zero. For the null graph, we return zero by convention. */ + if (no_of_nodes < 3) { + *res = 0; + return IGRAPH_SUCCESS; + } + + IGRAPH_VECTOR_INIT_FINALLY(&local_eff, no_of_nodes); + + IGRAPH_CHECK(igraph_local_efficiency(graph, &local_eff, igraph_vss_all(), weights, directed, mode)); + + *res = igraph_vector_sum(&local_eff); + *res /= no_of_nodes; + + igraph_vector_destroy(&local_eff); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + + +/***************************/ +/***** Graph diameter ******/ +/***************************/ + +/** + * \ingroup structural + * \function igraph_diameter + * \brief Calculates the diameter of a graph (longest geodesic). + * + * The diameter of a graph is the length of the longest shortest path it has. + * This function computes both the diameter, as well as the corresponding path. + * The diameter of the null graph is considered be infinity by convention. + * + * If the graph has no vertices, \c IGRAPH_NAN is returned. + * + * \param graph The graph object. + * \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. If the graph has no diameter path, + * it will be set to -1. + * \param pto Pointer to an integer, if not \c NULL it will be set to the + * target vertex of the diameter path. If the graph has no diameter path, + * it will be set to -1. + * \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: + * \c IGRAPH_ENOMEM, not enough memory for + * temporary data. + * + * Time complexity: O(|V||E|), the + * number of vertices times the number of edges. + * + * \sa \ref igraph_diameter_dijkstra() + * + * \example examples/simple/igraph_diameter.c + */ + +int igraph_diameter(const igraph_t *graph, igraph_real_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; + igraph_real_t res = 0; + + igraph_dqueue_t q = IGRAPH_DQUEUE_NULL; + igraph_vector_int_t *neis; + igraph_neimode_t dirmode; + igraph_adjlist_t allneis; + + /* See https://github.com/igraph/igraph/issues/1538#issuecomment-724071857 + * for why we return NaN for the null graph. */ + if (no_of_nodes == 0) { + if (pres) { + *pres = IGRAPH_NAN; + } + if (path) { + igraph_vector_clear(path); + } + if (pfrom) { + *pfrom = -1; + } + if (pto) { + *pto = -1; + } + return IGRAPH_SUCCESS; + } + + 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_LOOPS, IGRAPH_MULTIPLE)); + 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 IGRAPH_INFINITY */ + if (nodes_reached != no_of_nodes && !unconn) { + res = IGRAPH_INFINITY; + from = -1; + to = -1; + break; + } + } /* for i 0) { + igraph_real_t min = igraph_vector_min(weights); + if (min < 0) { + IGRAPH_ERROR("Weight vector must be non-negative", IGRAPH_EINVAL); + } + else if (igraph_is_nan(min)) { + IGRAPH_ERROR("Weight vector must not contain NaN values", 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_LOOPS)); + 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; +} diff --git a/src/rigraph/core/paths/simple_paths.c b/src/rigraph/core/paths/simple_paths.c new file mode 100644 index 0000000..45c0d3c --- /dev/null +++ b/src/rigraph/core/paths/simple_paths.c @@ -0,0 +1,179 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2014 Gabor Csardi + 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_interface.h" +#include "igraph_vector_ptr.h" +#include "igraph_iterators.h" +#include "igraph_adjlist.h" +#include "igraph_stack.h" + +#include "core/interruption.h" + +/** + * \function igraph_get_all_simple_paths + * \brief List all simple paths from one source. + * + * A path is simple if its vertices are unique, i.e. no vertex + * is visited more than once. + * + * + * 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. + * + * + * 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 = 0; + + 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, IGRAPH_NO_LOOPS, IGRAPH_NO_MULTIPLE + )); + 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_int_t *neis = igraph_lazy_adjlist_get(&adjlist, act); + int n = igraph_vector_int_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/src/rigraph/core/paths/unweighted.c b/src/rigraph/core/paths/unweighted.c new file mode 100644 index 0000000..9db9d72 --- /dev/null +++ b/src/rigraph/core/paths/unweighted.c @@ -0,0 +1,540 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2005-2021 The igraph development 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, see . +*/ + +#include "igraph_paths.h" + +#include "igraph_adjlist.h" +#include "igraph_dqueue.h" +#include "igraph_interface.h" +#include "igraph_memory.h" + +#include "core/interruption.h" + +/** + * \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 The source vertices. + * \param to The target vertices. It is not allowed to include a + * vertex twice or more. + * \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_LOOPS, IGRAPH_MULTIPLE)); + 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(igraph_free, already_counted); + IGRAPH_DQUEUE_INIT_FINALLY(&q, 100); + + all_to = igraph_vs_is_all(&to); + if (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); + long int nei_count = igraph_vector_int_size(neis); + for (j = 0; j < nei_count; 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 Shortest paths from a vertex. + * + * + * 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, vsize; + 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)); + vsize = igraph_vector_size(&tmp); + for (j = 0; j < vsize; 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 + * \brief 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. + * + * 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; +} diff --git a/src/rigraph/core/properties/basic_properties.c b/src/rigraph/core/properties/basic_properties.c new file mode 100644 index 0000000..58bed34 --- /dev/null +++ b/src/rigraph/core/properties/basic_properties.c @@ -0,0 +1,331 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2005-2012 Gabor Csardi + 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" + +/** + * \section about_structural + * + * These functions usually calculate some structural property + * of a graph, like its diameter, the degree of the nodes, etc. + */ + +/** + * \function igraph_density + * \brief Calculate the density of a graph. + * + * The density of a graph is simply the ratio of the actual number of its + * edges and the largest possible number of edges it could have. + * The maximum number of edges depends on interpretation: are vertices + * allowed to have a connected to themselves? This is controlled by the + * \p loops parameter. + * + * + * Note that density is ill-defined for graphs which have multiple edges + * between some pairs of vertices. Consider calling \ref igraph_simplify() + * on such graphs. + * + * \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 self-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_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. + * + * + * 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. The diversity of isolated vertices will be NaN + * (not-a-number), while that of vertices with a single connection + * will be zero. + * + * + * The measure works only if the graph is undirected and has no multiple edges. + * If the graph has multiple edges, simplify it first using \ref + * igraph_simplify(). If the graph is directed, convert it into an undirected + * graph with \ref igraph_to_undirected() . + * + * \param graph The undirected input graph. + * \param weights The edge weights, in the order of the edge ids, must + * have appropriate length. Weights must be non-negative. + * \param res An initialized vector, the results are stored here. + * \param vids Vertex selector that specifies the vertices which to calculate + * the measure. + * \return Error code. + * + * Time complexity: O(|V|+|E|), linear. + * + */ +int igraph_diversity(const igraph_t *graph, const igraph_vector_t *weights, + igraph_vector_t *res, const igraph_vs_t vids) { + + long int no_of_edges = igraph_ecount(graph); + long int k, i; + igraph_vector_t incident; + igraph_bool_t has_multiple; + igraph_vit_t vit; + + if (igraph_is_directed(graph)) { + IGRAPH_ERROR("Diversity measure works with undirected graphs only.", IGRAPH_EINVAL); + } + + 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_CHECK(igraph_has_multiple(graph, &has_multiple)); + if (has_multiple) { + IGRAPH_ERROR("Diversity measure works only if the graph has no multiple edges.", IGRAPH_EINVAL); + } + + if (no_of_edges > 0) { + igraph_real_t minweight = igraph_vector_min(weights); + if (minweight < 0) { + IGRAPH_ERROR("Weight vector must be non-negative.", IGRAPH_EINVAL); + } else if (igraph_is_nan(minweight)) { + IGRAPH_ERROR("Weight vector must not contain NaN values.", IGRAPH_EINVAL); + } + } + + IGRAPH_VECTOR_INIT_FINALLY(&incident, 10); + + IGRAPH_CHECK(igraph_vit_create(graph, vids, &vit)); + IGRAPH_FINALLY(igraph_vit_destroy, &vit); + + igraph_vector_clear(res); + IGRAPH_CHECK(igraph_vector_reserve(res, IGRAPH_VIT_SIZE(vit))); + + for (IGRAPH_VIT_RESET(vit); !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit)) { + igraph_real_t d; + long int v = IGRAPH_VIT_GET(vit); + + IGRAPH_CHECK(igraph_incident(graph, &incident, v, /*mode=*/ IGRAPH_ALL)); + k = igraph_vector_size(&incident); /* degree */ + + /* + * Non-normalized diversity is defined as + * d = -sum_i w_i/s log (w_i/s) + * where s = sum_i w_i. In order to avoid two passes through the w vector, + * we use the equivalent formulation of + * d = log s - (sum_i w_i log w_i) / s + * However, this formulation may not give an exact 0.0 for some w when k=1, + * due to roundoff errors (examples: w=3 or w=7). For this reason, we + * special-case the computation for k=1 even for the unnormalized diversity + * insted of just setting the normalization factor to 1 for this case. + */ + if (k == 0) { + d = IGRAPH_NAN; + } else if (k == 1) { + if (VECTOR(*weights)[0] > 0) d = 0.0; /* s > 0 */ + else d = IGRAPH_NAN; /* s == 0 */ + } else { + igraph_real_t s = 0.0, ent = 0.0; + for (i = 0; i < k; i++) { + igraph_real_t w = VECTOR(*weights)[(long int)VECTOR(incident)[i]]; + if (w == 0) continue; + s += w; + ent += (w * log(w)); + } + d = (log(s) - ent / s) / log(k); + } + + igraph_vector_push_back(res, d); /* reserved */ + } + + igraph_vit_destroy(&vit); + igraph_vector_destroy(&incident); + IGRAPH_FINALLY_CLEAN(2); + + return IGRAPH_SUCCESS; +} + +/** + * \ingroup structural + * \function igraph_reciprocity + * \brief Calculates the reciprocity of a directed graph. + * + * + * 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: + * sum(i, j, (A.*A')ij) / sum(i, j, Aij), where + * A.*A' is the element-wise product of matrix + * A and its transpose. This measure is + * calculated if the \p mode argument is \c + * IGRAPH_RECIPROCITY_DEFAULT. + * + * + * 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; +} diff --git a/src/rigraph/core/properties/constraint.c b/src/rigraph/core/properties/constraint.c new file mode 100644 index 0000000..15e5e95 --- /dev/null +++ b/src/rigraph/core/properties/constraint.c @@ -0,0 +1,306 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2005-2021 The igraph development 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., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_centrality.h" + +#include "igraph_interface.h" + +/** + * \function igraph_constraint + * \brief Burt's constraint scores. + * + * + * This function calculates Burt's constraint scores for the given + * vertices, also known as structural holes. + * + * + * 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, + *
+ * C[i] = sum( sum( (p[i,q] p[q,j])^2, q in V[i], q != i,j ), j in + * V[], j != i) + *
+ * for a graph of order (i.e. number of vertices) N, where proportional + * tie strengths are defined as + *
+ * p[i,j]=(a[i,j]+a[j,i]) / sum(a[i,k]+a[k,i], k in V[i], k != i), + *
+ * a[i,j] are elements of A and + * the latter being the graph adjacency matrix. For isolated vertices, + * constraint is undefined. + * + *
+ * Burt, R.S. (2004). Structural holes and good ideas. American + * Journal of Sociology 110, 349-399. + * + * + * 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, vsize, vsize2; + igraph_integer_t edge, from, to, edge2; + + 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(°ree, 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, °ree, 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 */ + vsize = igraph_vector_size(&ineis_in); + for (b = 0; b < vsize; b++) { + edge = (igraph_integer_t) VECTOR(ineis_in)[b]; + j = (long int) IGRAPH_OTHER(graph, edge, i); + VECTOR(contrib)[j] = 0.0; + } + vsize = igraph_vector_size(&ineis_out); + for (b = 0; b < vsize; b++) { + edge = (igraph_integer_t) VECTOR(ineis_out)[b]; + j = (long int) IGRAPH_OTHER(graph, edge, i); + VECTOR(contrib)[j] = 0.0; + } + + /* add the direct contributions, in-neighbors and out-neighbors */ + vsize = igraph_vector_size(&ineis_in); + for (b = 0; b < vsize; b++) { + edge = (igraph_integer_t) VECTOR(ineis_in)[b]; + j = (long int) IGRAPH_OTHER(graph, edge, i); + 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)) { + vsize = igraph_vector_size(&ineis_out); + for (b = 0; b < vsize; b++) { + edge = (igraph_integer_t) VECTOR(ineis_out)[b]; + j = (long int) IGRAPH_OTHER(graph, edge, i); + 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 */ + vsize = igraph_vector_size(&ineis_in); + for (b = 0; b < vsize; b++) { + edge = (igraph_integer_t) VECTOR(ineis_in)[b]; + j = (long int) IGRAPH_OTHER(graph, edge, i); + 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)); + vsize2 = igraph_vector_size(&jneis_in); + for (c = 0; c < vsize2; c++) { + edge2 = (igraph_integer_t) VECTOR(jneis_in)[c]; + q = (long int) IGRAPH_OTHER(graph, edge2, j); + 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)) { + vsize2 = igraph_vector_size(&jneis_out); + for (c = 0; c < vsize2; c++) { + edge2 = (igraph_integer_t) VECTOR(jneis_out)[c]; + q = (long int) IGRAPH_OTHER(graph, edge2, j); + 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)) { + vsize = igraph_vector_size(&ineis_out); + for (b = 0; b < vsize; b++) { + edge = (igraph_integer_t) VECTOR(ineis_out)[b]; + j = (long int) IGRAPH_OTHER(graph, edge, i); + 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)); + vsize2 = igraph_vector_size(&jneis_in); + for (c = 0; c < vsize2; c++) { + edge2 = (igraph_integer_t) VECTOR(jneis_in)[c]; + q = (long int) IGRAPH_OTHER(graph, edge2, j); + 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]; + } + } + } + vsize2 = igraph_vector_size(&jneis_out); + for (c = 0; c < vsize2; c++) { + edge2 = (igraph_integer_t) VECTOR(jneis_out)[c]; + q = (long int) IGRAPH_OTHER(graph, edge2, j); + 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 */ + vsize = igraph_vector_size(&ineis_in); + for (b = 0; b < vsize; b++) { + edge = (igraph_integer_t) VECTOR(ineis_in)[b]; + j = (long int) IGRAPH_OTHER(graph, edge, i); + if (i == j) { + continue; + } + VECTOR(*res)[a] += VECTOR(contrib)[j] * VECTOR(contrib)[j]; + VECTOR(contrib)[j] = 0.0; + } + if (igraph_is_directed(graph)) { + vsize = igraph_vector_size(&ineis_out); + for (b = 0; b < vsize; b++) { + edge = (igraph_integer_t) VECTOR(ineis_out)[b]; + j = (long int) IGRAPH_OTHER(graph, edge, i); + 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(°ree); + igraph_vector_destroy(&contrib); + IGRAPH_FINALLY_CLEAN(7); + + return 0; +} diff --git a/src/rigraph/core/properties/convergence_degree.c b/src/rigraph/core/properties/convergence_degree.c new file mode 100644 index 0000000..8230b5e --- /dev/null +++ b/src/rigraph/core/properties/convergence_degree.c @@ -0,0 +1,208 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2005-2021 The igraph development 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., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_centrality.h" + +#include "igraph_adjlist.h" +#include "igraph_dqueue.h" +#include "igraph_interface.h" +#include "igraph_memory.h" + +#include "core/interruption.h" + +#include + +/** + * \function igraph_convergence_degree + * \brief Calculates the convergence degree of each edge in a graph. + * + * Let us define the input set of an edge (i, j) as the set of vertices where + * the shortest paths passing through (i, j) originate, and similarly, let us + * defined the output set of an edge (i, j) as the set of vertices where the + * shortest paths passing through (i, j) terminate. The convergence degree of + * an edge is defined as the normalized value of the difference between the + * size of the input set and the output set, i.e. the difference of them + * divided by the sum of them. Convergence degrees are in the range (-1, 1); a + * positive value indicates that the edge is \em convergent since the shortest + * paths passing through it originate from a larger set and terminate in a + * smaller set, while a negative value indicates that the edge is \em divergent + * since the paths originate from a small set and terminate in a larger set. + * + * + * Note that the convergence degree as defined above does not make sense in + * undirected graphs as there is no distinction between the input and output + * set. Therefore, for undirected graphs, the input and output sets of an edge + * are determined by orienting the edge arbitrarily while keeping the remaining + * edges undirected, and then taking the absolute value of the convergence + * degree. + * + * \param graph The input graph, it can be either directed or undirected. + * \param result Pointer to an initialized vector; the convergence degrees of + * each edge will be stored here. May be \c NULL if we are not interested in + * the exact convergence degrees. + * \param ins Pointer to an initialized vector; the size of the input set of + * each edge will be stored here. May be \c NULL if we are not interested in + * the sizes of the input sets. + * \param outs Pointer to an initialized vector; the size of the output set of + * each edge will be stored here. May be \c NULL if we are not interested in + * the sizes of the output sets. + * \return Error code. + * + * Time complexity: O(|V||E|), the number of vertices times the number of edges. + */ +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 and output 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_LOOPS_ONCE)); + 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; +} diff --git a/src/rigraph/core/properties/dag.c b/src/rigraph/core/properties/dag.c new file mode 100644 index 0000000..6963209 --- /dev/null +++ b/src/rigraph/core/properties/dag.c @@ -0,0 +1,296 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2005-2021 The igraph development 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., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_topology.h" + +#include "igraph_constructors.h" +#include "igraph_dqueue.h" +#include "igraph_interface.h" +#include "igraph_stack.h" + +/** + * \function igraph_topological_sorting + * \brief Calculate a possible topological sorting of the graph. + * + * + * A topological sorting of a directed acyclic graph (DAG) is a linear ordering + * of its vertices where each vertex comes before all nodes to which it has + * edges. Every DAG has at least one topological sort, and may have many. + * This function returns one possible topological sort among them. If the + * graph contains any cycles that are not self-loops, an error is raised. + * + * \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 vertex comes + * before all vertices to which it has edges, so vertices with no incoming + * edges go first. For \c IGRAPH_IN, it is quite the opposite: each + * vertex comes before all vertices from which it receives edges. Vertices + * 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 acyclic. + * + * \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(°rees, 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, °rees, 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_ERROR("The graph has cycles; topological sorting is only possible in acyclic graphs", IGRAPH_EINVAL); + } + + igraph_vector_destroy(°rees); + igraph_vector_destroy(&neis); + igraph_dqueue_destroy(&sources); + IGRAPH_FINALLY_CLEAN(3); + + return 0; +} + +/** + * \function igraph_is_dag + * \brief Checks whether a graph is a directed acyclic graph (DAG). + * + * + * 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(°rees, 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, °rees, 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(°rees); + igraph_vector_destroy(&neis); + igraph_dqueue_destroy(&sources); + IGRAPH_FINALLY_CLEAN(3); + + return IGRAPH_SUCCESS; +} + +/* 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(°, 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, °, 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(°); + 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; +} diff --git a/src/rigraph/core/properties/degrees.c b/src/rigraph/core/properties/degrees.c new file mode 100644 index 0000000..1bfa21f --- /dev/null +++ b/src/rigraph/core/properties/degrees.c @@ -0,0 +1,500 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2005-2021 The igraph development 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., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_structural.h" + +#include "igraph_interface.h" + +/** + * \function igraph_maxdegree + * \brief The maximum degree in a graph (or set of vertices). + * + * + * The largest in-, out- or total degree of the specified vertices is + * calculated. If the graph has no vertices, or \p vids is empty, + * 0 is returned, as this is the smallest possible value for degrees. + * + * \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 + * of 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_CHECK(igraph_degree(graph, &tmp, vids, mode, loops)); + if (igraph_vector_size(&tmp) == 0) { + *res = 0; + } else { + *res = (igraph_integer_t) igraph_vector_max(&tmp); + } + + igraph_vector_destroy(&tmp); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + +static 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(°, no_of_nodes); + IGRAPH_CHECK(igraph_degree(graph, °, 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(°hist, (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; + } + } + + igraph_vector_destroy(&edge_neis); + igraph_vector_destroy(&neis); + IGRAPH_FINALLY_CLEAN(2); + + 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(°hist); + IGRAPH_FINALLY_CLEAN(1); + } + + igraph_vector_destroy(&strength); + igraph_vector_destroy(°); + 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 neighbor degree. + * + * Calculates the average degree of the neighbors for each vertex (\p knn), and + * optionally, the same quantity as a function of the vertex degree (\p knnk). + * + * + * For isolated vertices \p knn is set to NaN. + * The same is done in \p knnk for vertex degrees that + * don't appear in the graph. + * + * + * The weighted version computes a weighted average of the neighbor degrees as + * + * k_nn_u = 1/s_u sum_v w_uv k_v, + * + * where s_u = sum_v w_uv is the sum of the incident edge weights + * of vertex \c u, i.e. its strength. + * The sum runs over the neighbors \c v of vertex \c u + * as indicated by \p mode. w_uv denotes the weighted adjacency matrix + * and k_v is the neighbors' degree, specified by \p neighbor_degree_mode. + * + * + * Reference: + * A. Barrat, M. Barthélemy, R. Pastor-Satorras, and A. Vespignani, + * The architecture of complex weighted networks, + * Proc. Natl. Acad. Sci. USA 101, 3747 (2004). + * https://dx.doi.org/10.1073/pnas.0400087101 + * + * \param graph The input graph. It may be directed. + * \param vids The vertices for which the calculation is performed. + * \param mode The type of neighbors to consider in directed graphs. + * \c IGRAPH_OUT considers out-neighbors, \c IGRAPH_IN in-neighbors + * and \c IGRAPH_ALL ignores edge directions. + * \param neighbor_degree_mode The type of degree to average in directed graphs. + * \c IGRAPH_OUT averages out-degrees, \c IGRAPH_IN averages in-degrees + * and \c IGRAPH_ALL ignores edge directions for the degree calculation. + * \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 \c NULL pointer + * here, if you only want to calculate \c knnk. + * \param knnk Pointer to an initialized vector, the average + * neighbor degree as a function of the vertex degree is stored + * here. The first (zeroth) element is for degree one vertices, + * etc. Supply a \c 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. + * + * \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(°, no_of_nodes); + IGRAPH_CHECK(igraph_degree(graph, °, igraph_vss_all(), + neighbor_degree_mode, IGRAPH_LOOPS)); + IGRAPH_CHECK(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(°hist, (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(°hist); + IGRAPH_FINALLY_CLEAN(1); + } + + igraph_vector_destroy(&neis); + igraph_vector_destroy(°); + 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_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(°rees, 0); + IGRAPH_CHECK(igraph_degree(graph, °rees, vids, mode, loops)); + IGRAPH_CHECK((int) igraph_vector_qsort_ind(°rees, outvids, + order == IGRAPH_DESCENDING)); + if (only_indices || igraph_vs_is_all(&vids) ) { + igraph_vector_destroy(°rees); + 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(°rees); + IGRAPH_FINALLY_CLEAN(2); + } + return 0; +} diff --git a/src/rigraph/core/properties/girth.c b/src/rigraph/core/properties/girth.c new file mode 100644 index 0000000..18bd02c --- /dev/null +++ b/src/rigraph/core/properties/girth.c @@ -0,0 +1,208 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2005-2021 The igraph development 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., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_structural.h" + +#include "igraph_adjlist.h" +#include "igraph_components.h" +#include "igraph_dqueue.h" +#include "igraph_interface.h" + +#include "core/interruption.h" + +#include + +/** + * \function igraph_girth + * \brief The girth of a graph is the length of the shortest cycle in it. + * + * + * The current implementation works for undirected graphs only, + * directed graphs are treated as undirected graphs. Self-loops and + * multiple edges are ignored. + * + * + * For graphs that contain no cycles, and only for such graphs, + * zero is returned. Note that in some applications, it is customary + * to define the girth of acyclic graphs to be infinity. However, infinity + * is not representable as an \c igraph_integer_t, therefore zero is used + * for this case. + * + * + * 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 + * cycles 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_int_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_NO_LOOPS, IGRAPH_NO_MULTIPLE)); + 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_int_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_int_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; +} diff --git a/src/rigraph/core/properties/loops.c b/src/rigraph/core/properties/loops.c new file mode 100644 index 0000000..7728d57 --- /dev/null +++ b/src/rigraph/core/properties/loops.c @@ -0,0 +1,95 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2005-2021 The igraph development 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., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_structural.h" + +#include "igraph_interface.h" + +/** + * \function igraph_has_loop + * \brief Returns whether the graph has at least one loop edge. + * + * + * 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. + * + * + * 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; +} diff --git a/src/rigraph/core/properties/multiplicity.c b/src/rigraph/core/properties/multiplicity.c new file mode 100644 index 0000000..20c8304 --- /dev/null +++ b/src/rigraph/core/properties/multiplicity.c @@ -0,0 +1,356 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2005-2012 Gabor Csardi + 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_adjlist.h" +#include "igraph_interface.h" + +/** + * \function igraph_is_simple + * \brief Decides whether the input graph is a simple graph. + * + * + * 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_CHECK(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_multiple + * \brief Check whether the graph has at least one multiple edge. + * + * + * 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. + * + * + * An edge is a multiple edge if there is another + * edge with the same head and tail vertices in the graph. + * + * + * 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, j, n; + 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_LOOPS_ONCE)); + 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_int_t *neis = + igraph_lazy_inclist_get(&inclist, (igraph_integer_t) from); + + if (neis == 0) { + /* Most likely out of memory */ + IGRAPH_ERROR("Out of memory while building lazy incidence list", IGRAPH_ENOMEM); + } + + VECTOR(*res)[i] = 0; + + n = igraph_vector_int_size(neis); + 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. + * + * + * 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.) + * + * + * \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, j, n; + 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_LOOPS_ONCE)); + 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_int_t *neis = + igraph_lazy_inclist_get(&inclist, (igraph_integer_t) from); + + if (neis == 0) { + /* Most likely out of memory */ + IGRAPH_ERROR("Out of memory while building lazy incidence list", IGRAPH_ENOMEM); + } + + VECTOR(*res)[i] = 0; + + n = igraph_vector_int_size(neis); + 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; + } + } + } + + igraph_lazy_inclist_destroy(&inclist); + igraph_eit_destroy(&eit); + IGRAPH_FINALLY_CLEAN(2); + + return IGRAPH_SUCCESS; +} + +/** + * \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. + * + * + * An undirected graph only has mutual edges, by definition. + * + * + * 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. + * + * + * Loops are always 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 + * igraph_ess_all() 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(const 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 IGRAPH_SUCCESS; + } + + IGRAPH_CHECK(igraph_lazy_adjlist_init(graph, &adjlist, IGRAPH_OUT, IGRAPH_LOOPS_ONCE, IGRAPH_MULTIPLE)); + 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_int_t *neis = igraph_lazy_adjlist_get(&adjlist, + (igraph_integer_t) to); + if (igraph_vector_int_empty(neis)) { + VECTOR(*res)[i] = 0; + } else { + VECTOR(*res)[i] = igraph_vector_int_binsearch2(neis, from); + } + } + + igraph_lazy_adjlist_destroy(&adjlist); + igraph_eit_destroy(&eit); + IGRAPH_FINALLY_CLEAN(2); + + return IGRAPH_SUCCESS; +} diff --git a/src/rigraph/core/properties/neighborhood.c b/src/rigraph/core/properties/neighborhood.c new file mode 100644 index 0000000..5a65415 --- /dev/null +++ b/src/rigraph/core/properties/neighborhood.c @@ -0,0 +1,454 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2005-2021 The igraph development 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., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_neighborhood.h" + +#include "igraph_dqueue.h" +#include "igraph_interface.h" +#include "igraph_memory.h" +#include "igraph_operators.h" + +/** + * \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. I.e., 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. + * + * + * 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. + * Vertices reachable with a path shorter than this value are excluded. + * 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_ERRORF("Negative order in neighborhood size: %" IGRAPH_PRId ".", + IGRAPH_EINVAL, order); + } + + if (mindist < 0 || mindist > order) { + IGRAPH_ERRORF("Minimum distance should be between 0 and the neighborhood order (%" IGRAPH_PRId "), got %" IGRAPH_PRId ".", + IGRAPH_EINVAL, order, mindist); + } + + 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 IGRAPH_SUCCESS; +} + +/** + * \function igraph_neighborhood + * \brief 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. I.e., 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. + * + * + * 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 \ref igraph_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 \p order steps are included. For \c IGRAPH_IN + * all vertices from which the source vertex is reachable in at most + * \p 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. + * Vertices reachable with a path shorter than this value are excluded. + * 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 IGRAPH_SUCCESS; +} + +/** + * \function igraph_neighborhood_graphs + * \brief 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. + * + * + * This function finds every vertex in the neighborhood + * of a given parameter vertex and creates the induced subgraph from these + * vertices. + * + * + * 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. \p 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 \p order steps are counted. For \c IGRAPH_IN + * all vertices from which the source vertex is reachable in at most + * \p 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. + * Vertices reachable with a path shorter than this value are excluded. + * 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 IGRAPH_SUCCESS; +} diff --git a/src/rigraph/core/properties/properties_internal.h b/src/rigraph/core/properties/properties_internal.h new file mode 100644 index 0000000..f3daab8 --- /dev/null +++ b/src/rigraph/core/properties/properties_internal.h @@ -0,0 +1,31 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2011-2021 The igraph development 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, see . +*/ + +#ifndef IGRAPH_PROPERTIES_INTERNAL_H +#define IGRAPH_PROPERTIES_INTERNAL_H + +#include "igraph_adjlist.h" +#include "igraph_constants.h" +#include "igraph_iterators.h" +#include "igraph_types.h" + +int igraph_i_trans4_al_simplify(igraph_adjlist_t *al, + const igraph_vector_int_t *rank); + +#endif diff --git a/src/rigraph/core/properties/spectral.c b/src/rigraph/core/properties/spectral.c new file mode 100644 index 0000000..8bd78b3 --- /dev/null +++ b/src/rigraph/core/properties/spectral.c @@ -0,0 +1,436 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2006-2012 Gabor Csardi + 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 + +static 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(°ree, 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(°ree); + igraph_eit_destroy(&edgeit); + IGRAPH_FINALLY_CLEAN(2); + + return 0; +} + +/** + * \function igraph_laplacian + * \brief Returns the Laplacian matrix of a graph + * + * + * 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. + * + * + * 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. + * + * + * 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(°ree, no_of_nodes); + + IGRAPH_CHECK(igraph_degree(graph, °ree, 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(°ree); + igraph_eit_destroy(&edgeit); + IGRAPH_FINALLY_CLEAN(2); + return 0; +} diff --git a/src/rigraph/core/properties/trees.c b/src/rigraph/core/properties/trees.c new file mode 100644 index 0000000..e2bc8ea --- /dev/null +++ b/src/rigraph/core/properties/trees.c @@ -0,0 +1,351 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2005-2021 The igraph development 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., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_structural.h" + +#include "igraph_adjlist.h" +#include "igraph_constructors.h" +#include "igraph_dqueue.h" +#include "igraph_interface.h" +#include "igraph_stack.h" + +/** + * \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 + * + * 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. + * + * + * 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_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; + } + + /* 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(°ree, 0)); + IGRAPH_FINALLY(igraph_vector_destroy, °ree); + + IGRAPH_CHECK(igraph_degree(graph, °ree, 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 (VECTOR(degree)[i] > 1) { + /* In an out-tree, all vertices have in-degree 1, except for the root, + * which has in-degree 0. Thus, if we encounter a larger in-degree, + * the graph cannot be an out-tree. + * We could perform this check for all degrees, but that would not + * improve performance when the graph is indeed a tree, persumably + * the most common case. Thus we only check until finding the root. + */ + *res = 0; + break; + } + } + + /* If no suitable root is found, the graph is not a tree. */ + if (*res && i == vcount) { + *res = 0; + } else { + iroot = i; + } + + igraph_vector_destroy(°ree); + IGRAPH_FINALLY_CLEAN(1); + } + + break; + default: + IGRAPH_ERROR("Invalid mode,", IGRAPH_EINVMODE); + } + + /* if no suitable root was found, skip visiting vertices */ + if (*res) { + IGRAPH_CHECK(igraph_i_is_tree_visitor(graph, iroot, mode, &visited_count)); + *res = visited_count == vcount; + } + + if (root) { + *root = iroot; + } + + return IGRAPH_SUCCESS; +} diff --git a/src/rigraph/core/properties/triangles.c b/src/rigraph/core/properties/triangles.c new file mode 100644 index 0000000..99aa20f --- /dev/null +++ b/src/rigraph/core/properties/triangles.c @@ -0,0 +1,925 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2005-2012 Gabor Csardi + 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_motifs.h" +#include "igraph_structural.h" + +#include "core/interruption.h" +#include "properties/properties_internal.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. + * Edge directions and edge multiplicities are ignored. + * + * + * 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. + * + * + * Clustering coefficient is an alternative name for transitivity. + * + * + * References: + * + * + * D. J. Watts and S. Strogatz: Collective dynamics of small-world networks. + * Nature 393(6684):440-442 (1998). + * + * \param graph The input graph. Edge directions and multiplicites are ignored. + * \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) { + + igraph_integer_t i, no_of_nodes = igraph_vcount(graph), nans = 0; + igraph_real_t sum = 0.0; + igraph_vector_t vec; + + if (no_of_nodes == 0) { + if (mode == IGRAPH_TRANSITIVITY_ZERO) { + *res = 0; + } else { + *res = IGRAPH_NAN; + } + } else { + IGRAPH_VECTOR_INIT_FINALLY(&vec, no_of_nodes); + + IGRAPH_CHECK(igraph_transitivity_local_undirected(graph, &vec, igraph_vss_all(), mode)); + + for (i = 0, nans = 0; i < no_of_nodes; i++) { + if (!igraph_is_nan(VECTOR(vec)[i])) { + sum += VECTOR(vec)[i]; + } else { + nans++; + } + } + + igraph_vector_destroy(&vec); + IGRAPH_FINALLY_CLEAN(1); + + *res = sum / (no_of_nodes - nans); + } + + return IGRAPH_SUCCESS; +} + +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 "properties/triangles_template1.h" +#undef TRANSIT + + return IGRAPH_SUCCESS; +} + +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_NO_LOOPS, IGRAPH_NO_MULTIPLE)); + 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_int_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_int_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(°ree, affected_nodes); + for (i = 0; i < affected_nodes; i++) { + long int v = (long int) VECTOR(avids)[i]; + igraph_vector_int_t *neis2; + long int deg; + neis2 = igraph_lazy_adjlist_get(&adjlist, (igraph_integer_t) v); + VECTOR(degree)[i] = deg = igraph_vector_int_size(neis2); + if (deg > maxdegree) { + maxdegree = deg; + } + } + igraph_vector_order1(°ree, &order, maxdegree + 1); + igraph_vector_destroy(°ree); + 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("Insufficient memory for local transitivity calculation.", 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_int_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_int_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_int_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_int_t *neis2 = + igraph_lazy_adjlist_get(&adjlist, (igraph_integer_t) node); + long int deg = igraph_vector_int_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_NO_LOOPS, IGRAPH_NO_MULTIPLE)); */ +/* 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 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. */ +/* Note: Also used in scan.c */ +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, + igraph_transitivity_mode_t mode) { + +#define TRANSIT 1 +#include "properties/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. + * + * + * 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. + * + * + * Clustering coefficient is an alternative name for transitivity. + * + * + * References: + * + * + * D. J. Watts and S. Strogatz: Collective dynamics of small-world networks. + * Nature 393(6684):440-442 (1998). + * + * \param graph The input graph. Edge directions and multiplicities are ignored. + * \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) { + + if (igraph_vs_is_all(&vids)) { + return igraph_transitivity_local_undirected4(graph, res, 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); + } + } +} + +static int igraph_adjacent_triangles1(const igraph_t *graph, + igraph_vector_t *res, + const igraph_vs_t vids) { +# include "properties/triangles_template1.h" + return 0; +} + +static int igraph_adjacent_triangles4(const igraph_t *graph, + igraph_vector_t *res) { +# include "properties/triangles_template.h" + return 0; +} + +/** + * \function igraph_adjacent_triangles + * \brief Count the number of triangles a vertex is part of. + * + * \param graph The input graph. Edge directions and multiplicities 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); + } +} + +/** + * \function igraph_list_triangles + * \brief Find all triangles in a graph. + * + * \param graph The input graph, edge directions are ignored. + * Multiple edges 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 "properties/triangles_template.h" +# undef TRIANGLES + return IGRAPH_SUCCESS; +} + +/** + * \ingroup structural + * \function igraph_transitivity_undirected + * \brief Calculates the transitivity (clustering coefficient) of a graph. + * + * + * 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 + * and multi-edges are ignored. + * + * + * 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. + * + * + * Clustering coefficient is an alternative name for transitivity. + * + * + * References: + * + * + * S. Wasserman and K. Faust: Social Network Analysis: Methods and + * Applications. Cambridge: Cambridge University Press, 1994. + * + * \param graph The graph object. Edge directions and multiplicites are ignored. + * \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; + + if (no_of_nodes == 0) { + *res = mode == IGRAPH_TRANSITIVITY_ZERO ? 0.0 : IGRAPH_NAN; + return IGRAPH_SUCCESS; + } + + IGRAPH_VECTOR_INIT_FINALLY(&order, no_of_nodes); + IGRAPH_VECTOR_INIT_FINALLY(°ree, no_of_nodes); + + IGRAPH_CHECK(igraph_degree(graph, °ree, igraph_vss_all(), IGRAPH_ALL, + IGRAPH_LOOPS)); + maxdegree = (long int) igraph_vector_max(°ree) + 1; + IGRAPH_CHECK(igraph_vector_order1(°ree, &order, maxdegree)); + + igraph_vector_destroy(°ree); + 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_NO_LOOPS, IGRAPH_NO_MULTIPLE)); + IGRAPH_FINALLY(igraph_adjlist_destroy, &allneis); + + neis = IGRAPH_CALLOC(no_of_nodes, long int); + if (! neis) { + IGRAPH_ERROR("Insufficient memory for undirected global transitivity.", 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; +} + +static int igraph_i_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); + igraph_vit_t vit; + long int nodes_to_calc; + igraph_vector_int_t *adj1, *adj2; + igraph_vector_long_t neis; + igraph_vector_t actw; + igraph_lazy_inclist_t incident; + long int i; + igraph_vector_t strength; + + /* Precondition: weight vector is not null, its length equals the number of + * edges, and the graph has at least one vertex. The graph must not have + * multi-edges. These must be ensured by the caller. + */ + + 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_CHECK(igraph_lazy_inclist_init(graph, &incident, IGRAPH_ALL, IGRAPH_LOOPS_TWICE)); + 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_int_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_int_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 IGRAPH_SUCCESS; +} + +static int igraph_i_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); + 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; + + /* Precondition: weight vector is not null, its length equals the number of + * edges, and the graph has at least one vertex. The graph must not have + * multi-edges. These must be ensured by the caller. + */ + + IGRAPH_VECTOR_INIT_FINALLY(&order, no_of_nodes); + IGRAPH_VECTOR_INIT_FINALLY(°ree, no_of_nodes); + + IGRAPH_CHECK(igraph_degree(graph, °ree, igraph_vss_all(), IGRAPH_ALL, + IGRAPH_LOOPS)); + maxdegree = (long int) igraph_vector_max(°ree) + 1; + IGRAPH_CHECK(igraph_vector_order1(°ree, &order, maxdegree)); + + IGRAPH_CHECK(igraph_strength(graph, °ree, 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_LOOPS_TWICE)); + 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(°ree); + igraph_vector_destroy(&order); + IGRAPH_FINALLY_CLEAN(6); + + return IGRAPH_SUCCESS; +} + +/** + * \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 \em 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) { + long int no_of_nodes = igraph_vcount(graph); + long int no_of_edges = igraph_ecount(graph); + igraph_bool_t has_multiple; + + /* Handle fallback to unweighted version and common cases */ + if (!weights) { + if (no_of_edges != 0) { + 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_ERRORF("Edge weight vector length (%ld) not equal to " + "number of edges (%ld).", IGRAPH_EINVAL, + igraph_vector_size(weights), no_of_edges); + } + + if (no_of_nodes == 0) { + igraph_vector_clear(res); + return IGRAPH_SUCCESS; + } + + IGRAPH_CHECK(igraph_has_multiple(graph, &has_multiple)); + if (has_multiple) { + IGRAPH_ERROR( + "Barrat's weighted transitivity measure works only if the graph " + "has no multiple edges.", IGRAPH_EINVAL + ); + } + + /* Preconditions validated, now we can call the real implementation */ + + if (igraph_vs_is_all(&vids)) { + return igraph_i_transitivity_barrat4(graph, res, vids, weights, mode); + } else { + return igraph_i_transitivity_barrat1(graph, res, vids, weights, mode); + } +} diff --git a/src/rigraph/core/properties/triangles_template.h b/src/rigraph/core/properties/triangles_template.h new file mode 100644 index 0000000..61418fd --- /dev/null +++ b/src/rigraph/core/properties/triangles_template.h @@ -0,0 +1,143 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2005-2012 Gabor Csardi + 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 TRANSIT +#define TRANSIT_TRIEDGES +#endif + +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; +long int *neis; +long int maxdegree; + +#ifdef TRANSIT_TRIEDGES +long int deg1; +#endif + +igraph_vector_int_t order; +igraph_vector_int_t rank; +igraph_vector_t degree; + +if (no_of_nodes == 0) { +#ifndef TRIANGLES + igraph_vector_clear(res); +#else + igraph_vector_int_clear(res); +#endif + return IGRAPH_SUCCESS; +} + +igraph_vector_int_init(&order, no_of_nodes); +IGRAPH_FINALLY(igraph_vector_int_destroy, &order); +IGRAPH_VECTOR_INIT_FINALLY(°ree, no_of_nodes); + +IGRAPH_CHECK(igraph_adjlist_init(graph, &allneis, IGRAPH_ALL, IGRAPH_NO_LOOPS, IGRAPH_NO_MULTIPLE)); +IGRAPH_FINALLY(igraph_adjlist_destroy, &allneis); + +for (i = 0; i < no_of_nodes; i++) { + VECTOR(degree)[i] = igraph_vector_int_size(igraph_adjlist_get(&allneis, i)); +} + +maxdegree = (long int) igraph_vector_max(°ree) + 1; +igraph_vector_order1_int(°ree, &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_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); + +#ifdef TRANSIT_TRIEDGES + deg1 = (long int) VECTOR(degree)[node]; +#endif + + /* 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 +} + +igraph_free(neis); +igraph_adjlist_destroy(&allneis); +igraph_vector_int_destroy(&rank); +igraph_vector_destroy(°ree); +igraph_vector_int_destroy(&order); +IGRAPH_FINALLY_CLEAN(5); + +#ifdef TRANSIT_TRIEDGES +#undef TRANSIT_TRIEDGES +#endif diff --git a/src/rigraph/core/properties/triangles_template1.h b/src/rigraph/core/properties/triangles_template1.h new file mode 100644 index 0000000..3ab191f --- /dev/null +++ b/src/rigraph/core/properties/triangles_template1.h @@ -0,0 +1,95 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2005-2012 Gabor Csardi + 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_int_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); + +if (nodes_to_calc == 0) { + igraph_vector_clear(res); + igraph_vit_destroy(&vit); + IGRAPH_FINALLY_CLEAN(1); + return IGRAPH_SUCCESS; +} + +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_CHECK(igraph_lazy_adjlist_init(graph, &adjlist, IGRAPH_ALL, IGRAPH_NO_LOOPS, IGRAPH_NO_MULTIPLE)); +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_int_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_int_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/src/rigraph/core/random/random.c b/src/rigraph/core/random/random.c new file mode 100644 index 0000000..5cb0753 --- /dev/null +++ b/src/rigraph/core/random/random.c @@ -0,0 +1,2284 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2005-2012 Gabor Csardi + 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_nongraph.h" +#include "igraph_error.h" +#include "igraph_types.h" +#include "igraph_vector.h" +#include "igraph_memory.h" + +#include "core/math.h" + +#include "config.h" +#include +#include + +/** + * \section about_rngs + * + *
+ * About random numbers in igraph, use cases + * + * + * 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. + * + *
+ * + */ + +/** + * \section rng_use_cases + * + *
Use cases + * + *
Normal (default) use + * + * 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 time(0) function + * call, using the time function from the standard C + * library. This ensures that igraph creates a different random graph, + * each time the C program is called. + * + * + * + * The created default generator is stored internally and can be + * queried with the \ref igraph_rng_default() function. + * + *
+ * + *
Reproducible simulations + * + * 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). + * + *
+ * + *
Changing the default generator + * + * 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. + * + *
+ * + *
Using multiple generators + * + * 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. + * + * + * + * 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(). + * + *
+ * + *
Example + * + * \example examples/simple/random_seed.c + * + *
+ * + *
+ */ + +/* ------------------------------------ */ + +typedef struct { + int i, j; + long int x[31]; +} igraph_i_rng_glibc2_state_t; + +static 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; +} + +static 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); +} + +static 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 */ + +static 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 ; + } +} + +static 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 IGRAPH_SUCCESS; +} + +static 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 IGRAPH_SUCCESS; +} + +static 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 introduced in GNU libc 2. + * + * This 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. It is a + * reimplementation and does not call the system glibc generator. + */ + +const igraph_rng_type_t igraph_rngtype_glibc2 = { + /* name= */ "LIBC", + /* min= */ 0, + /* max= */ 0x7fffffffUL, + /* 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; + +static 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; +} + +static igraph_real_t igraph_rng_rand_get_real(void *vstate) { + return igraph_rng_rand_get (vstate) / 2147483648.0 ; +} + +static int igraph_rng_rand_seed(void *vstate, unsigned long int seed) { + igraph_i_rng_rand_state_t *state = vstate; + state->x = seed; + return IGRAPH_SUCCESS; +} + +static 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 IGRAPH_SUCCESS; +} + +static 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/srand 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; + +static 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; +} + +static igraph_real_t igraph_rng_mt19937_get_real(void *vstate) { + return igraph_rng_mt19937_get (vstate) / 4294967296.0 ; +} + +static 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 IGRAPH_SUCCESS; +} + +static 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 IGRAPH_SUCCESS; +} + +static 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 \c s equal to zero in + * \c gsl_rng_set reproduces this. Later versions switched to 5489 as the + * default seed, you can choose this explicitly via \ref 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 \c 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 + +/* ------------------------------------ */ + +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 + * \brief 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 in crashes and/or + * unpredictable results. + * + * Time complexity: O(1). + */ + +void igraph_rng_set_default(igraph_rng_t *rng) { + igraph_i_rng_default = (*rng); +} + + +/* ------------------------------------ */ + +/** + * \function igraph_rng_default + * \brief 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; +} + +/* ------------------------------------ */ + +static double igraph_norm_rand(igraph_rng_t *rng); +static double igraph_rgeom(igraph_rng_t *rng, double p); +static double igraph_rbinom(igraph_rng_t *rng, double nin, double pp); +static double igraph_rexp(igraph_rng_t *rng, double rate); +static double igraph_rgamma(igraph_rng_t *rng, double shape, double scale); + +/** + * \function igraph_rng_init + * \brief 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, like \ref igraph_rngtype_glibc2, + * \ref igraph_rngtype_mt19937 or \ref igraph_rngtype_rand. + * \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 IGRAPH_SUCCESS; +} + +/** + * \function igraph_rng_destroy + * \brief 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 + * \brief 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 IGRAPH_SUCCESS; +} + +/** + * \function igraph_rng_max + * \brief 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 + * \brief Query the minimum possible integer for a random number generator. + * + * This function will be removed in a future version. Assume zero + * as the retun value. + * + * \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). + * + * \deprecated 0.9.3 + */ + +unsigned long int igraph_rng_min(igraph_rng_t *rng) { + const igraph_rng_type_t *type = rng->type; + IGRAPH_WARNING("igraph_rng_min() is deprecated; assume 0 as the return value."); + return type->min; +} + +/** + * \function igraph_rng_name + * \brief 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 + * \brief 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 l. + * \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; + /* We require the random integer to be in the range [l, h]. We do so by + * first casting (truncate toward zero) to the range [0, h - l] and then add + * l to arrive at the range [l, h]. That is, we calculate + * (long)( r * (h - l + 1) ) + l + * instead of + * (long)( r * (h - l + 1) + l), + * please note the difference in the parentheses. + * + * In the latter formulation, if l is negative, this would incorrectly lead + * to the range [l + 1, h] instead of the desired [l, h] because negative + * numbers are truncated towards zero when cast. For example, if l = -5, any + * real in the range (-5, -4] would get cast to -4, not to -5. + */ + 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_FATAL("Internal random generator error"); +} + +/** + * \function igraph_rng_get_normal + * \brief 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 + * \brief 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_FATAL("Internal random generator error"); +} + +/** + * \function igraph_rng_get_unif01 + * \brief 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_FATAL("Internal random generator error"); +} + +/** + * \function igraph_rng_get_geom + * \brief 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 + * \brief 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 + * \brief 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); + } +} + + +/* + * \ingroup internal + * + * This function appends the rest of the needed random number to the + * result vector. + */ + +static 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 IGRAPH_SUCCESS; +} + +/** + * \ingroup nongraph + * \function igraph_random_sample + * \brief Generates an increasing random sequence of integers. + * + * + * 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. + * + * 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 > \c h. (2) Assuming that + * \c l < \c h and N is the sample size, the above error code is + * returned if N > |\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). + * + * + * 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 IGRAPH_SUCCESS; + } + if (length == 0) { + igraph_vector_clear(res); + return IGRAPH_SUCCESS; + } + 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 IGRAPH_SUCCESS; + } + + 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; +} + +/* + * 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; +} + +static 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)); +} + +static int imax2(int x, int y) { + return (x < y) ? y : x; +} + +static int imin2(int x, int y) { + return (x < y) ? x : y; +} + +static 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 + * 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 + * 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) */ + +static 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 (!igraph_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 + +static double igraph_rgeom(igraph_rng_t *rng, double p) { + if (igraph_is_nan(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(;;) + +static 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 (!igraph_finite(nin)) { + ML_ERR_return_NAN; + } + n = floor(nin + 0.5); + if (n != nin) { + ML_ERR_return_NAN; + } + + if (!igraph_finite(pp) || + /* n=0, p=0, p=1 are not errors */ + 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; +} + +static 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 (!igraph_finite(sigma)) { + return R_D__0; + } + if (!igraph_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 (!igraph_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 + * 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 + */ + +static double igraph_rgamma(igraph_rng_t *rng, double a, double scale) { + /* Constants : */ + static const double sqrt32 = 5.656854; + static const 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) + */ + static const double q1 = 0.04166669; + static const double q2 = 0.02083148; + static const double q3 = 0.00801191; + static const double q4 = 0.00144121; + static const double q5 = -7.388e-5; + static const double q6 = 2.4511e-4; + static const double q7 = 2.424e-4; + + static const double a1 = 0.3333333; + static const double a2 = -0.250003; + static const double a3 = 0.2000062; + static const double a4 = -0.1662921; + static const double a5 = 0.1423657; + static const double a6 = -0.1367177; + static const 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 (!igraph_finite(a) || !igraph_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; +} + +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 IGRAPH_SUCCESS; +} diff --git a/src/rigraph/core/scg/scg.c b/src/rigraph/core/scg/scg.c new file mode 100644 index 0000000..ab435d1 --- /dev/null +++ b/src/rigraph/core/scg/scg.c @@ -0,0 +1,2303 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2011-12 Gabor Csardi + 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 + * + * Copyright (C) 2008 David Morton de Lachapelle + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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_community.h" +#include "igraph_constructors.h" +#include "igraph_conversion.h" +#include "igraph_memory.h" +#include "igraph_qsort.h" + +#include "misc/conversion_internal.h" + +#include "scg_headers.h" + +#include "math.h" + +/** + * \section about_scg + * + * + * The SCG functions provide a framework, called Spectral Coarse Graining + * (SCG), for reducing large graphs while preserving their + * spectral-related features, 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). + * + * + * + * 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). + * + * + * + * SCG differs from traditional clustering schemes by producing a + * coarse-grained graph (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 exact SCG, where the matrix to be + * coarse-grained is the covariance matrix of some data set. + * + * + * + * 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. + * + * + *
SCG in brief + * + * 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 + *
+ * M'=LMR*, + *
+ * where n' < 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 + *
+ * |lambda-lambda'| <= const ||e[P](v)|| + * [1+O(||e[P](v)||2)], + *
+ * 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. + *
+ * + * + * 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 + *
+ * min(||e[P](v)||, p in Omega), + *
+ * 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). + *
+ * + * + * 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 homogeneous mixing, 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 + *
+ * ||e[P](v)||2 = + * sum([v(i)-(Pv)(i)]2; + * alpha=1,...,n', i in alpha), + *
+ * where alpha denotes a group (i.e. a block) in a partition of + * {1, ..., n}, and |alpha| is the number of elements in alpha. + *
+ * + * + * 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 structural constraint + * has led to the construction of particular semi-projectors for + * symmetric [1], stochastic [3] and Laplacian [2] matrices, that are + * made available. + * + * + * + * 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 + * + *
+ * + *
Functions for performing SCG + * + * 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. + * + * + * + * 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]). + * + *
+ * + *
References + * + * [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 + * SIAM Journal on Matrix Analysis and + * Applications, 2008. + * http://people.epfl.ch/david.morton + * + * + * [2] D. Gfeller, and P. De Los Rios, Spectral Coarse Graining and + * Synchronization in Oscillator Networks. + * Physical Review Letters, + * 100(17), 2008. + * http://arxiv.org/abs/0708.2055 + * + * + * [3] D. Gfeller, and P. De Los Rios, Spectral Coarse Graining of Complex + * Networks, Physical Review Letters, + * 99(3), 2007. + * http://arxiv.org/abs/0706.0812 + * + *
+ */ + +/** + * \function igraph_scg_grouping + * \brief SCG problem solver. + * + * This function solves the Spectral Coarse Graining (SCG) problem; + * either exactly, or approximately but faster. + * + *
+ * The algorithm \c IGRAPH_SCG_OPTIMUM solves the SCG problem exactly + * 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). + * + * + * The algorithms \c IGRAPH_SCG_INTERV and \c IGRAPH_SCG_INTERV_KM solve + * the SCG problem approximately by performing a (for now) constant + * binning of the components of the eigenvectors, that is nt_vec[i] + * constant-size bins are used to partition the ith eigenvector in \c V. + * 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. + * + * + * 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. + * + * + * 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 May be (1) a numeric vector of length one, or + * (2) a vector of the same length as the number of eigenvectors given in \p V, or + * (3) 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("The p vector must be given for the stochastic matrix case", + IGRAPH_EINVAL); + } + + if (p) { + if (igraph_vector_size(p) != no_of_nodes) { + IGRAPH_ERROR("Invalid p vector size", IGRAPH_EINVAL); + } + + if (igraph_vector_min(p) < 0) { + IGRAPH_ERROR("The elements of the p vector must be non-negative", 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; + int gr_nb = 0; + + g = IGRAPH_CALLOC(no_of_nodes, igraph_i_scg_groups_t); + IGRAPH_FINALLY(igraph_free, g); + + 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); + } + + igraph_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_FINALLY_CLEAN(1); + } + + igraph_matrix_int_destroy(&gr_mat); + IGRAPH_FINALLY_CLEAN(1); + + IGRAPH_CHECK(igraph_reindex_membership(groups, 0, 0)); + + return 0; +} + +static 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; +} + +static 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; +} + +static 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. + * + * + * The symmetric semi-projectors are defined as + *
+ * L[alpha,j] = R[alpha,j] = 1/sqrt(|alpha|) delta[alpha,gamma(j)], + *
+ * the (row) Laplacian semi-projectors as + *
+ * L[alpha,j] = 1/|alpha| delta[alpha,gamma(j)] + *
+ * and + *
+ * R[alpha,j] = delta[alpha,gamma(j)], + *
+ * and the (row) stochastic semi-projectors as + *
+ * L[alpha,j] = p[1][j] / sum(p[1][k]; k in gamma(j)) + * delta[alpha,gamma(j)] + *
+ * and + *
+ * R[alpha,j] = delta[alpha,gamma(j)], + *
+ * 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 + * \brief 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_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); + + 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; +} + +static 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(°ree, n); + + if (norm == IGRAPH_SCG_NORM_ROW) { + IGRAPH_CHECK(igraph_matrix_rowsum(matrix, °ree)); + } else { + IGRAPH_CHECK(igraph_matrix_colsum(matrix, °ree)); + } + 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(°ree); + IGRAPH_FINALLY_CLEAN(1); + + return 0; +} + +static 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(°ree, 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(°ree); + IGRAPH_FINALLY_CLEAN(2); /* + mysparse */ + + return 0; +} + +static 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; +} + +static 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; +} + +static 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; +} + +static 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(). + */ + +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 + * \brief Spectral coarse graining, Laplacian 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 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(). + */ + +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; + + IGRAPH_UNUSED(direction); + + /* --------------------------------------------------------------------*/ + /* 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/src/rigraph/core/scg/scg_approximate_methods.c b/src/rigraph/core/scg/scg_approximate_methods.c new file mode 100644 index 0000000..fe8022d --- /dev/null +++ b/src/rigraph/core/scg/scg_approximate_methods.c @@ -0,0 +1,173 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2011-12 Gabor Csardi + 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 + * + * Copyright (C) 2008 David Morton de Lachapelle + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 "scg_headers.h" + +#include "igraph_error.h" +#include "igraph_types.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(¢ers, n_interv); + + igraph_i_breaks_computation(v, ¢ers, n_interv, 2); + IGRAPH_CHECK(igraph_i_kmeans_Lloyd(v, n, 1, ¢ers, n_interv, gr, + maxiter)); + + /*renumber the groups*/ + for (i = 0; i < n; i++) { + gr[i] = gr[i] - 1; + } + + igraph_vector_destroy(¢ers); + 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/src/rigraph/core/scg/scg_exact_scg.c b/src/rigraph/core/scg/scg_exact_scg.c new file mode 100644 index 0000000..6495eb7 --- /dev/null +++ b/src/rigraph/core/scg/scg_exact_scg.c @@ -0,0 +1,69 @@ +/* + * 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 + * + * Copyright (C) 2008 David Morton de Lachapelle + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 "scg_headers.h" + +#include "igraph_memory.h" +#include "igraph_qsort.h" + +#include + +int igraph_i_exact_coarse_graining(const igraph_real_t *v, + int *gr, 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; + } + + igraph_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/src/rigraph/core/scg/scg_headers.h b/src/rigraph/core/scg/scg_headers.h new file mode 100644 index 0000000..876ac31 --- /dev/null +++ b/src/rigraph/core/scg/scg_headers.h @@ -0,0 +1,128 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2011-2012 Gabor Csardi + 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 + * + * Copyright (C) 2008 David Morton de Lachapelle + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 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 "igraph_types.h" +#include "igraph_vector.h" + +#include +#include + +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, igraph_integer_t *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/src/rigraph/core/scg/scg_kmeans.c b/src/rigraph/core/scg/scg_kmeans.c new file mode 100644 index 0000000..067af30 --- /dev/null +++ b/src/rigraph/core/scg/scg_kmeans.c @@ -0,0 +1,102 @@ +/* + * 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 + * + * Copyright (C) 2008 David Morton de Lachapelle + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 "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/src/rigraph/core/scg/scg_optimal_method.c b/src/rigraph/core/scg/scg_optimal_method.c new file mode 100644 index 0000000..68eca3f --- /dev/null +++ b/src/rigraph/core/scg/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 + * + * Copyright (C) 2008 David Morton de Lachapelle + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 "scg_headers.h" + +#include "igraph_error.h" +#include "igraph_memory.h" +#include "igraph_matrix.h" +#include "igraph_vector.h" +#include "igraph_qsort.h" + +int igraph_i_optimal_partition(const igraph_real_t *v, igraph_integer_t *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; + } + + igraph_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_int_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/src/rigraph/core/scg/scg_utils.c b/src/rigraph/core/scg/scg_utils.c new file mode 100644 index 0000000..ed5500d --- /dev/null +++ b/src/rigraph/core/scg/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 + * + * Copyright (C) 2008 David Morton de Lachapelle + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 "scg_headers.h" + +#include "igraph_error.h" +#include "igraph_memory.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(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()", + IGRAPH_FILE_BASENAME, __LINE__, IGRAPH_ENOMEM); + } + return S; +} diff --git a/src/rigraph/core/version.c b/src/rigraph/core/version.c new file mode 100644 index 0000000..72af7ca --- /dev/null +++ b/src/rigraph/core/version.c @@ -0,0 +1,67 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2008-2012 Gabor Csardi + 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 + +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/src/rigraph/igraph-win.def b/src/rigraph/igraph-win.def new file mode 100644 index 0000000..a7a6ea2 --- /dev/null +++ b/src/rigraph/igraph-win.def @@ -0,0 +1,3 @@ +LIBRARY igraph.dll +EXPORTS + R_init_igraph diff --git a/src/leidenalg/igraph-R/igraph.h b/src/rigraph/include/igraph.h similarity index 90% rename from src/leidenalg/igraph-R/igraph.h rename to src/rigraph/include/igraph.h index 0e4c2ca..5b8c147 100644 --- a/src/leidenalg/igraph-R/igraph.h +++ b/src/rigraph/include/igraph.h @@ -1,22 +1,22 @@ /* -*- mode: C -*- */ -/* +/* IGraph library. Copyright (C) 2003-2012 Gabor Csardi 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 + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -25,7 +25,7 @@ #define IGRAPH_H #ifndef _GNU_SOURCE -# define _GNU_SOURCE 1 + #define _GNU_SOURCE 1 #endif #include "igraph_version.h" @@ -74,7 +74,6 @@ #include "igraph_motifs.h" #include "igraph_operators.h" #include "igraph_flow.h" -#include "igraph_revolver.h" #include "igraph_nongraph.h" #include "igraph_cocitation.h" #include "igraph_adjlist.h" @@ -91,5 +90,13 @@ #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" +#include "igraph_eulerian.h" +#include "igraph_graphicality.h" #endif diff --git a/src/rigraph/include/igraph_adjlist.h b/src/rigraph/include/igraph_adjlist.h new file mode 100644 index 0000000..ee56bdb --- /dev/null +++ b/src/rigraph/include/igraph_adjlist.h @@ -0,0 +1,189 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2009-2012 Gabor Csardi + 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; + +IGRAPH_EXPORT int igraph_adjlist_init(const igraph_t *graph, igraph_adjlist_t *al, + igraph_neimode_t mode, igraph_loops_t loops, + igraph_multiple_t multiple); +IGRAPH_EXPORT int igraph_adjlist_init_empty(igraph_adjlist_t *al, igraph_integer_t no_of_nodes); +IGRAPH_EXPORT igraph_integer_t igraph_adjlist_size(const igraph_adjlist_t *al); +IGRAPH_EXPORT int igraph_adjlist_init_complementer(const igraph_t *graph, + igraph_adjlist_t *al, + igraph_neimode_t mode, + igraph_bool_t loops); +IGRAPH_EXPORT void igraph_adjlist_destroy(igraph_adjlist_t *al); +IGRAPH_EXPORT void igraph_adjlist_clear(igraph_adjlist_t *al); +IGRAPH_EXPORT void igraph_adjlist_sort(igraph_adjlist_t *al); +IGRAPH_EXPORT int igraph_adjlist_simplify(igraph_adjlist_t *al); +IGRAPH_DEPRECATED IGRAPH_EXPORT int igraph_adjlist_remove_duplicate(const igraph_t *graph, + igraph_adjlist_t *al); +IGRAPH_EXPORT int igraph_adjlist_print(const igraph_adjlist_t *al); +IGRAPH_EXPORT int igraph_adjlist_fprint(const igraph_adjlist_t *al, FILE *outfile); +IGRAPH_EXPORT igraph_bool_t igraph_adjlist_has_edge(igraph_adjlist_t* al, igraph_integer_t from, igraph_integer_t to, igraph_bool_t directed); +IGRAPH_EXPORT 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); + +/** + * \define igraph_adjlist_get + * \brief Query a vector in an adjacency list. + * + * Returns a pointer to an igraph_vector_int_t object from an + * adjacency list. The vector can be modified as desired. + * \param al The adjacency list object. + * \param no The vertex whose adjacent vertices will be returned. + * \return Pointer to the igraph_vector_int_t object. + * + * Time complexity: O(1). + */ +#define igraph_adjlist_get(al,no) (&(al)->adjs[(long int)(no)]) + +IGRAPH_EXPORT 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; + +IGRAPH_EXPORT int igraph_inclist_init(const igraph_t *graph, + igraph_inclist_t *il, + igraph_neimode_t mode, + igraph_loops_t loops); +IGRAPH_EXPORT int igraph_inclist_init_empty(igraph_inclist_t *il, igraph_integer_t n); +IGRAPH_EXPORT igraph_integer_t igraph_inclist_size(const igraph_inclist_t *al); +IGRAPH_EXPORT void igraph_inclist_destroy(igraph_inclist_t *il); +IGRAPH_EXPORT void igraph_inclist_clear(igraph_inclist_t *il); +IGRAPH_DEPRECATED IGRAPH_EXPORT int igraph_inclist_remove_duplicate(const igraph_t *graph, + igraph_inclist_t *il); +IGRAPH_EXPORT int igraph_inclist_print(const igraph_inclist_t *il); +IGRAPH_EXPORT int igraph_inclist_fprint(const igraph_inclist_t *il, FILE *outfile); + +/** + * \define igraph_inclist_get + * \brief Query a vector in an incidence list. + * + * Returns a pointer to an igraph_vector_int_t 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 igraph_vector_int_t 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_int_t **adjs; + igraph_neimode_t mode; + igraph_loops_t loops; + igraph_multiple_t multiple; + igraph_vector_t dummy; +} igraph_lazy_adjlist_t; + +IGRAPH_EXPORT int igraph_lazy_adjlist_init(const igraph_t *graph, + igraph_lazy_adjlist_t *al, + igraph_neimode_t mode, + igraph_loops_t loops, + igraph_multiple_t multiple); +IGRAPH_EXPORT void igraph_lazy_adjlist_destroy(igraph_lazy_adjlist_t *al); +IGRAPH_EXPORT void igraph_lazy_adjlist_clear(igraph_lazy_adjlist_t *al); +IGRAPH_EXPORT igraph_integer_t igraph_lazy_adjlist_size(const igraph_lazy_adjlist_t *al); + +/** + * \define igraph_lazy_adjlist_get + * \brief 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_i_lazy_adjlist_get_real(al, no))) +IGRAPH_EXPORT igraph_vector_int_t *igraph_i_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_int_t **incs; + igraph_neimode_t mode; + igraph_vector_t dummy; + igraph_loops_t loops; +} igraph_lazy_inclist_t; + +IGRAPH_EXPORT int igraph_lazy_inclist_init(const igraph_t *graph, + igraph_lazy_inclist_t *il, + igraph_neimode_t mode, + igraph_loops_t loops); +IGRAPH_EXPORT void igraph_lazy_inclist_destroy(igraph_lazy_inclist_t *il); +IGRAPH_EXPORT void igraph_lazy_inclist_clear(igraph_lazy_inclist_t *il); +IGRAPH_EXPORT igraph_integer_t igraph_lazy_inclist_size(const igraph_lazy_inclist_t *il); + +/** + * \define igraph_lazy_inclist_get + * \brief 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_i_lazy_inclist_get_real(al, no))) +IGRAPH_EXPORT igraph_vector_int_t *igraph_i_lazy_inclist_get_real(igraph_lazy_inclist_t *al, igraph_integer_t no); + +__END_DECLS + +#endif diff --git a/src/leidenalg/igraph-R/igraph_arpack.h b/src/rigraph/include/igraph_arpack.h similarity index 76% rename from src/leidenalg/igraph-R/igraph_arpack.h rename to src/rigraph/include/igraph_arpack.h index b165665..c28af92 100644 --- a/src/leidenalg/igraph-R/igraph_arpack.h +++ b/src/rigraph/include/igraph_arpack.h @@ -1,5 +1,5 @@ /* -*- mode: C -*- */ -/* +/* IGraph library. Copyright (C) 2007-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA @@ -8,36 +8,27 @@ it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. - + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +#ifndef IGRAPH_ARPACK_H +#define IGRAPH_ARPACK_H + +#include "igraph_decls.h" #include "igraph_types.h" #include "igraph_vector.h" #include "igraph_matrix.h" -#ifndef ARPACK_H -#define ARPACK_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 /** @@ -229,38 +220,38 @@ __BEGIN_DECLS */ 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]; + /* 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; /** @@ -285,23 +276,23 @@ typedef struct igraph_arpack_options_t { */ 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; + 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; -void igraph_arpack_options_init(igraph_arpack_options_t *o); +IGRAPH_EXPORT void igraph_arpack_options_init(igraph_arpack_options_t *o); -int igraph_arpack_storage_init(igraph_arpack_storage_t *s, long int maxn, - long int maxncv, long int maxldv, igraph_bool_t symm); -void igraph_arpack_storage_destroy(igraph_arpack_storage_t *s); +IGRAPH_EXPORT int igraph_arpack_storage_init(igraph_arpack_storage_t *s, long int maxn, + long int maxncv, long int maxldv, igraph_bool_t symm); +IGRAPH_EXPORT void igraph_arpack_storage_destroy(igraph_arpack_storage_t *s); /** * \typedef igraph_arpack_function_t @@ -321,20 +312,20 @@ void igraph_arpack_storage_destroy(igraph_arpack_storage_t *s); */ typedef int igraph_arpack_function_t(igraph_real_t *to, const igraph_real_t *from, - int n, void *extra); + int n, void *extra); -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_EXPORT 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); -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_EXPORT 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); -int igraph_arpack_unpack_complex(igraph_matrix_t *vectors, igraph_matrix_t *values, - long int nev); +IGRAPH_EXPORT int igraph_arpack_unpack_complex(igraph_matrix_t *vectors, igraph_matrix_t *values, + long int nev); __END_DECLS diff --git a/src/leidenalg/igraph-R/igraph_array.h b/src/rigraph/include/igraph_array.h similarity index 88% rename from src/leidenalg/igraph-R/igraph_array.h rename to src/rigraph/include/igraph_array.h index 4b8a47e..627433a 100644 --- a/src/leidenalg/igraph-R/igraph_array.h +++ b/src/rigraph/include/igraph_array.h @@ -1,22 +1,22 @@ /* -*- mode: C -*- */ -/* +/* IGraph library. Copyright (C) 2009-2012 Gabor Csardi 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 + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -24,15 +24,7 @@ #ifndef IGRAPH_ARRAY_H #define IGRAPH_ARRAY_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_decls.h" __BEGIN_DECLS diff --git a/src/rigraph/include/igraph_array_pmt.h b/src/rigraph/include/igraph_array_pmt.h new file mode 100644 index 0000000..18c73ae --- /dev/null +++ b/src/rigraph/include/igraph_array_pmt.h @@ -0,0 +1,51 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2007-2012 Gabor Csardi + 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 + +IGRAPH_EXPORT int FUNCTION(igraph_array3, init)(TYPE(igraph_array3) *a, long int n1, long int n2, + long int n3); +IGRAPH_EXPORT void FUNCTION(igraph_array3, destroy)(TYPE(igraph_array3) *a); +IGRAPH_EXPORT long int FUNCTION(igraph_array3, size)(const TYPE(igraph_array3) *a); +IGRAPH_EXPORT long int FUNCTION(igraph_array3, n)(const TYPE(igraph_array3) *a, long int idx); +IGRAPH_EXPORT int FUNCTION(igraph_array3, resize)(TYPE(igraph_array3) *a, long int n1, long int n2, + long int n3); +IGRAPH_EXPORT void FUNCTION(igraph_array3, null)(TYPE(igraph_array3) *a); +IGRAPH_EXPORT BASE FUNCTION(igraph_array3, sum)(const TYPE(igraph_array3) *a); +IGRAPH_EXPORT void FUNCTION(igraph_array3, scale)(TYPE(igraph_array3) *a, BASE by); +IGRAPH_EXPORT void FUNCTION(igraph_array3, fill)(TYPE(igraph_array3) *a, BASE e); +IGRAPH_EXPORT int FUNCTION(igraph_array3, update)(TYPE(igraph_array3) *to, + const TYPE(igraph_array3) *from); diff --git a/src/rigraph/include/igraph_attributes.h b/src/rigraph/include/igraph_attributes.h new file mode 100644 index 0000000..37c8739 --- /dev/null +++ b/src/rigraph/include/igraph_attributes.h @@ -0,0 +1,826 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2005-2012 Gabor Csardi + 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_ATTRIBUTES_H +#define IGRAPH_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 + * + * 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. + * + * 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. + * + * 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. + */ + +/** + * \section about_attribute_table + * 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. + * + * By default there is no attribute interface attached to \a igraph, + * to attach one, call \ref igraph_set_attribute_table with your new + * table. + * + */ + +/** + * \section about_attribute_combination + * + * Several graph operations may collapse multiple vertices or edges into + * a single one. Attribute combination lists are used to indicate to the attribute + * handler how to combine the attributes of the original vertices or edges and + * how to derive the final attribute value that is to be assigned to the collapsed + * vertex or edge. For example, \ref igraph_simplify() removes loops and combines + * multiple edges into a single one; in case of a graph with an edge attribute + * named \c weight the attribute combination list can tell the attribute handler + * whether the weight of a collapsed edge should be the sum, the mean or some other + * function of the weights of the original edges that were collapsed into one. + * + * One attribute combination list may contain several attribute combination + * records, one for each vertex or edge attribute that is to be handled during the + * operation. + */ + +/** + * \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 igraph_attribute_combination_type_t + * The possible types of attribute combinations. + * + * \enumval IGRAPH_ATTRIBUTE_COMBINE_IGNORE Ignore old attributes, use an empty value. + * \enumval IGRAPH_ATTRIBUTE_COMBINE_DEFAULT Use the default way to combine attributes (decided by the attribute handler implementation). + * \enumval IGRAPH_ATTRIBUTE_COMBINE_FUNCTION Supply your own function to combine + * attributes. + * \enumval IGRAPH_ATTRIBUTE_COMBINE_SUM Take the sum of the attributes. + * \enumval IGRAPH_ATTRIBUTE_COMBINE_PROD Take the product of the attributes. + * \enumval IGRAPH_ATTRIBUTE_COMBINE_MIN Take the minimum attribute. + * \enumval IGRAPH_ATTRIBUTE_COMBINE_MAX Take the maximum attribute. + * \enumval IGRAPH_ATTRIBUTE_COMBINE_RANDOM Take a random attribute. + * \enumval IGRAPH_ATTRIBUTE_COMBINE_FIRST Take the first attribute. + * \enumval IGRAPH_ATTRIBUTE_COMBINE_LAST Take the last attribute. + * \enumval IGRAPH_ATTRIBUTE_COMBINE_MEAN Take the mean of the attributes. + * \enumval IGRAPH_ATTRIBUTE_COMBINE_MEDIAN Take the median of the attributes. + * \enumval IGRAPH_ATTRIBUTE_COMBINE_CONCAT Concatenate the attributes. + */ +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) + +IGRAPH_EXPORT int igraph_attribute_combination_init(igraph_attribute_combination_t *comb); +IGRAPH_EXPORT int igraph_attribute_combination(igraph_attribute_combination_t *comb, ...); +IGRAPH_EXPORT void igraph_attribute_combination_destroy(igraph_attribute_combination_t *comb); +IGRAPH_EXPORT int igraph_attribute_combination_add(igraph_attribute_combination_t *comb, + const char *name, + igraph_attribute_combination_type_t type, + igraph_function_pointer_t func); +IGRAPH_EXPORT int igraph_attribute_combination_remove(igraph_attribute_combination_t *comb, + const char *name); +IGRAPH_EXPORT 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 get_*_*_attr are allowed to + * convert the attributes to numeric or string. E.g. if a vertex attribute + * is a GNU R complex data type, then + * get_string_vertex_attribute may serialize it + * into a string, but this probably makes sense only if + * add_vertices 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; + +IGRAPH_EXPORT IGRAPH_DEPRECATED igraph_attribute_table_t * igraph_i_set_attribute_table(const igraph_attribute_table_t * table); +IGRAPH_EXPORT igraph_attribute_table_t * igraph_set_attribute_table(const igraph_attribute_table_t * table); + +IGRAPH_EXPORT igraph_bool_t igraph_has_attribute_table(void); + +/* Experimental attribute handler in C */ + +IGRAPH_EXPORT extern const igraph_attribute_table_t igraph_cattribute_table; + +IGRAPH_EXPORT igraph_real_t igraph_cattribute_GAN(const igraph_t *graph, const char *name); +IGRAPH_EXPORT igraph_bool_t igraph_cattribute_GAB(const igraph_t *graph, const char *name); +IGRAPH_EXPORT const char* igraph_cattribute_GAS(const igraph_t *graph, const char *name); +IGRAPH_EXPORT igraph_real_t igraph_cattribute_VAN(const igraph_t *graph, const char *name, + igraph_integer_t vid); +IGRAPH_EXPORT igraph_bool_t igraph_cattribute_VAB(const igraph_t *graph, const char *name, + igraph_integer_t vid); +IGRAPH_EXPORT const char* igraph_cattribute_VAS(const igraph_t *graph, const char *name, + igraph_integer_t vid); +IGRAPH_EXPORT igraph_real_t igraph_cattribute_EAN(const igraph_t *graph, const char *name, + igraph_integer_t eid); +IGRAPH_EXPORT igraph_bool_t igraph_cattribute_EAB(const igraph_t *graph, const char *name, + igraph_integer_t eid); +IGRAPH_EXPORT const char* igraph_cattribute_EAS(const igraph_t *graph, const char *name, + igraph_integer_t eid); + +IGRAPH_EXPORT int igraph_cattribute_VANV(const igraph_t *graph, const char *name, + igraph_vs_t vids, igraph_vector_t *result); +IGRAPH_EXPORT int igraph_cattribute_EANV(const igraph_t *graph, const char *name, + igraph_es_t eids, igraph_vector_t *result); +IGRAPH_EXPORT int igraph_cattribute_VASV(const igraph_t *graph, const char *name, + igraph_vs_t vids, igraph_strvector_t *result); +IGRAPH_EXPORT int igraph_cattribute_EASV(const igraph_t *graph, const char *name, + igraph_es_t eids, igraph_strvector_t *result); +IGRAPH_EXPORT int igraph_cattribute_VABV(const igraph_t *graph, const char *name, + igraph_vs_t vids, igraph_vector_bool_t *result); +IGRAPH_EXPORT int igraph_cattribute_EABV(const igraph_t *graph, const char *name, + igraph_es_t eids, igraph_vector_bool_t *result); + +IGRAPH_EXPORT 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); +IGRAPH_EXPORT igraph_bool_t igraph_cattribute_has_attr(const igraph_t *graph, + igraph_attribute_elemtype_t type, + const char *name); + +IGRAPH_EXPORT int igraph_cattribute_GAN_set(igraph_t *graph, const char *name, + igraph_real_t value); +IGRAPH_EXPORT int igraph_cattribute_GAB_set(igraph_t *graph, const char *name, + igraph_bool_t value); +IGRAPH_EXPORT int igraph_cattribute_GAS_set(igraph_t *graph, const char *name, + const char *value); +IGRAPH_EXPORT int igraph_cattribute_VAN_set(igraph_t *graph, const char *name, + igraph_integer_t vid, igraph_real_t value); +IGRAPH_EXPORT int igraph_cattribute_VAB_set(igraph_t *graph, const char *name, + igraph_integer_t vid, igraph_bool_t value); +IGRAPH_EXPORT int igraph_cattribute_VAS_set(igraph_t *graph, const char *name, + igraph_integer_t vid, const char *value); +IGRAPH_EXPORT int igraph_cattribute_EAN_set(igraph_t *graph, const char *name, + igraph_integer_t eid, igraph_real_t value); +IGRAPH_EXPORT int igraph_cattribute_EAB_set(igraph_t *graph, const char *name, + igraph_integer_t eid, igraph_bool_t value); +IGRAPH_EXPORT int igraph_cattribute_EAS_set(igraph_t *graph, const char *name, + igraph_integer_t eid, const char *value); + +IGRAPH_EXPORT int igraph_cattribute_VAN_setv(igraph_t *graph, const char *name, + const igraph_vector_t *v); +IGRAPH_EXPORT int igraph_cattribute_VAB_setv(igraph_t *graph, const char *name, + const igraph_vector_bool_t *v); +IGRAPH_EXPORT int igraph_cattribute_VAS_setv(igraph_t *graph, const char *name, + const igraph_strvector_t *sv); +IGRAPH_EXPORT int igraph_cattribute_EAN_setv(igraph_t *graph, const char *name, + const igraph_vector_t *v); +IGRAPH_EXPORT int igraph_cattribute_EAB_setv(igraph_t *graph, const char *name, + const igraph_vector_bool_t *v); +IGRAPH_EXPORT int igraph_cattribute_EAS_setv(igraph_t *graph, const char *name, + const igraph_strvector_t *sv); + +IGRAPH_EXPORT void igraph_cattribute_remove_g(igraph_t *graph, const char *name); +IGRAPH_EXPORT void igraph_cattribute_remove_v(igraph_t *graph, const char *name); +IGRAPH_EXPORT void igraph_cattribute_remove_e(igraph_t *graph, const char *name); +IGRAPH_EXPORT 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 edges + * + * 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 edges + * + * 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 edges + * + * 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/src/rigraph/include/igraph_bipartite.h b/src/rigraph/include/igraph_bipartite.h new file mode 100644 index 0000000..13771e7 --- /dev/null +++ b/src/rigraph/include/igraph_bipartite.h @@ -0,0 +1,97 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2009-2012 Gabor Csardi + 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 */ +/* -------------------------------------------------- */ + +IGRAPH_EXPORT 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_EXPORT int igraph_create_bipartite(igraph_t *g, const igraph_vector_bool_t *types, + const igraph_vector_t *edges, + igraph_bool_t directed); + +IGRAPH_EXPORT 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); + +IGRAPH_EXPORT 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); + +IGRAPH_EXPORT 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_EXPORT 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); + +IGRAPH_EXPORT int igraph_is_bipartite(const igraph_t *graph, + igraph_bool_t *res, + igraph_vector_bool_t *types); + +IGRAPH_EXPORT 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); + +IGRAPH_EXPORT 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); + +IGRAPH_EXPORT 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/src/leidenalg/igraph-R/igraph_blas.h b/src/rigraph/include/igraph_blas.h similarity index 70% rename from src/leidenalg/igraph-R/igraph_blas.h rename to src/rigraph/include/igraph_blas.h index 7742720..4daf5df 100644 --- a/src/leidenalg/igraph-R/igraph_blas.h +++ b/src/rigraph/include/igraph_blas.h @@ -1,48 +1,39 @@ /* -*- mode: C -*- */ -/* +/* IGraph library. Copyright (C) 2007-2012 Gabor Csardi 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 + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#ifndef BLAS_H -#define BLAS_H +#ifndef IGRAPH_BLAS_H +#define IGRAPH_BLAS_H +#include "igraph_decls.h" #include "igraph_types.h" #include "igraph_vector.h" #include "igraph_matrix.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 /** * \section about_blas BLAS interface in igraph - * + * * * BLAS is a highly optimized library for basic linear algebra operations * such as vector-vector, matrix-vector and matrix-matrix product. @@ -60,12 +51,17 @@ __BEGIN_DECLS * */ -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); -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); +IGRAPH_EXPORT 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); +IGRAPH_EXPORT 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); + +IGRAPH_EXPORT igraph_real_t igraph_blas_dnrm2(const igraph_vector_t *v); + +IGRAPH_EXPORT int igraph_blas_ddot(const igraph_vector_t *v1, const igraph_vector_t *v2, + igraph_real_t *res); __END_DECLS diff --git a/src/rigraph/include/igraph_centrality.h b/src/rigraph/include/igraph_centrality.h new file mode 100644 index 0000000..c6f5abb --- /dev/null +++ b/src/rigraph/include/igraph_centrality.h @@ -0,0 +1,204 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2009-2012 Gabor Csardi + 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 */ +/* -------------------------------------------------- */ + +IGRAPH_EXPORT int igraph_closeness(const igraph_t *graph, igraph_vector_t *res, + igraph_vector_t *reachable_count, igraph_bool_t *all_reachable, + const igraph_vs_t vids, igraph_neimode_t mode, + const igraph_vector_t *weights, igraph_bool_t normalized); +IGRAPH_EXPORT int igraph_closeness_cutoff(const igraph_t *graph, igraph_vector_t *res, + igraph_vector_t *reachable_count, igraph_bool_t *all_reachable, + const igraph_vs_t vids, igraph_neimode_t mode, + const igraph_vector_t *weights, + igraph_bool_t normalized, + igraph_real_t cutoff); + +IGRAPH_EXPORT int igraph_harmonic_centrality(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); +IGRAPH_EXPORT int igraph_harmonic_centrality_cutoff(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, + igraph_real_t cutoff); + +IGRAPH_EXPORT 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_EXPORT int igraph_betweenness_cutoff(const igraph_t *graph, igraph_vector_t *res, + const igraph_vs_t vids, igraph_bool_t directed, + const igraph_vector_t *weights, igraph_real_t cutoff); +IGRAPH_EXPORT int igraph_edge_betweenness(const igraph_t *graph, igraph_vector_t *result, + igraph_bool_t directed, + const igraph_vector_t *weigths); +IGRAPH_EXPORT int igraph_edge_betweenness_cutoff(const igraph_t *graph, igraph_vector_t *result, + igraph_bool_t directed, + const igraph_vector_t *weights, igraph_real_t cutoff); + +/** + * \typedef igraph_pagerank_algo_t + * \brief PageRank algorithm implementation + * + * Algorithms to calculate PageRank. + * \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_ARPACK = 1, + IGRAPH_PAGERANK_ALGO_PRPACK = 2 +} igraph_pagerank_algo_t; + +IGRAPH_EXPORT 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, igraph_arpack_options_t *options); +IGRAPH_EXPORT 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, + const igraph_vector_t *reset, + const igraph_vector_t *weights, igraph_arpack_options_t *options); +IGRAPH_EXPORT 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, igraph_arpack_options_t *options); + +IGRAPH_EXPORT 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); + +IGRAPH_EXPORT 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); +IGRAPH_EXPORT 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); + +IGRAPH_EXPORT int igraph_constraint(const igraph_t *graph, igraph_vector_t *res, + igraph_vs_t vids, const igraph_vector_t *weights); + +IGRAPH_EXPORT int igraph_convergence_degree(const igraph_t *graph, igraph_vector_t *result, + igraph_vector_t *ins, igraph_vector_t *outs); + +IGRAPH_EXPORT igraph_real_t igraph_centralization(const igraph_vector_t *scores, + igraph_real_t theoretical_max, + igraph_bool_t normalized); + +IGRAPH_EXPORT 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_EXPORT 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_EXPORT int igraph_centralization_betweenness(const igraph_t *graph, + igraph_vector_t *res, + igraph_bool_t directed, + igraph_real_t *centralization, + igraph_real_t *theoretical_max, + igraph_bool_t normalized); +IGRAPH_EXPORT int igraph_centralization_betweenness_tmax(const igraph_t *graph, + igraph_integer_t nodes, + igraph_bool_t directed, + igraph_real_t *res); + +IGRAPH_EXPORT 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_EXPORT int igraph_centralization_closeness_tmax(const igraph_t *graph, + igraph_integer_t nodes, + igraph_neimode_t mode, + igraph_real_t *res); + +IGRAPH_EXPORT 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_EXPORT 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); + + +/* Deprecated functions: */ + +IGRAPH_EXPORT IGRAPH_DEPRECATED 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); + +IGRAPH_EXPORT IGRAPH_DEPRECATED 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_EXPORT IGRAPH_DEPRECATED 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); + +__END_DECLS + +#endif diff --git a/src/rigraph/include/igraph_cliques.h b/src/rigraph/include/igraph_cliques.h new file mode 100644 index 0000000..179b3f7 --- /dev/null +++ b/src/rigraph/include/igraph_cliques.h @@ -0,0 +1,114 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2009-2012 Gabor Csardi + 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 */ +/* -------------------------------------------------- */ + +IGRAPH_EXPORT int igraph_maximal_cliques(const igraph_t *graph, igraph_vector_ptr_t *res, + igraph_integer_t min_size, igraph_integer_t max_size); +IGRAPH_EXPORT int igraph_maximal_cliques_file(const igraph_t *graph, + FILE *outfile, + igraph_integer_t min_size, + igraph_integer_t max_size); +IGRAPH_EXPORT int igraph_maximal_cliques_count(const igraph_t *graph, + igraph_integer_t *res, + igraph_integer_t min_size, + igraph_integer_t max_size); +IGRAPH_EXPORT 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); +IGRAPH_EXPORT int igraph_maximal_cliques_hist(const igraph_t *graph, + igraph_vector_t *hist, + igraph_integer_t min_size, + igraph_integer_t max_size); + +IGRAPH_EXPORT int igraph_cliques(const igraph_t *graph, igraph_vector_ptr_t *res, + igraph_integer_t min_size, igraph_integer_t max_size); +IGRAPH_EXPORT int igraph_clique_size_hist(const igraph_t *graph, igraph_vector_t *hist, + igraph_integer_t min_size, igraph_integer_t max_size); +IGRAPH_EXPORT int igraph_largest_cliques(const igraph_t *graph, + igraph_vector_ptr_t *cliques); +IGRAPH_EXPORT int igraph_clique_number(const igraph_t *graph, igraph_integer_t *no); +IGRAPH_EXPORT 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); +IGRAPH_EXPORT int igraph_largest_weighted_cliques(const igraph_t *graph, + const igraph_vector_t *vertex_weights, igraph_vector_ptr_t *res); +IGRAPH_EXPORT int igraph_weighted_clique_number(const igraph_t *graph, + const igraph_vector_t *vertex_weights, igraph_real_t *res); +IGRAPH_EXPORT int igraph_independent_vertex_sets(const igraph_t *graph, + igraph_vector_ptr_t *res, + igraph_integer_t min_size, + igraph_integer_t max_size); +IGRAPH_EXPORT int igraph_largest_independent_vertex_sets(const igraph_t *graph, + igraph_vector_ptr_t *res); +IGRAPH_EXPORT int igraph_maximal_independent_vertex_sets(const igraph_t *graph, + igraph_vector_ptr_t *res); +IGRAPH_EXPORT 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); + +IGRAPH_EXPORT 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); + +IGRAPH_EXPORT 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/src/rigraph/include/igraph_cocitation.h b/src/rigraph/include/igraph_cocitation.h new file mode 100644 index 0000000..32699b1 --- /dev/null +++ b/src/rigraph/include/igraph_cocitation.h @@ -0,0 +1,66 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2009-2012 Gabor Csardi + 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 */ +/* -------------------------------------------------- */ + +IGRAPH_EXPORT int igraph_cocitation(const igraph_t *graph, igraph_matrix_t *res, + const igraph_vs_t vids); +IGRAPH_EXPORT int igraph_bibcoupling(const igraph_t *graph, igraph_matrix_t *res, + const igraph_vs_t vids); + +IGRAPH_EXPORT 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_EXPORT 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_EXPORT 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_EXPORT 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); +IGRAPH_EXPORT 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); +IGRAPH_EXPORT 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); + +IGRAPH_EXPORT 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/src/leidenalg/igraph-R/igraph_cohesive_blocks.h b/src/rigraph/include/igraph_cohesive_blocks.h similarity index 72% rename from src/leidenalg/igraph-R/igraph_cohesive_blocks.h rename to src/rigraph/include/igraph_cohesive_blocks.h index 723706c..b26a5f3 100644 --- a/src/leidenalg/igraph-R/igraph_cohesive_blocks.h +++ b/src/rigraph/include/igraph_cohesive_blocks.h @@ -1,22 +1,22 @@ /* -*- mode: C -*- */ -/* +/* IGraph library. Copyright (C) 2010-2012 Gabor Csardi 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 + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -24,14 +24,19 @@ #ifndef IGRAPH_COHESIVE_BLOCKS_H #define IGRAPH_COHESIVE_BLOCKS_H +#include "igraph_decls.h" #include "igraph_datatype.h" #include "igraph_vector.h" #include "igraph_vector_ptr.h" -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); +__BEGIN_DECLS + +IGRAPH_EXPORT 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/src/rigraph/include/igraph_coloring.h b/src/rigraph/include/igraph_coloring.h new file mode 100644 index 0000000..02876eb --- /dev/null +++ b/src/rigraph/include/igraph_coloring.h @@ -0,0 +1,46 @@ +/* + Heuristic graph coloring algorithms. + Copyright (C) 2017 Szabolcs Horvat + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public 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_COLORING_H +#define IGRAPH_COLORING_H + +#include "igraph_decls.h" +#include "igraph_datatype.h" + +__BEGIN_DECLS + +/** + * \typedef igraph_coloring_greedy_t + * \brief Ordering heuristics for greedy graph coloring. + * + * Ordering heuristics for \ref 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; + +IGRAPH_EXPORT 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/src/rigraph/include/igraph_community.h b/src/rigraph/include/igraph_community.h new file mode 100644 index 0000000..aaaa246 --- /dev/null +++ b/src/rigraph/include/igraph_community.h @@ -0,0 +1,253 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2009-2012 Gabor Csardi + 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 */ +/* -------------------------------------------------- */ + +IGRAPH_EXPORT 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: */ + +IGRAPH_EXPORT int igraph_community_optimal_modularity(const igraph_t *graph, + igraph_real_t *modularity, + igraph_vector_t *membership, + const igraph_vector_t *weights); + +IGRAPH_EXPORT 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); + +IGRAPH_EXPORT 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_EXPORT 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); + +IGRAPH_EXPORT 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); + +IGRAPH_EXPORT 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); +IGRAPH_EXPORT int igraph_community_eb_get_merges(const igraph_t *graph, + const igraph_bool_t directed, + 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); + +IGRAPH_EXPORT 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); + +IGRAPH_EXPORT 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); +IGRAPH_EXPORT int igraph_le_community_to_membership(const igraph_matrix_t *merges, + igraph_integer_t steps, + igraph_vector_t *membership, + igraph_vector_t *csize); + +IGRAPH_EXPORT int igraph_modularity(const igraph_t *graph, + const igraph_vector_t *membership, + const igraph_vector_t *weights, + const igraph_real_t resolution, + const igraph_bool_t directed, + igraph_real_t *modularity); + +IGRAPH_EXPORT int igraph_modularity_matrix(const igraph_t *graph, + const igraph_vector_t *weights, + const igraph_real_t resolution, + igraph_matrix_t *modmat, + igraph_bool_t directed); + +IGRAPH_EXPORT 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); + +IGRAPH_EXPORT 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); + +IGRAPH_EXPORT int igraph_community_fluid_communities(const igraph_t *graph, + igraph_integer_t no_of_communities, + igraph_vector_t *membership, + igraph_real_t *modularity); + +IGRAPH_EXPORT int igraph_community_label_propagation(const igraph_t *graph, + igraph_vector_t *membership, + const igraph_vector_t *weights, + const igraph_vector_t *initial, + const igraph_vector_bool_t *fixed, + igraph_real_t *modularity); + +IGRAPH_EXPORT int igraph_community_multilevel(const igraph_t *graph, + const igraph_vector_t *weights, + const igraph_real_t resolution, + igraph_vector_t *membership, + igraph_matrix_t *memberships, + igraph_vector_t *modularity); + +IGRAPH_EXPORT 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 */ +/* -------------------------------------------------- */ + +IGRAPH_EXPORT int igraph_compare_communities(const igraph_vector_t *comm1, + const igraph_vector_t *comm2, + igraph_real_t* result, + igraph_community_comparison_t method); +IGRAPH_EXPORT 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/src/rigraph/include/igraph_complex.h b/src/rigraph/include/igraph_complex.h new file mode 100644 index 0000000..2ff0b45 --- /dev/null +++ b/src/rigraph/include/igraph_complex.h @@ -0,0 +1,104 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2010-2012 Gabor Csardi + 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]) + +IGRAPH_EXPORT igraph_complex_t igraph_complex(igraph_real_t x, igraph_real_t y); +IGRAPH_EXPORT igraph_complex_t igraph_complex_polar(igraph_real_t r, igraph_real_t theta); + +IGRAPH_EXPORT igraph_bool_t igraph_complex_eq_tol(igraph_complex_t z1, + igraph_complex_t z2, + igraph_real_t tol); + +IGRAPH_EXPORT igraph_real_t igraph_complex_mod(igraph_complex_t z); +IGRAPH_EXPORT igraph_real_t igraph_complex_arg(igraph_complex_t z); + +IGRAPH_EXPORT igraph_real_t igraph_complex_abs(igraph_complex_t z); +IGRAPH_EXPORT igraph_real_t igraph_complex_logabs(igraph_complex_t z); + +IGRAPH_EXPORT igraph_complex_t igraph_complex_add(igraph_complex_t z1, + igraph_complex_t z2); +IGRAPH_EXPORT igraph_complex_t igraph_complex_sub(igraph_complex_t z1, + igraph_complex_t z2); +IGRAPH_EXPORT igraph_complex_t igraph_complex_mul(igraph_complex_t z1, + igraph_complex_t z2); +IGRAPH_EXPORT igraph_complex_t igraph_complex_div(igraph_complex_t z1, + igraph_complex_t z2); + +IGRAPH_EXPORT igraph_complex_t igraph_complex_add_real(igraph_complex_t z, + igraph_real_t x); +IGRAPH_EXPORT igraph_complex_t igraph_complex_add_imag(igraph_complex_t z, + igraph_real_t y); +IGRAPH_EXPORT igraph_complex_t igraph_complex_sub_real(igraph_complex_t z, + igraph_real_t x); +IGRAPH_EXPORT igraph_complex_t igraph_complex_sub_imag(igraph_complex_t z, + igraph_real_t y); +IGRAPH_EXPORT igraph_complex_t igraph_complex_mul_real(igraph_complex_t z, + igraph_real_t x); +IGRAPH_EXPORT igraph_complex_t igraph_complex_mul_imag(igraph_complex_t z, + igraph_real_t y); +IGRAPH_EXPORT igraph_complex_t igraph_complex_div_real(igraph_complex_t z, + igraph_real_t x); +IGRAPH_EXPORT igraph_complex_t igraph_complex_div_imag(igraph_complex_t z, + igraph_real_t y); + +IGRAPH_EXPORT igraph_complex_t igraph_complex_conj(igraph_complex_t z); +IGRAPH_EXPORT igraph_complex_t igraph_complex_neg(igraph_complex_t z); +IGRAPH_EXPORT igraph_complex_t igraph_complex_inv(igraph_complex_t z); + +IGRAPH_EXPORT igraph_complex_t igraph_complex_sqrt(igraph_complex_t z); +IGRAPH_EXPORT igraph_complex_t igraph_complex_sqrt_real(igraph_real_t x); +IGRAPH_EXPORT igraph_complex_t igraph_complex_exp(igraph_complex_t z); +IGRAPH_EXPORT igraph_complex_t igraph_complex_pow(igraph_complex_t z1, + igraph_complex_t z2); +IGRAPH_EXPORT igraph_complex_t igraph_complex_pow_real(igraph_complex_t z, + igraph_real_t x); +IGRAPH_EXPORT igraph_complex_t igraph_complex_log(igraph_complex_t z); +IGRAPH_EXPORT igraph_complex_t igraph_complex_log10(igraph_complex_t z); +IGRAPH_EXPORT igraph_complex_t igraph_complex_log_b(igraph_complex_t z, + igraph_complex_t b); + +IGRAPH_EXPORT igraph_complex_t igraph_complex_sin(igraph_complex_t z); +IGRAPH_EXPORT igraph_complex_t igraph_complex_cos(igraph_complex_t z); +IGRAPH_EXPORT igraph_complex_t igraph_complex_tan(igraph_complex_t z); +IGRAPH_EXPORT igraph_complex_t igraph_complex_sec(igraph_complex_t z); +IGRAPH_EXPORT igraph_complex_t igraph_complex_csc(igraph_complex_t z); +IGRAPH_EXPORT igraph_complex_t igraph_complex_cot(igraph_complex_t z); + +__END_DECLS + +#endif diff --git a/src/rigraph/include/igraph_components.h b/src/rigraph/include/igraph_components.h new file mode 100644 index 0000000..b9c4797 --- /dev/null +++ b/src/rigraph/include/igraph_components.h @@ -0,0 +1,61 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2009-2012 Gabor Csardi + 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 */ +/* -------------------------------------------------- */ + +IGRAPH_EXPORT int igraph_clusters(const igraph_t *graph, igraph_vector_t *membership, + igraph_vector_t *csize, igraph_integer_t *no, + igraph_connectedness_t mode); +IGRAPH_EXPORT int igraph_is_connected(const igraph_t *graph, igraph_bool_t *res, + igraph_connectedness_t mode); +IGRAPH_EXPORT void igraph_decompose_destroy(igraph_vector_ptr_t *complist); +IGRAPH_EXPORT int igraph_decompose(const igraph_t *graph, igraph_vector_ptr_t *components, + igraph_connectedness_t mode, + long int maxcompno, long int minelements); +IGRAPH_EXPORT int igraph_articulation_points(const igraph_t *graph, + igraph_vector_t *res); +IGRAPH_EXPORT 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); +IGRAPH_EXPORT int igraph_bridges(const igraph_t *graph, igraph_vector_t *bridges); + +__END_DECLS + +#endif diff --git a/src/rigraph/include/igraph_constants.h b/src/rigraph/include/igraph_constants.h new file mode 100644 index 0000000..3fcb305 --- /dev/null +++ b/src/rigraph/include/igraph_constants.h @@ -0,0 +1,203 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2009-2012 Gabor Csardi + 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; + +/* Note for the enum below: yes, IGRAPH_LOOPS_TWICE is 1, and IGRAPH_LOOPS_ONCE + * is 2. This is intentional, for the sake of backwards compatibility with + * earlier versions where we only had IGRAPH_LOOPS and it meant + * IGRAPH_LOOPS_TWICE */ +typedef enum { IGRAPH_NO_LOOPS = 0, IGRAPH_LOOPS = 1, IGRAPH_LOOPS_TWICE = 1, IGRAPH_LOOPS_ONCE = 2 } igraph_loops_t; + +typedef enum { IGRAPH_NO_MULTIPLE = 0, IGRAPH_MULTIPLE = 1 } igraph_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; + +/* Reverse IGRAPH_OUT to IGRAPH_IN and vice versa. Leave other values alone. */ +#define IGRAPH_REVERSE_MODE(mode) \ + ((mode) == IGRAPH_IN ? IGRAPH_OUT : ((mode) == IGRAPH_OUT ? IGRAPH_IN : (mode))) + +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_RANDOM, + IGRAPH_TO_DIRECTED_ACYCLIC + } 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/src/rigraph/include/igraph_constructors.h b/src/rigraph/include/igraph_constructors.h new file mode 100644 index 0000000..d7b1987 --- /dev/null +++ b/src/rigraph/include/igraph_constructors.h @@ -0,0 +1,80 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2009-2012 Gabor Csardi + 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" +#include "igraph_graphicality.h" + +__BEGIN_DECLS + +/* -------------------------------------------------- */ +/* Constructors, deterministic */ +/* -------------------------------------------------- */ + +IGRAPH_EXPORT int igraph_create(igraph_t *graph, const igraph_vector_t *edges, igraph_integer_t n, + igraph_bool_t directed); +IGRAPH_EXPORT int igraph_small(igraph_t *graph, igraph_integer_t n, igraph_bool_t directed, + ...); +IGRAPH_EXPORT int igraph_adjacency(igraph_t *graph, const igraph_matrix_t *adjmatrix, + igraph_adjacency_t mode); +IGRAPH_EXPORT int igraph_weighted_adjacency(igraph_t *graph, const igraph_matrix_t *adjmatrix, + igraph_adjacency_t mode, const char* attr, + igraph_bool_t loops); +IGRAPH_EXPORT int igraph_star(igraph_t *graph, igraph_integer_t n, igraph_star_mode_t mode, + igraph_integer_t center); +IGRAPH_EXPORT 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); +IGRAPH_EXPORT int igraph_ring(igraph_t *graph, igraph_integer_t n, igraph_bool_t directed, + igraph_bool_t mutual, igraph_bool_t circular); +IGRAPH_EXPORT int igraph_tree(igraph_t *graph, igraph_integer_t n, igraph_integer_t children, + igraph_tree_mode_t type); +IGRAPH_EXPORT int igraph_from_prufer(igraph_t *graph, const igraph_vector_int_t *prufer); +IGRAPH_EXPORT int igraph_full(igraph_t *graph, igraph_integer_t n, igraph_bool_t directed, igraph_bool_t loops); +IGRAPH_EXPORT int igraph_full_citation(igraph_t *graph, igraph_integer_t n, + igraph_bool_t directed); +IGRAPH_EXPORT int igraph_atlas(igraph_t *graph, int number); +IGRAPH_EXPORT int igraph_extended_chordal_ring(igraph_t *graph, igraph_integer_t nodes, + const igraph_matrix_t *W, igraph_bool_t directed); +IGRAPH_EXPORT int igraph_linegraph(const igraph_t *graph, igraph_t *linegraph); + +IGRAPH_EXPORT int igraph_de_bruijn(igraph_t *graph, igraph_integer_t m, igraph_integer_t n); +IGRAPH_EXPORT int igraph_kautz(igraph_t *graph, igraph_integer_t m, igraph_integer_t n); +IGRAPH_EXPORT int igraph_famous(igraph_t *graph, const char *name); +IGRAPH_EXPORT int igraph_lcf_vector(igraph_t *graph, igraph_integer_t n, + const igraph_vector_t *shifts, + igraph_integer_t repeats); +IGRAPH_EXPORT int igraph_lcf(igraph_t *graph, igraph_integer_t n, ...); +IGRAPH_EXPORT int igraph_realize_degree_sequence(igraph_t *graph, + const igraph_vector_t *outdeg, const igraph_vector_t *indeg, + igraph_edge_type_sw_t allowed_edge_types, + igraph_realize_degseq_t method); + +__END_DECLS + +#endif diff --git a/src/leidenalg/igraph-R/igraph_conversion.h b/src/rigraph/include/igraph_conversion.h similarity index 50% rename from src/leidenalg/igraph-R/igraph_conversion.h rename to src/rigraph/include/igraph_conversion.h index 9dce4c0..d191dc2 100644 --- a/src/leidenalg/igraph-R/igraph_conversion.h +++ b/src/rigraph/include/igraph_conversion.h @@ -1,22 +1,22 @@ /* -*- mode: C -*- */ -/* +/* IGraph library. Copyright (C) 2009-2012 Gabor Csardi 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 + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -24,16 +24,7 @@ #ifndef IGRAPH_CONVERSION_H #define IGRAPH_CONVERSION_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_decls.h" #include "igraph_constants.h" #include "igraph_types.h" #include "igraph_datatype.h" @@ -48,26 +39,27 @@ __BEGIN_DECLS /* Conversion */ /* -------------------------------------------------- */ -int igraph_get_adjacency(const igraph_t *graph, igraph_matrix_t *res, - igraph_get_adjacency_t type, igraph_bool_t eids); -int igraph_get_adjacency_sparse(const igraph_t *graph, igraph_spmatrix_t *res, - igraph_get_adjacency_t type); +IGRAPH_EXPORT int igraph_get_adjacency(const igraph_t *graph, igraph_matrix_t *res, + igraph_get_adjacency_t type, igraph_bool_t eids); +IGRAPH_EXPORT int igraph_get_adjacency_sparse(const igraph_t *graph, igraph_spmatrix_t *res, + igraph_get_adjacency_t type); -int igraph_get_stochastic(const igraph_t *graph, - igraph_matrix_t *matrix, - igraph_bool_t column_wise); +IGRAPH_EXPORT int igraph_get_stochastic(const igraph_t *graph, + igraph_matrix_t *matrix, + igraph_bool_t column_wise); -int igraph_get_stochastic_sparsemat(const igraph_t *graph, - igraph_sparsemat_t *sparsemat, - igraph_bool_t column_wise); +IGRAPH_EXPORT int igraph_get_stochastic_sparsemat(const igraph_t *graph, + igraph_sparsemat_t *sparsemat, + igraph_bool_t column_wise); -int igraph_get_edgelist(const igraph_t *graph, igraph_vector_t *res, igraph_bool_t bycol); +IGRAPH_EXPORT int igraph_get_edgelist(const igraph_t *graph, igraph_vector_t *res, igraph_bool_t bycol); -int igraph_to_directed(igraph_t *graph, - igraph_to_directed_t flags); -int igraph_to_undirected(igraph_t *graph, - igraph_to_undirected_t flags, - const igraph_attribute_combination_t *edge_comb); +IGRAPH_EXPORT int igraph_to_directed(igraph_t *graph, + igraph_to_directed_t flags); +IGRAPH_EXPORT int igraph_to_undirected(igraph_t *graph, + igraph_to_undirected_t mode, + const igraph_attribute_combination_t *edge_comb); +IGRAPH_EXPORT int igraph_to_prufer(const igraph_t *graph, igraph_vector_int_t *prufer); __END_DECLS diff --git a/src/leidenalg/igraph-R/igraph_datatype.h b/src/rigraph/include/igraph_datatype.h similarity index 69% rename from src/leidenalg/igraph-R/igraph_datatype.h rename to src/rigraph/include/igraph_datatype.h index 563b8d0..5e22192 100644 --- a/src/leidenalg/igraph-R/igraph_datatype.h +++ b/src/rigraph/include/igraph_datatype.h @@ -1,22 +1,22 @@ /* -*- mode: C -*- */ -/* +/* IGraph library. Copyright (C) 2009-2012 Gabor Csardi 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 + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -24,16 +24,7 @@ #ifndef IGRAPH_DATATYPE_H #define IGRAPH_DATATYPE_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_decls.h" #include "igraph_types.h" #include "igraph_vector.h" @@ -45,7 +36,7 @@ __BEGIN_DECLS * \brief The internal data structure for storing graphs. * * It is simple and efficient. It has the following members: - * - n The number of vertices, reduntant. + * - n The number of vertices, redundant. * - directed Whether the graph is directed. * - from The first column of the edge list. * - to The second column of the edge list. @@ -53,38 +44,41 @@ __BEGIN_DECLS * 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. - * - ii The index of the edge list by the second column. + * - ii The index of the edge list by the second column. * The length of this vector is the same as the number of edges. * - os 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 + * vertex \c v is edge no. \c from[oi[os[v]]] if * \c os[v]is
This is basically the same as os, 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. + * + * For undirected graphs, the same edge list is stored, i.e. an + * undirected edge is stored only once. Currently, undirected edges + * are canonicalized so that the index of the 'from' vertex is not greater + * than the index of the 'to' vertex. Thus, if v1 <= v2, only the edge (v1, v2) + * needs to be searched for, not (v2, v1), to determine if v1 and v2 are connected. + * However, this fact is NOT guaranteed by the documented public API, + * and should not be relied upon by the implementation of any functions, + * except those belonging to the minimal API in type_indexededgelist.c. * * 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_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 diff --git a/src/rigraph/include/igraph_decls.h b/src/rigraph/include/igraph_decls.h new file mode 100644 index 0000000..f55e4fa --- /dev/null +++ b/src/rigraph/include/igraph_decls.h @@ -0,0 +1,19 @@ +#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 + +/* This is to eliminate gcc warnings about unused parameters */ +#define IGRAPH_UNUSED(x) (void)(x) + +/* Include the definition of macros controlling symbol visibility */ +#include "igraph_export.h" + +/* Used instead of IGRAPH_EXPORT with functions that need to be tested, + * but are not part of the public API. */ +#define IGRAPH_PRIVATE_EXPORT IGRAPH_EXPORT diff --git a/src/leidenalg/igraph-R/igraph_dqueue.h b/src/rigraph/include/igraph_dqueue.h similarity index 84% rename from src/leidenalg/igraph-R/igraph_dqueue.h rename to src/rigraph/include/igraph_dqueue.h index d7146ba..5aab71f 100644 --- a/src/leidenalg/igraph-R/igraph_dqueue.h +++ b/src/rigraph/include/igraph_dqueue.h @@ -1,22 +1,22 @@ /* -*- mode: C -*- */ -/* +/* IGraph library. Copyright (C) 2009-2012 Gabor Csardi 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 + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -24,18 +24,9 @@ #ifndef IGRAPH_DQUEUE_H #define IGRAPH_DQUEUE_H +#include "igraph_decls.h" #include "igraph_types.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 /* -------------------------------------------------- */ @@ -66,10 +57,16 @@ __BEGIN_DECLS #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) + do { IGRAPH_CHECK(igraph_dqueue_init(v, size)); \ + IGRAPH_FINALLY(igraph_dqueue_destroy, v); } while (0) __END_DECLS diff --git a/src/rigraph/include/igraph_dqueue_pmt.h b/src/rigraph/include/igraph_dqueue_pmt.h new file mode 100644 index 0000000..d478bdc --- /dev/null +++ b/src/rigraph/include/igraph_dqueue_pmt.h @@ -0,0 +1,49 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2007-2012 Gabor Csardi + 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); + +IGRAPH_EXPORT int FUNCTION(igraph_dqueue, init) (TYPE(igraph_dqueue)* q, long int size); +IGRAPH_EXPORT void FUNCTION(igraph_dqueue, destroy) (TYPE(igraph_dqueue)* q); +IGRAPH_EXPORT igraph_bool_t FUNCTION(igraph_dqueue, empty) (const TYPE(igraph_dqueue)* q); +IGRAPH_EXPORT void FUNCTION(igraph_dqueue, clear) (TYPE(igraph_dqueue)* q); +IGRAPH_EXPORT igraph_bool_t FUNCTION(igraph_dqueue, full) (TYPE(igraph_dqueue)* q); +IGRAPH_EXPORT long int FUNCTION(igraph_dqueue, size) (const TYPE(igraph_dqueue)* q); +IGRAPH_EXPORT BASE FUNCTION(igraph_dqueue, pop) (TYPE(igraph_dqueue)* q); +IGRAPH_EXPORT BASE FUNCTION(igraph_dqueue, pop_back)(TYPE(igraph_dqueue)* q); +IGRAPH_EXPORT BASE FUNCTION(igraph_dqueue, head) (const TYPE(igraph_dqueue)* q); +IGRAPH_EXPORT BASE FUNCTION(igraph_dqueue, back) (const TYPE(igraph_dqueue)* q); +IGRAPH_EXPORT int FUNCTION(igraph_dqueue, push) (TYPE(igraph_dqueue)* q, BASE elem); +IGRAPH_EXPORT int FUNCTION(igraph_dqueue, print)(const TYPE(igraph_dqueue)* q); +IGRAPH_EXPORT int FUNCTION(igraph_dqueue, fprint)(const TYPE(igraph_dqueue)* q, FILE *file); +IGRAPH_EXPORT BASE FUNCTION(igraph_dqueue, e)(const TYPE(igraph_dqueue) *q, long int idx); diff --git a/src/rigraph/include/igraph_eigen.h b/src/rigraph/include/igraph_eigen.h new file mode 100644 index 0000000..67ba26d --- /dev/null +++ b/src/rigraph/include/igraph_eigen.h @@ -0,0 +1,111 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2010-2012 Gabor Csardi + 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_EIGEN_H +#define IGRAPH_EIGEN_H + +#include "igraph_decls.h" +#include "igraph_arpack.h" +#include "igraph_lapack.h" +#include "igraph_sparsemat.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; + +IGRAPH_EXPORT 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_EXPORT 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_EXPORT 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); + +IGRAPH_EXPORT 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/src/rigraph/include/igraph_embedding.h b/src/rigraph/include/igraph_embedding.h new file mode 100644 index 0000000..3eb97c6 --- /dev/null +++ b/src/rigraph/include/igraph_embedding.h @@ -0,0 +1,68 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2013 Gabor Csardi + 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 + +IGRAPH_EXPORT 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; + +IGRAPH_EXPORT 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_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_EXPORT int igraph_dim_select(const igraph_vector_t *sv, igraph_integer_t *dim); + +__END_DECLS + +#endif diff --git a/src/rigraph/include/igraph_epidemics.h b/src/rigraph/include/igraph_epidemics.h new file mode 100644 index 0000000..52c1122 --- /dev/null +++ b/src/rigraph/include/igraph_epidemics.h @@ -0,0 +1,67 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2014 Gabor Csardi + 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 + * \brief The result of one SIR model simulation. + * + * 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; + +IGRAPH_EXPORT int igraph_sir_init(igraph_sir_t *sir); +IGRAPH_EXPORT void igraph_sir_destroy(igraph_sir_t *sir); + +IGRAPH_EXPORT 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/src/leidenalg/igraph-R/igraph_error.h b/src/rigraph/include/igraph_error.h similarity index 52% rename from src/leidenalg/igraph-R/igraph_error.h rename to src/rigraph/include/igraph_error.h index 42ecb5d..8a9f15b 100644 --- a/src/leidenalg/igraph-R/igraph_error.h +++ b/src/rigraph/include/igraph_error.h @@ -1,22 +1,22 @@ /* -*- mode: C -*- */ -/* +/* IGraph library. Copyright (C) 2003-2012 Gabor Csardi 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 + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -24,17 +24,9 @@ #ifndef IGRAPH_ERROR_H #define IGRAPH_ERROR_H -#include +#include "igraph_decls.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 __BEGIN_DECLS @@ -43,25 +35,41 @@ __BEGIN_DECLS * prefix renamed to IGRAPH_), as I couldn't find a better way to do * them. */ +/* IGRAPH_NORETURN indicates to the compiler that a function does not return. + * There are standard facilities for this, namely _Noreturn in C11 and [[noreturn]] in C++11. + * However, since igraph is currently compiled with older standards, and since + * the standard 'noreturn' specification would need to be diferent between C and C++, + * we do not use these facilities. + */ +#if defined(__GNUC__) +/* Compilers that support the GNU C syntax. Use __noreturn__ instead of 'noreturn' as the latter is a macro in C11. */ +#define IGRAPH_NORETURN __attribute__((__noreturn__)) +#elif defined(_MSC_VER) +/* Compilers that support the MSVC syntax. */ +#define IGRAPH_NORETURN __declspec(noreturn) +#else +#define IGRAPH_NORETURN +#endif + /** - * \section errorhandlingbasics Error handling basics - * - * \a igraph functions can run into various problems preventing them + * \section error_handling_basics Error handling basics + * + * \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 + * 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. * - * - * By default \a igraph aborts the program when it runs into an - * error. While this behavior might be good enough for smaller programs, + * + * 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 + * if your project requires more sophisticated error handling. You can * safely skip the rest of this chapter otherwise. * */ /** - * \section errorhandlers Error handlers + * \section error_handlers Error handlers * * * If \a igraph runs into an error - an invalid argument was supplied @@ -96,8 +104,8 @@ __BEGIN_DECLS */ /** - * \section errorcodes Error codes - * + * \section error_codes Error codes + * * 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 @@ -114,21 +122,23 @@ __BEGIN_DECLS * for those who want to create an interface to \a igraph from another * language. Most readers can safely skip to the next chapter. * - * + * * * 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 + * signal errors the appropriate way, e.g. the R interface defines an * error handler which calls the error() * function, as required by R, while the Python interface has an error * handler which raises an exception according to the Python way. * - * + * * 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. + * prevent memory leaks. Note that this may invalidate the error message + * buffer \p reason passed to the error handler. Do not access it after + * having called \ref IGRAPH_FINALLY_FREE(). * */ @@ -137,16 +147,16 @@ __BEGIN_DECLS * * * If an error happens, the functions in the library call the - * \ref IGRAPH_ERROR macro with a textual description of the error and an + * \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. + * IGRAPH_ERROR() if it is not \c IGRAPH_SUCCESS. * */ -/** +/** * \section deallocating_memory Deallocating memory * * @@ -171,7 +181,7 @@ __BEGIN_DECLS * * 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, + * 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. @@ -183,7 +193,7 @@ __BEGIN_DECLS * adjlist.c file in the * \a igraph source for an example. * - * + * * 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. @@ -204,8 +214,8 @@ __BEGIN_DECLS /** * \typedef igraph_error_handler_t - * \brief Type of error handler functions. - * + * \brief The 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. @@ -214,70 +224,69 @@ __BEGIN_DECLS * \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); +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. + * program. */ -extern igraph_error_handler_t igraph_error_handler_abort; +IGRAPH_EXPORT 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. + * with the error code. */ -extern igraph_error_handler_t igraph_error_handler_ignore; +IGRAPH_EXPORT 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. + * standard error and returns with the error code. */ -extern igraph_error_handler_t igraph_error_handler_printignore; +IGRAPH_EXPORT igraph_error_handler_t igraph_error_handler_printignore; /** * \function igraph_set_error_handler - * \brief Set a new error handler. + * \brief Sets 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). + * \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. */ -igraph_error_handler_t* -igraph_set_error_handler(igraph_error_handler_t* new_handler); +IGRAPH_EXPORT 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 + * 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. + * 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 + * \enumval IGRAPH_EINVAL A parameter's value is invalid. E.g. negative * number was specified as the number of vertices. * \enumval IGRAPH_EXISTS A graph/vertex/edge attribute is already * installed with the given name. @@ -287,7 +296,7 @@ igraph_set_error_handler(igraph_error_handler_t* new_handler); * \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, + * \enumval IGRAPH_EFILE A file operation failed. E.g. 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. @@ -317,118 +326,146 @@ igraph_set_error_handler(igraph_error_handler_t* new_handler); * \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 + * \enumval IGRAPH_GLP_EBOUND GLPK error (GLP_EBOUND). + * \enumval IGRAPH_GLP_EROOT GLPK error (GLP_EROOT). + * \enumval IGRAPH_GLP_ENOPFS GLPK error (GLP_ENOPFS). + * \enumval IGRAPH_GLP_ENODFS GLPK error (GLP_ENODFS). + * \enumval IGRAPH_GLP_EFAIL GLPK error (GLP_EFAIL). + * \enumval IGRAPH_GLP_EMIPGAP GLPK error (GLP_EMIPGAP). + * \enumval IGRAPH_GLP_ETMLIM GLPK error (GLP_ETMLIM). + * \enumval IGRAPH_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 + * \enumval IGRAPH_EATTRCOMBINE Unimplemented attribute combination * method for the given attribute type. - * \enumval IGRAPH_ELAPACK A LAPACK call resulted an error. + * \enumval IGRAPH_ELAPACK A LAPACK call resulted in 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. */ 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_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 */ } igraph_error_type_t; +/* Each enum value above must have a corresponding error string in + * igraph_i_error_strings[] in igraph_error.c + * + * Information on undocumented codes: + * - IGRAPH_STOP signals a request to stop in functions like igraph_i_maximal_cliques_bk() + */ + +/* We use IGRAPH_FILE_BASENAME instead of __FILE__ to ensure that full + * paths don't leak into the library code. IGRAPH_FILE_BASENAME is set up + * by the build system when compiling the individual files. However, when + * including igraph_error.h in user code, this macro is not defined so we + * fall back to __FILE__ here + */ +#ifndef IGRAPH_FILE_BASENAME +# define IGRAPH_FILE_BASENAME __FILE__ +#endif /** * \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 + * \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, + * code. E.g. 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. + * 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) +#define IGRAPH_ERROR(reason, igraph_errno) \ + do { \ + igraph_error (reason, IGRAPH_FILE_BASENAME, __LINE__, igraph_errno) ; \ + return igraph_errno ; \ + } while (0) + +#define IGRAPH_ERROR_NO_RETURN(reason, igraph_errno) \ + do { \ + igraph_error (reason, IGRAPH_FILE_BASENAME, __LINE__, igraph_errno) ; \ + } while (0) /** * \function igraph_error - * \brief Trigger an error. + * \brief Triggers an error. * - * \a igraph functions usually call this function (most often via the + * \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. + * supplied arguments. * * \param reason Textual description of the error. * \param file The source file in which the error was noticed. @@ -436,46 +473,76 @@ typedef enum { * error. * \param igraph_errno The \a igraph error code. * \return the error code (if it returns) - * + * * \sa igraph_errorf(). */ -int igraph_error(const char *reason, const char *file, int line, - int igraph_errno); +IGRAPH_EXPORT int igraph_error(const char *reason, const char *file, int line, + int igraph_errno); + +/** + * \define IGRAPH_ERRORF + * \brief Triggers an error, with printf-like syntax. + * + * \a igraph functions can use this macro when they notice an error and + * want to pass on extra information to the user about what went wrong. + * It calls \ref igraph_errorf() 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_errorf() directly. + * \param reason Textual description of the error, a template string + * with the same syntax as the standard printf C library function. + * This should be something more descriptive than the text associated + * with the error code. E.g. 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 what was expected and what was recieved. + * \param igraph_errno The \a igraph error code. + * \param ... The additional arguments to be substituted into the + * template string. + */ + +#define IGRAPH_ERRORF(reason, igraph_errno, ...) \ + do { \ + igraph_errorf(reason, IGRAPH_FILE_BASENAME, __LINE__, \ + igraph_errno, __VA_ARGS__) ; \ + return igraph_errno; \ + } while (0) /** * \function igraph_errorf - * \brief Trigger an error, printf-like version. - * - * \param reason Textual description of the error, interpreted as - * a printf format string. + * \brief Triggers an error, printf-like version. + * + * \param reason Textual description of the error, interpreted as + * a \c 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(). */ -int igraph_errorf(const char *reason, const char *file, int line, - int igraph_errno, ...); +IGRAPH_EXPORT int igraph_errorf(const char *reason, const char *file, int line, + int igraph_errno, ...); -int igraph_errorvf(const char *reason, const char *file, int line, - int igraph_errno, va_list ap); +IGRAPH_EXPORT 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. */ -const char* igraph_strerror(const int igraph_errno); +IGRAPH_EXPORT 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)) @@ -484,35 +551,35 @@ const char* igraph_strerror(const int igraph_errno); /* 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 + * 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*); + int all; + void *ptr; + void (*func)(void*); }; typedef void igraph_finally_func_t (void*); -void IGRAPH_FINALLY_REAL(void (*func)(void*), void* ptr); +IGRAPH_EXPORT void IGRAPH_FINALLY_REAL(void (*func)(void*), void* ptr); /** * \function IGRAPH_FINALLY_CLEAN - * \brief Signal clean deallocation of objects. - * + * \brief Signals 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. + * stack. */ -void IGRAPH_FINALLY_CLEAN(int num); +IGRAPH_EXPORT void IGRAPH_FINALLY_CLEAN(int num); /** * \function IGRAPH_FINALLY_FREE - * \brief Deallocate all registered objects. + * \brief Deallocates all registered objects. * * Calls the destroy function for all objects in the stack of * temporarily allocated objects. This is usually called only from an @@ -522,11 +589,11 @@ void IGRAPH_FINALLY_CLEAN(int num); * as well. */ -void IGRAPH_FINALLY_FREE(void); +IGRAPH_EXPORT void IGRAPH_FINALLY_FREE(void); /** * \function IGRAPH_FINALLY_STACK_SIZE - * \brief Returns the number of registered objects. + * \brief 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 @@ -539,102 +606,126 @@ void IGRAPH_FINALLY_FREE(void); * write your own test cases and examine \ref IGRAPH_FINALLY_STACK_SIZE * before and after your test cases - the numbers should be equal. */ -int IGRAPH_FINALLY_STACK_SIZE(void); +IGRAPH_EXPORT 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. + * \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. + * \brief Registers 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)) +#define IGRAPH_FINALLY(func, ptr) \ + do { \ + /* the following branch makes the compiler check the compatibility of \ + * func and ptr to detect cases when we are accidentally invoking an \ + * incorrect destructor function with the pointer */ \ + if (0) { func(ptr); } \ + IGRAPH_FINALLY_REAL((igraph_finally_func_t*)(func), (ptr)); \ + } while (0) + +#if !defined(GCC_VERSION_MAJOR) && defined(__GNUC__) + #define GCC_VERSION_MAJOR __GNUC__ +#endif -#if (defined(__GNUC__) && GCC_VERSION_MAJOR >= 3) -# define IGRAPH_UNLIKELY(a) __builtin_expect((a), 0) -# define IGRAPH_LIKELY(a) __builtin_expect((a), 1) +#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 + #define IGRAPH_UNLIKELY(a) a + #define IGRAPH_LIKELY(a) a #endif +#if IGRAPH_VERIFY_FINALLY_STACK == 1 +#define IGRAPH_CHECK(a) \ + do { \ + int enter_stack_size = IGRAPH_FINALLY_STACK_SIZE(); \ + int igraph_i_ret=(a); \ + if (IGRAPH_UNLIKELY(igraph_i_ret != 0)) {\ + IGRAPH_ERROR("", igraph_i_ret); \ + } \ + if (IGRAPH_UNLIKELY(enter_stack_size != IGRAPH_FINALLY_STACK_SIZE())) { \ + IGRAPH_ERROR("Non-matching number of IGRAPH_FINALLY and IGRAPH_FINALLY_CLEAN", IGRAPH_FAILURE); \ + } \ + } while (0) +#else /** * \define IGRAPH_CHECK - * \brief Check the return value of a function call. + * \brief Checks 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(&v, 100)); \endverbatim - * - * There is only one reason to use this macro when writing + * + * 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 + * 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 (i.e. non-igraph) calling function. This is achieved * by using IGRAPH_CHECK 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) + int igraph_i_ret=(a); \ + if (IGRAPH_UNLIKELY(igraph_i_ret != 0)) {\ + IGRAPH_ERROR("", igraph_i_ret); \ + } } while (0) +#endif + /** * \section about_igraph_warnings Warning messages - * + * * - * Igraph also supports warning messages in addition to error - * messages. Warning messages typically do not terminate the + * \a 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. * - * + * * - * Igraph warning are handled similarly to errors. There is a + * \a igraph warnings 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 + * an \a 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. * - * + * * - * To trigger a warning, igraph functions typically use the - * \ref IGRAPH_WARNING() macro, the \ref igraph_warning() function, + * To trigger a warning, \a igraph functions typically use the + * \ref IGRAPH_WARNING() macro, the \ref igraph_warning() function, * or if more flexibility is needed, \ref igraph_warningf(). * */ /** * \typedef igraph_warning_handler_t - * Type of igraph warning handler functions - * - * Currently it is defined to have the same type as + * \brief The 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. */ @@ -643,74 +734,263 @@ typedef igraph_error_handler_t igraph_warning_handler_t; /** * \function igraph_set_warning_handler - * Install a warning handler - * + * \brief Installs 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 + * Supply a null pointer here to uninstall the current * warning handler, without installing a new one. * \return The current warning handler function. */ -igraph_warning_handler_t* -igraph_set_warning_handler(igraph_warning_handler_t* new_handler); +IGRAPH_EXPORT 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; +IGRAPH_EXPORT extern igraph_warning_handler_t igraph_warning_handler_ignore; +IGRAPH_EXPORT 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. + * \brief Triggers a warning. + * + * Call this function if you want to trigger a warning from within + * a function that uses \a 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, + * \param igraph_errno Warnings could have potentially error codes as well, * but this is currently not used in igraph. * \return The supplied error code. */ -int igraph_warning(const char *reason, const char *file, int line, - int igraph_errno); +IGRAPH_EXPORT int igraph_warning(const char *reason, const char *file, int line, + int igraph_errno); + +/** + * \define IGRAPH_WARNINGF + * \brief Triggers a warning, with printf-like syntax. + * + * \a igraph functions can use this macro when they notice a warning and + * want to pass on extra information to the user about what went wrong. + * It calls \ref igraph_warningf() with the proper parameters and no + * error code. + * \param reason Textual description of the warning, a template string + * with the same syntax as the standard printf C library function. + * \param ... The additional arguments to be substituted into the + * template string. + */ + +#define IGRAPH_WARNINGF(reason, ...) \ + do { \ + igraph_warningf(reason, IGRAPH_FILE_BASENAME, __LINE__, \ + -1, __VA_ARGS__); \ + } while (0) + + /** * \function igraph_warningf - * Trigger a warning, more flexible printf-like syntax - * + * \brief Triggers a warning, printf-like version. + * * This function is similar to \ref igraph_warning(), but - * uses a printf-like syntax. It substitutes the additional arguments + * 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, + * \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 + * \param ... The additional arguments to be substituted into the * template string. * \return The supplied error code. */ -int igraph_warningf(const char *reason, const char *file, int line, - int igraph_errno, ...); +IGRAPH_EXPORT int igraph_warningf(const char *reason, const char *file, int line, + int igraph_errno, ...); /** * \define IGRAPH_WARNING - * Trigger a warning. - * + * \brief Triggers 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) + do { \ + igraph_warning(reason, IGRAPH_FILE_BASENAME, __LINE__, -1); \ + } while (0) + + +/** + * \section fatal_error_handlers Fatal errors + * + * + * In some rare situations, \a igraph may encounter an internal error + * that cannot be fully handled. In this case, it will call the + * current fatal error handler. The default fatal error handler + * simply prints the error and aborts the program. + * + * + * + * Fatal error handlers do not return. Typically, they might abort the + * the program immediately, or in the case of the high-level \a igraph + * interfaces, they might return to the top level using a + * longjmp(). The fatal error handler is only called when + * a serious error has occurred, and as a result igraph may be in an + * inconsistent state. The purpose of returning to the top level is to + * give the user a chance to save their work instead of aborting immediately. + * However, the program session should be restarted as soon as possible. + * + * + * + * Most projects that use \a igraph will use the default fatal error + * handler. + * + */ + +/** + * \typedef igraph_fatal_handler_t + * \brief The type of igraph fatal error handler functions. + * + * Functions of this type \em must not return. Typically they + * call abort() or do a longjmp(). + * + * \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 + */ + +typedef void igraph_fatal_handler_t (const char *reason, const char *file, int line); + +/** + * \function igraph_set_fatal_handler + * \brief Installs a fatal error handler. + * + * Installs the supplied fatal error handler function. + * + * + * Fatal error handler functions \em must not return. Typically, the fatal + * error handler would either call abort() or longjmp(). + * + * \param new_handler The new fatal error handler function to install. + * Supply a null pointer here to uninstall the current + * fatal error handler, without installing a new one. + * \return The current fatal error handler function. + */ + +IGRAPH_EXPORT igraph_fatal_handler_t* igraph_set_fatal_handler(igraph_fatal_handler_t* new_handler); + +/** + * \var igraph_fatal_handler_abort + * \brief Abort program in case of fatal error. + * + * The default fatal error handler, prints an error message and aborts the program. + */ + +IGRAPH_EXPORT igraph_fatal_handler_t igraph_fatal_handler_abort; + +/** + * \function igraph_fatal + * \brief Triggers a fatal error. + * + * This function triggers a fatal error. Typically it is called indirectly through + * \ref IGRAPH_FATAL() or \ref IGRAPH_ASSERT(). + * + * \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. + */ + +IGRAPH_EXPORT IGRAPH_NORETURN void igraph_fatal(const char *reason, const char *file, int line); + +/** + * \function igraph_fatalf + * \brief Triggers a fatal error, printf-like syntax. + * + * This function is similar to \ref igraph_fatal(), but + * uses a printf-like syntax. It substitutes the additional arguments + * into the \p reason template string and calls \ref igraph_fatal(). + * + * \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 ... The additional arguments to be substituted into the template string. + */ + +IGRAPH_EXPORT IGRAPH_NORETURN void igraph_fatalf(const char *reason, const char *file, int line, ...); + +/** + * \define IGRAPH_FATALF + * \brief Triggers a fatal error, with printf-like syntax. + * + * \a igraph functions can use this macro when a fatal error occurs and + * want to pass on extra information to the user about what went wrong. + * It calls \ref igraph_fatalf() with the proper parameters. + * \param reason Textual description of the error, a template string + * with the same syntax as the standard printf C library function. + * \param ... The additional arguments to be substituted into the + * template string. + */ + +#define IGRAPH_FATALF(reason, ...) \ + do { \ + igraph_fatalf(reason, IGRAPH_FILE_BASENAME, __LINE__, \ + __VA_ARGS__); \ + } while (0) + +/** + * \define IGRAPH_FATAL + * \brief Triggers a fatal error. + * + * This is the usual way of triggering a fatal error from an igraph + * function. It calls \ref igraph_fatal(). + * + * + * Use this macro only in situations where the error cannot be handled. + * The normal way to handle errors is \ref IGRAPH_ERROR(). + * + * \param reason The error message. + */ + +#define IGRAPH_FATAL(reason) \ + do { \ + igraph_fatal(reason, IGRAPH_FILE_BASENAME, __LINE__); \ + } while (0) + +/** + * \define IGRAPH_ASSERT + * \brief igraph-specific replacement for assert(). + * + * This macro is like the standard assert(), but instead of + * calling abort(), it calls \ref igraph_fatal(). This allows for returning + * the control to the calling program, e.g. returning to the top level in a high-level + * \a igraph interface. + * + * + * Unlike assert(), IGRAPH_ASSERT() is not disabled + * when the \c NDEBUG macro is defined. + * + * + * This macro is meant for internal use by \a igraph. + * + * + * Since a typial fatal error handler does a longjmp(), avoid using this + * macro in C++ code. With most compilers, destructor will not be called when + * longjmp() leaves the current scope. + * + * \param condition The condition to be checked. + */ + +#define IGRAPH_ASSERT(condition) \ + do { \ + if (!(condition)) { \ + igraph_fatal("Assertion failed: " #condition, IGRAPH_FILE_BASENAME, __LINE__); \ + } \ + } while (0) __END_DECLS diff --git a/src/rigraph/include/igraph_eulerian.h b/src/rigraph/include/igraph_eulerian.h new file mode 100644 index 0000000..79f669f --- /dev/null +++ b/src/rigraph/include/igraph_eulerian.h @@ -0,0 +1,38 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2009-2012 Gabor Csardi + 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_EULERIAN_H +#define IGRAPH_EULERIAN_H + +#include "igraph_decls.h" +#include "igraph_datatype.h" + +__BEGIN_DECLS + +IGRAPH_EXPORT int igraph_is_eulerian(const igraph_t *graph, igraph_bool_t *has_path, igraph_bool_t *has_cycle); +IGRAPH_EXPORT int igraph_eulerian_path(const igraph_t *graph, igraph_vector_t *edge_res, igraph_vector_t *vertex_res); +IGRAPH_EXPORT int igraph_eulerian_cycle(const igraph_t *graph, igraph_vector_t *edge_res, igraph_vector_t *vertex_res); + +__END_DECLS + +#endif diff --git a/src/rigraph/include/igraph_export.h b/src/rigraph/include/igraph_export.h new file mode 100644 index 0000000..508cb6e --- /dev/null +++ b/src/rigraph/include/igraph_export.h @@ -0,0 +1,11 @@ + +#ifndef IGRAPH_EXPORT_H +#define IGRAPH_EXPORT_H + +#define IGRAPH_EXPORT +#define IGRAPH_NO_EXPORT +#define IGRAPH_DEPRECATED __attribute__ ((__deprecated__)) +#define IGRAPH_DEPRECATED_EXPORT IGRAPH_EXPORT IGRAPH_DEPRECATED +#define IGRAPH_DEPRECATED_NO_EXPORT IGRAPH_NO_EXPORT IGRAPH_DEPRECATED + +#endif /* IGRAPH_EXPORT_H */ diff --git a/src/rigraph/include/igraph_flow.h b/src/rigraph/include/igraph_flow.h new file mode 100644 index 0000000..37ee417 --- /dev/null +++ b/src/rigraph/include/igraph_flow.h @@ -0,0 +1,158 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2003-2012 Gabor Csardi + 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; + +IGRAPH_EXPORT 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_EXPORT 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); + +IGRAPH_EXPORT 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); +IGRAPH_EXPORT 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); + +IGRAPH_EXPORT int igraph_mincut_value(const igraph_t *graph, igraph_real_t *res, + const igraph_vector_t *capacity); +IGRAPH_EXPORT 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); + +IGRAPH_EXPORT 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); +IGRAPH_EXPORT int igraph_vertex_connectivity(const igraph_t *graph, igraph_integer_t *res, + igraph_bool_t checks); + +IGRAPH_EXPORT int igraph_st_edge_connectivity(const igraph_t *graph, igraph_integer_t *res, + igraph_integer_t source, + igraph_integer_t target); +IGRAPH_EXPORT int igraph_edge_connectivity(const igraph_t *graph, igraph_integer_t *res, + igraph_bool_t checks); + +IGRAPH_EXPORT int igraph_edge_disjoint_paths(const igraph_t *graph, igraph_integer_t *res, + igraph_integer_t source, + igraph_integer_t target); +IGRAPH_EXPORT int igraph_vertex_disjoint_paths(const igraph_t *graph, igraph_integer_t *res, + igraph_integer_t source, + igraph_integer_t target); + +IGRAPH_EXPORT int igraph_adhesion(const igraph_t *graph, igraph_integer_t *res, + igraph_bool_t checks); +IGRAPH_EXPORT int igraph_cohesion(const igraph_t *graph, igraph_integer_t *res, + igraph_bool_t checks); + +/* s-t cut listing related stuff */ + +IGRAPH_EXPORT int igraph_even_tarjan_reduction(const igraph_t *graph, igraph_t *graphbar, + igraph_vector_t *capacity); + +IGRAPH_EXPORT 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_EXPORT int igraph_reverse_residual_graph(const igraph_t *graph, + const igraph_vector_t *capacity, + igraph_t *residual, + const igraph_vector_t *flow); + +IGRAPH_EXPORT 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); + +IGRAPH_EXPORT 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); + +IGRAPH_EXPORT 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); + +IGRAPH_EXPORT 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/src/rigraph/include/igraph_foreign.h b/src/rigraph/include/igraph_foreign.h new file mode 100644 index 0000000..e4a9442 --- /dev/null +++ b/src/rigraph/include/igraph_foreign.h @@ -0,0 +1,85 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2009-2012 Gabor Csardi + 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 + +__BEGIN_DECLS + +/* -------------------------------------------------- */ +/* Read and write foreign formats */ +/* -------------------------------------------------- */ + +IGRAPH_EXPORT int igraph_read_graph_edgelist(igraph_t *graph, FILE *instream, + igraph_integer_t n, igraph_bool_t directed); +IGRAPH_EXPORT int igraph_read_graph_ncol(igraph_t *graph, FILE *instream, + const igraph_strvector_t *predefnames, igraph_bool_t names, + igraph_add_weights_t weights, igraph_bool_t directed); +IGRAPH_EXPORT int igraph_read_graph_lgl(igraph_t *graph, FILE *instream, + igraph_bool_t names, igraph_add_weights_t weights, + igraph_bool_t directed); +IGRAPH_EXPORT int igraph_read_graph_pajek(igraph_t *graph, FILE *instream); +IGRAPH_EXPORT int igraph_read_graph_graphml(igraph_t *graph, FILE *instream, + int index); +IGRAPH_EXPORT 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_EXPORT int igraph_read_graph_graphdb(igraph_t *graph, FILE *instream, + igraph_bool_t directed); +IGRAPH_EXPORT int igraph_read_graph_gml(igraph_t *graph, FILE *instream); +IGRAPH_EXPORT int igraph_read_graph_dl(igraph_t *graph, FILE *instream, + igraph_bool_t directed); + +IGRAPH_EXPORT int igraph_write_graph_edgelist(const igraph_t *graph, FILE *outstream); +IGRAPH_EXPORT int igraph_write_graph_ncol(const igraph_t *graph, FILE *outstream, + const char *names, const char *weights); +IGRAPH_EXPORT int igraph_write_graph_lgl(const igraph_t *graph, FILE *outstream, + const char *names, const char *weights, + igraph_bool_t isolates); +IGRAPH_EXPORT int igraph_write_graph_graphml(const igraph_t *graph, FILE *outstream, + igraph_bool_t prefixattr); +IGRAPH_EXPORT int igraph_write_graph_pajek(const igraph_t *graph, FILE *outstream); +IGRAPH_EXPORT int igraph_write_graph_dimacs(const igraph_t *graph, FILE *outstream, + long int source, long int target, + const igraph_vector_t *capacity); +IGRAPH_EXPORT int igraph_write_graph_gml(const igraph_t *graph, FILE *outstream, + const igraph_vector_t *id, const char *creator); +IGRAPH_EXPORT int igraph_write_graph_dot(const igraph_t *graph, FILE *outstream); +IGRAPH_EXPORT 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/src/rigraph/include/igraph_games.h b/src/rigraph/include/igraph_games.h new file mode 100644 index 0000000..df5f6a9 --- /dev/null +++ b/src/rigraph/include/igraph_games.h @@ -0,0 +1,223 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2009-2012 Gabor Csardi + 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) */ +/* -------------------------------------------------- */ + +IGRAPH_EXPORT 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); +IGRAPH_EXPORT 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); +IGRAPH_EXPORT 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); +IGRAPH_EXPORT 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_EXPORT int igraph_degree_sequence_game(igraph_t *graph, const igraph_vector_t *out_deg, + const igraph_vector_t *in_deg, + igraph_degseq_t method); +IGRAPH_EXPORT int igraph_growing_random_game(igraph_t *graph, igraph_integer_t n, + igraph_integer_t m, igraph_bool_t directed, igraph_bool_t citation); +IGRAPH_EXPORT 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); +IGRAPH_EXPORT 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); +IGRAPH_EXPORT 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); +IGRAPH_EXPORT int igraph_callaway_traits_game(igraph_t *graph, igraph_integer_t nodes, + igraph_integer_t types, igraph_integer_t edges_per_step, + const igraph_vector_t *type_dist, + const igraph_matrix_t *pref_matrix, + igraph_bool_t directed, + igraph_vector_t *node_type_vec); +IGRAPH_EXPORT int igraph_establishment_game(igraph_t *graph, igraph_integer_t nodes, + igraph_integer_t types, igraph_integer_t k, + const igraph_vector_t *type_dist, + const igraph_matrix_t *pref_matrix, + igraph_bool_t directed, + igraph_vector_t *node_type_vec); +IGRAPH_EXPORT 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); +IGRAPH_EXPORT 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); +IGRAPH_EXPORT int igraph_asymmetric_preference_game(igraph_t *graph, igraph_integer_t nodes, + igraph_integer_t out_types, + igraph_integer_t in_types, + const igraph_matrix_t *type_dist_matrix, + const igraph_matrix_t *pref_matrix, + igraph_vector_t *node_type_out_vec, + igraph_vector_t *node_type_in_vec, + igraph_bool_t loops); + +IGRAPH_EXPORT int igraph_rewire_edges(igraph_t *graph, igraph_real_t prob, + igraph_bool_t loops, igraph_bool_t multiple); +IGRAPH_EXPORT int igraph_rewire_directed_edges(igraph_t *graph, igraph_real_t prob, + igraph_bool_t loops, igraph_neimode_t mode); + +IGRAPH_EXPORT 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_EXPORT 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); + +IGRAPH_EXPORT 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_EXPORT 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_EXPORT 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); + + +IGRAPH_EXPORT 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_EXPORT int igraph_static_fitness_game(igraph_t *graph, igraph_integer_t no_of_edges, + const igraph_vector_t *fitness_out, const igraph_vector_t *fitness_in, + igraph_bool_t loops, igraph_bool_t multiple); + +IGRAPH_EXPORT 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_EXPORT 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_EXPORT 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); + +IGRAPH_EXPORT 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); + +IGRAPH_EXPORT 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); + +IGRAPH_EXPORT 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); + +IGRAPH_EXPORT int igraph_correlated_pair_game(igraph_t *graph1, igraph_t *graph2, + igraph_integer_t n, igraph_real_t corr, igraph_real_t p, + igraph_bool_t directed, + const igraph_vector_t *permutation); + +IGRAPH_EXPORT int igraph_tree_game(igraph_t *graph, igraph_integer_t n, igraph_bool_t directed, + igraph_random_tree_t method); + +IGRAPH_EXPORT int igraph_dot_product_game(igraph_t *graph, const igraph_matrix_t *vecs, + igraph_bool_t directed); + +IGRAPH_EXPORT 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_EXPORT 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_EXPORT int igraph_sample_dirichlet(igraph_integer_t n, const igraph_vector_t *alpha, + igraph_matrix_t *res); + +__END_DECLS + +#endif diff --git a/src/rigraph/include/igraph_graphicality.h b/src/rigraph/include/igraph_graphicality.h new file mode 100644 index 0000000..02feabf --- /dev/null +++ b/src/rigraph/include/igraph_graphicality.h @@ -0,0 +1,65 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2009-2020 Gabor Csardi + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU 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 . +*/ + +#ifndef IGRAPH_GRAPHICALITY_H +#define IGRAPH_GRAPHICALITY_H + +#include "igraph_decls.h" +#include "igraph_datatype.h" + +__BEGIN_DECLS + +typedef unsigned char igraph_edge_type_sw_t; + +/* + * bit 0: self-loops alowed? + * bit 1: more than one edge allowed between distinct vertices? + * bit 2: more than one self-loop allowed (assuming bit 0 is set)? + */ +enum { + IGRAPH_SIMPLE_SW = 0x00, /* 000 */ + IGRAPH_LOOPS_SW = 0x01, /* 001 */ + IGRAPH_MULTI_SW = 0x06 /* 110 */ +}; + +IGRAPH_EXPORT int igraph_is_graphical(const igraph_vector_t *out_degrees, + const igraph_vector_t *in_degrees, + const igraph_edge_type_sw_t allowed_edge_types, + igraph_bool_t *res); + +IGRAPH_EXPORT int igraph_is_bigraphical(const igraph_vector_t *degrees1, + const igraph_vector_t *degrees2, + const igraph_edge_type_sw_t allowed_edge_types, + igraph_bool_t *res); + + +/* Legacy functions (deprecated): */ + +IGRAPH_EXPORT IGRAPH_DEPRECATED int igraph_is_degree_sequence(const igraph_vector_t *out_degrees, + const igraph_vector_t *in_degrees, + igraph_bool_t *res); + +IGRAPH_EXPORT IGRAPH_DEPRECATED int igraph_is_graphical_degree_sequence(const igraph_vector_t *out_degrees, + const igraph_vector_t *in_degrees, + igraph_bool_t *res); + +__END_DECLS + +#endif // IGRAPH_GRAPHICALITY_H diff --git a/src/rigraph/include/igraph_graphlets.h b/src/rigraph/include/igraph_graphlets.h new file mode 100644 index 0000000..6b52853 --- /dev/null +++ b/src/rigraph/include/igraph_graphlets.h @@ -0,0 +1,52 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2013 Gabor Csardi + 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 + +IGRAPH_EXPORT int igraph_graphlets_candidate_basis(const igraph_t *graph, + const igraph_vector_t *weights, + igraph_vector_ptr_t *cliques, + igraph_vector_t *thresholds); + +IGRAPH_EXPORT 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); + +IGRAPH_EXPORT 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/src/leidenalg/igraph-R/igraph_heap.h b/src/rigraph/include/igraph_heap.h similarity index 90% rename from src/leidenalg/igraph-R/igraph_heap.h rename to src/rigraph/include/igraph_heap.h index 5e46ed9..3729226 100644 --- a/src/leidenalg/igraph-R/igraph_heap.h +++ b/src/rigraph/include/igraph_heap.h @@ -1,22 +1,22 @@ /* -*- mode: C -*- */ -/* +/* IGraph library. Copyright (C) 2009-2012 Gabor Csardi 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 + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -24,15 +24,7 @@ #ifndef IGRAPH_HEAP_H #define IGRAPH_HEAP_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_decls.h" __BEGIN_DECLS diff --git a/src/rigraph/include/igraph_heap_pmt.h b/src/rigraph/include/igraph_heap_pmt.h new file mode 100644 index 0000000..437a199 --- /dev/null +++ b/src/rigraph/include/igraph_heap_pmt.h @@ -0,0 +1,39 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2007-2012 Gabor Csardi + 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); + +IGRAPH_EXPORT int FUNCTION(igraph_heap, init)(TYPE(igraph_heap)* h, long int size); +IGRAPH_EXPORT int FUNCTION(igraph_heap, init_array)(TYPE(igraph_heap) *t, BASE* data, long int len); +IGRAPH_EXPORT void FUNCTION(igraph_heap, destroy)(TYPE(igraph_heap)* h); +IGRAPH_EXPORT igraph_bool_t FUNCTION(igraph_heap, empty)(TYPE(igraph_heap)* h); +IGRAPH_EXPORT int FUNCTION(igraph_heap, push)(TYPE(igraph_heap)* h, BASE elem); +IGRAPH_EXPORT BASE FUNCTION(igraph_heap, top)(TYPE(igraph_heap)* h); +IGRAPH_EXPORT BASE FUNCTION(igraph_heap, delete_top)(TYPE(igraph_heap)* h); +IGRAPH_EXPORT long int FUNCTION(igraph_heap, size)(TYPE(igraph_heap)* h); +IGRAPH_EXPORT int FUNCTION(igraph_heap, reserve)(TYPE(igraph_heap)* h, long int size); diff --git a/src/leidenalg/igraph-R/igraph_hrg.h b/src/rigraph/include/igraph_hrg.h similarity index 54% rename from src/leidenalg/igraph-R/igraph_hrg.h rename to src/rigraph/include/igraph_hrg.h index cb50518..e81efb2 100644 --- a/src/leidenalg/igraph-R/igraph_hrg.h +++ b/src/rigraph/include/igraph_hrg.h @@ -1,22 +1,22 @@ /* -*- mode: C -*- */ -/* +/* IGraph library. Copyright (C) 2009-2012 Gabor Csardi 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 + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -24,16 +24,7 @@ #ifndef IGRAPH_HRG_H #define IGRAPH_HRG_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_decls.h" #include "igraph_vector.h" #include "igraph_vector_ptr.h" #include "igraph_datatype.h" @@ -43,15 +34,15 @@ __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. - * + * * 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. - * + * * * It has the following members: * \member left Vector that contains the left children of the internal @@ -74,50 +65,51 @@ __BEGIN_DECLS */ typedef struct igraph_hrg_t { - igraph_vector_t left, right, prob, edges, vertices; + igraph_vector_t left, right, prob, edges, vertices; } igraph_hrg_t; -int igraph_hrg_init(igraph_hrg_t *hrg, int n); -void igraph_hrg_destroy(igraph_hrg_t *hrg); -int igraph_hrg_size(const igraph_hrg_t *hrg); -int igraph_hrg_resize(igraph_hrg_t *hrg, int newsize); - -int igraph_hrg_fit(const igraph_t *graph, - igraph_hrg_t *hrg, - igraph_bool_t start, - int steps); - -int igraph_hrg_sample(const igraph_t *graph, - igraph_t *sample, - igraph_vector_ptr_t *samples, - igraph_hrg_t *hrg, - igraph_bool_t start); - -int igraph_hrg_game(igraph_t *graph, - const igraph_hrg_t *hrg); - -int igraph_hrg_dendrogram(igraph_t *graph, - const igraph_hrg_t *hrg); - -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); - -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); - -int igraph_hrg_create(igraph_hrg_t *hrg, - const igraph_t *graph, - const igraph_vector_t *prob); +IGRAPH_EXPORT int igraph_hrg_init(igraph_hrg_t *hrg, int n); +IGRAPH_EXPORT void igraph_hrg_destroy(igraph_hrg_t *hrg); +IGRAPH_EXPORT int igraph_hrg_size(const igraph_hrg_t *hrg); +IGRAPH_EXPORT int igraph_hrg_resize(igraph_hrg_t *hrg, int newsize); + +IGRAPH_EXPORT int igraph_hrg_fit(const igraph_t *graph, + igraph_hrg_t *hrg, + igraph_bool_t start, + int steps); + +IGRAPH_EXPORT int igraph_hrg_sample(const igraph_t *graph, + igraph_t *sample, + igraph_vector_ptr_t *samples, + igraph_integer_t no_samples, + igraph_hrg_t *hrg, + igraph_bool_t start); + +IGRAPH_EXPORT int igraph_hrg_game(igraph_t *graph, + const igraph_hrg_t *hrg); + +IGRAPH_EXPORT int igraph_hrg_dendrogram(igraph_t *graph, + const igraph_hrg_t *hrg); + +IGRAPH_EXPORT 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); + +IGRAPH_EXPORT 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); + +IGRAPH_EXPORT int igraph_hrg_create(igraph_hrg_t *hrg, + const igraph_t *graph, + const igraph_vector_t *prob); __END_DECLS -#endif /* IGRAPH_HRG_H */ +#endif /* IGRAPH_HRG_H */ diff --git a/src/rigraph/include/igraph_interface.h b/src/rigraph/include/igraph_interface.h new file mode 100644 index 0000000..d8ec568 --- /dev/null +++ b/src/rigraph/include/igraph_interface.h @@ -0,0 +1,124 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2009-2012 Gabor Csardi + 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 */ +/* -------------------------------------------------- */ + +IGRAPH_EXPORT int igraph_empty(igraph_t *graph, igraph_integer_t n, igraph_bool_t directed); +IGRAPH_EXPORT int igraph_empty_attrs(igraph_t *graph, igraph_integer_t n, igraph_bool_t directed, void *attr); +IGRAPH_EXPORT void igraph_destroy(igraph_t *graph); +IGRAPH_EXPORT int igraph_copy(igraph_t *to, const igraph_t *from); +IGRAPH_EXPORT int igraph_add_edges(igraph_t *graph, const igraph_vector_t *edges, + void *attr); +IGRAPH_EXPORT int igraph_add_vertices(igraph_t *graph, igraph_integer_t nv, + void *attr); +IGRAPH_EXPORT int igraph_delete_edges(igraph_t *graph, igraph_es_t edges); +IGRAPH_EXPORT int igraph_delete_vertices(igraph_t *graph, const igraph_vs_t vertices); +IGRAPH_EXPORT int igraph_delete_vertices_idx(igraph_t *graph, const igraph_vs_t vertices, + igraph_vector_t *idx, + igraph_vector_t *invidx); +IGRAPH_EXPORT igraph_integer_t igraph_vcount(const igraph_t *graph); +IGRAPH_EXPORT igraph_integer_t igraph_ecount(const igraph_t *graph); +IGRAPH_EXPORT int igraph_neighbors(const igraph_t *graph, igraph_vector_t *neis, igraph_integer_t vid, + igraph_neimode_t mode); +IGRAPH_EXPORT igraph_bool_t igraph_is_directed(const igraph_t *graph); +IGRAPH_EXPORT int igraph_degree(const igraph_t *graph, igraph_vector_t *res, + const igraph_vs_t vids, igraph_neimode_t mode, + igraph_bool_t loops); +IGRAPH_EXPORT int igraph_edge(const igraph_t *graph, igraph_integer_t eid, + igraph_integer_t *from, igraph_integer_t *to); +IGRAPH_EXPORT int igraph_edges(const igraph_t *graph, igraph_es_t eids, + igraph_vector_t *edges); +IGRAPH_EXPORT 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); +IGRAPH_EXPORT 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); +IGRAPH_EXPORT 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); +IGRAPH_EXPORT int igraph_incident(const igraph_t *graph, igraph_vector_t *eids, igraph_integer_t vid, + igraph_neimode_t mode); +IGRAPH_EXPORT int igraph_is_same_graph(const igraph_t *graph1, const igraph_t *igraph2, igraph_bool_t *res); + +/** + * \define IGRAPH_FROM + * \brief The source vertex of an edge. + * + * Faster than \ref igraph_edge(), but no error checking is done: \p eid is assumed to be valid. + * + * \param graph The graph. + * \param eid The edge ID. + * \return The source vertex of the edge. + * \sa \ref igraph_edge() if error checking is desired. + */ +#define IGRAPH_FROM(graph,eid) ((igraph_integer_t)(VECTOR((graph)->from)[(long int)(eid)])) + +/** + * \define IGRAPH_TO + * \brief The target vertex of an edge. + * + * Faster than \ref igraph_edge(), but no error checking is done: \p eid is assumed to be valid. + * + * \param graph The graph object. + * \param eid The edge ID. + * \return The target vertex of the edge. + * \sa \ref igraph_edge() if error checking is desired. + */ +#define IGRAPH_TO(graph,eid) ((igraph_integer_t)(VECTOR((graph)->to) [(long int)(eid)])) + +/** + * \define IGRAPH_OTHER + * \brief The other endpoint of an edge. + * + * Typically used with undirected edges when one endpoint of the edge is known, + * and the other endpoint is needed. No error checking is done: + * \p eid and \p vid are assumed to be valid. + * + * \param graph The graph object. + * \param eid The edge ID. + * \param vid The vertex ID of one endpoint of an edge. + * \return The other endpoint of the edge. + * \sa \ref IGRAPH_TO() and \ref IGRAPH_FROM() to get the source and target + * of directed edges. + */ +#define IGRAPH_OTHER(graph,eid,vid) \ + ((igraph_integer_t)(IGRAPH_TO(graph,(eid))==(vid) ? IGRAPH_FROM((graph),(eid)) : IGRAPH_TO((graph),(eid)))) + +__END_DECLS + +#endif diff --git a/src/leidenalg/igraph-R/igraph_interrupt.h b/src/rigraph/include/igraph_interrupt.h similarity index 90% rename from src/leidenalg/igraph-R/igraph_interrupt.h rename to src/rigraph/include/igraph_interrupt.h index 8907401..1c32b69 100644 --- a/src/leidenalg/igraph-R/igraph_interrupt.h +++ b/src/rigraph/include/igraph_interrupt.h @@ -1,22 +1,22 @@ /* -*- mode: C -*- */ -/* +/* IGraph library. Copyright (C) 2003-2012 Gabor Csardi 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 + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -24,18 +24,9 @@ #ifndef IGRAPH_INTERRUPT_H #define IGRAPH_INTERRUPT_H +#include "igraph_decls.h" #include "igraph_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 /* This file contains the igraph interruption handling. */ @@ -81,7 +72,7 @@ __BEGIN_DECLS * interface writers, because usually this is the only way to allow handling * of Ctrl-C and similar keypresses properly. * - * + * * 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 @@ -108,9 +99,9 @@ __BEGIN_DECLS /** * \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. */ @@ -121,17 +112,16 @@ 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 + * \ref IGRAPH_ALLOW_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. */ -int igraph_allow_interruption(void* data); +IGRAPH_EXPORT int igraph_allow_interruption(void* data); -igraph_interruption_handler_t * -igraph_set_interruption_handler (igraph_interruption_handler_t * new_handler); +IGRAPH_EXPORT igraph_interruption_handler_t * igraph_set_interruption_handler (igraph_interruption_handler_t * new_handler); __END_DECLS diff --git a/src/leidenalg/igraph-R/igraph_iterators.h b/src/rigraph/include/igraph_iterators.h similarity index 54% rename from src/leidenalg/igraph-R/igraph_iterators.h rename to src/rigraph/include/igraph_iterators.h index 224e9cb..2b91337 100644 --- a/src/leidenalg/igraph-R/igraph_iterators.h +++ b/src/rigraph/include/igraph_iterators.h @@ -1,22 +1,22 @@ /* -*- mode: C -*- */ -/* +/* IGraph library. Copyright (C) 2009-2012 Gabor Csardi 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 + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -24,16 +24,7 @@ #ifndef IGRAPH_ITERATORS_H #define IGRAPH_ITERATORS_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_decls.h" #include "igraph_constants.h" __BEGIN_DECLS @@ -52,60 +43,59 @@ __BEGIN_DECLS #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; + 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; - -int igraph_vs_all(igraph_vs_t *vs); -igraph_vs_t igraph_vss_all(void); -int igraph_vs_adj(igraph_vs_t *vs, - igraph_integer_t vid, igraph_neimode_t mode); -igraph_vs_t igraph_vss_adj(igraph_integer_t vid, igraph_neimode_t mode); +IGRAPH_EXPORT int igraph_vs_all(igraph_vs_t *vs); +IGRAPH_EXPORT igraph_vs_t igraph_vss_all(void); + +IGRAPH_EXPORT int igraph_vs_adj(igraph_vs_t *vs, + igraph_integer_t vid, igraph_neimode_t mode); -int igraph_vs_nonadj(igraph_vs_t *vs, igraph_integer_t vid, - igraph_neimode_t mode); +IGRAPH_EXPORT int igraph_vs_nonadj(igraph_vs_t *vs, igraph_integer_t vid, + igraph_neimode_t mode); -int igraph_vs_none(igraph_vs_t *vs); -igraph_vs_t igraph_vss_none(void); +IGRAPH_EXPORT int igraph_vs_none(igraph_vs_t *vs); +IGRAPH_EXPORT igraph_vs_t igraph_vss_none(void); -int igraph_vs_1(igraph_vs_t *vs, igraph_integer_t vid); -igraph_vs_t igraph_vss_1(igraph_integer_t vid); +IGRAPH_EXPORT int igraph_vs_1(igraph_vs_t *vs, igraph_integer_t vid); +IGRAPH_EXPORT igraph_vs_t igraph_vss_1(igraph_integer_t vid); -int igraph_vs_vector(igraph_vs_t *vs, - const igraph_vector_t *v); -igraph_vs_t igraph_vss_vector(const igraph_vector_t *v); +IGRAPH_EXPORT int igraph_vs_vector(igraph_vs_t *vs, + const igraph_vector_t *v); +IGRAPH_EXPORT igraph_vs_t igraph_vss_vector(const igraph_vector_t *v); -int igraph_vs_vector_small(igraph_vs_t *vs, ...); +IGRAPH_EXPORT int igraph_vs_vector_small(igraph_vs_t *vs, ...); -int igraph_vs_vector_copy(igraph_vs_t *vs, - const igraph_vector_t *v); +IGRAPH_EXPORT int igraph_vs_vector_copy(igraph_vs_t *vs, + const igraph_vector_t *v); -int igraph_vs_seq(igraph_vs_t *vs, igraph_integer_t from, igraph_integer_t to); -igraph_vs_t igraph_vss_seq(igraph_integer_t from, igraph_integer_t to); +IGRAPH_EXPORT int igraph_vs_seq(igraph_vs_t *vs, igraph_integer_t from, igraph_integer_t to); +IGRAPH_EXPORT igraph_vs_t igraph_vss_seq(igraph_integer_t from, igraph_integer_t to); -void igraph_vs_destroy(igraph_vs_t *vs); +IGRAPH_EXPORT void igraph_vs_destroy(igraph_vs_t *vs); -igraph_bool_t igraph_vs_is_all(const igraph_vs_t *vs); +IGRAPH_EXPORT igraph_bool_t igraph_vs_is_all(const igraph_vs_t *vs); -int igraph_vs_copy(igraph_vs_t* dest, const igraph_vs_t* src); +IGRAPH_EXPORT int igraph_vs_copy(igraph_vs_t* dest, const igraph_vs_t* src); -int igraph_vs_as_vector(const igraph_t *graph, igraph_vs_t vs, - igraph_vector_t *v); -int igraph_vs_size(const igraph_t *graph, const igraph_vs_t *vs, - igraph_integer_t *result); -int igraph_vs_type(const igraph_vs_t *vs); +IGRAPH_EXPORT int igraph_vs_as_vector(const igraph_t *graph, igraph_vs_t vs, + igraph_vector_t *v); +IGRAPH_EXPORT int igraph_vs_size(const igraph_t *graph, const igraph_vs_t *vs, + igraph_integer_t *result); +IGRAPH_EXPORT int igraph_vs_type(const igraph_vs_t *vs); /* -------------------------------------------------- */ /* Vertex iterators */ @@ -116,11 +106,11 @@ int igraph_vs_type(const igraph_vs_t *vs); #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; + int type; + long int pos; + long int start; + long int end; + const igraph_vector_t *vec; } igraph_vit_t; /** @@ -160,68 +150,68 @@ typedef struct igraph_vit_t { /** * \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. + * 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])) + ((igraph_integer_t)(((vit).type == IGRAPH_VIT_SEQ) ? (vit).pos : \ + VECTOR(*(vit).vec)[(vit).pos])) -int igraph_vit_create(const igraph_t *graph, - igraph_vs_t vs, igraph_vit_t *vit); -void igraph_vit_destroy(const igraph_vit_t *vit); +IGRAPH_EXPORT int igraph_vit_create(const igraph_t *graph, + igraph_vs_t vs, igraph_vit_t *vit); +IGRAPH_EXPORT void igraph_vit_destroy(const igraph_vit_t *vit); -int igraph_vit_as_vector(const igraph_vit_t *vit, igraph_vector_t *v); +IGRAPH_EXPORT int igraph_vit_as_vector(const igraph_vit_t *vit, igraph_vector_t *v); /* -------------------------------------------------- */ /* Edge Selectors */ @@ -241,75 +231,73 @@ int igraph_vit_as_vector(const igraph_vit_t *vit, igraph_vector_t *v); #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; + 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; -int igraph_es_all(igraph_es_t *es, - igraph_edgeorder_type_t order); -igraph_es_t igraph_ess_all(igraph_edgeorder_type_t order); +IGRAPH_EXPORT int igraph_es_all(igraph_es_t *es, + igraph_edgeorder_type_t order); +IGRAPH_EXPORT igraph_es_t igraph_ess_all(igraph_edgeorder_type_t order); -int igraph_es_adj(igraph_es_t *es, - igraph_integer_t vid, igraph_neimode_t mode); /* deprecated */ -int igraph_es_incident(igraph_es_t *es, - igraph_integer_t vid, igraph_neimode_t mode); +IGRAPH_EXPORT int igraph_es_incident(igraph_es_t *es, + igraph_integer_t vid, igraph_neimode_t mode); -int igraph_es_none(igraph_es_t *es); -igraph_es_t igraph_ess_none(void); +IGRAPH_EXPORT int igraph_es_none(igraph_es_t *es); +IGRAPH_EXPORT igraph_es_t igraph_ess_none(void); -int igraph_es_1(igraph_es_t *es, igraph_integer_t eid); -igraph_es_t igraph_ess_1(igraph_integer_t eid); +IGRAPH_EXPORT int igraph_es_1(igraph_es_t *es, igraph_integer_t eid); +IGRAPH_EXPORT igraph_es_t igraph_ess_1(igraph_integer_t eid); -int igraph_es_vector(igraph_es_t *es, - const igraph_vector_t *v); -igraph_es_t igraph_ess_vector(const igraph_vector_t *v); +IGRAPH_EXPORT int igraph_es_vector(igraph_es_t *es, + const igraph_vector_t *v); +IGRAPH_EXPORT igraph_es_t igraph_ess_vector(const igraph_vector_t *v); -int igraph_es_fromto(igraph_es_t *es, - igraph_vs_t from, igraph_vs_t to); +IGRAPH_EXPORT int igraph_es_fromto(igraph_es_t *es, + igraph_vs_t from, igraph_vs_t to); -int igraph_es_seq(igraph_es_t *es, igraph_integer_t from, igraph_integer_t to); -igraph_es_t igraph_ess_seq(igraph_integer_t from, igraph_integer_t to); +IGRAPH_EXPORT int igraph_es_seq(igraph_es_t *es, igraph_integer_t from, igraph_integer_t to); +IGRAPH_EXPORT igraph_es_t igraph_ess_seq(igraph_integer_t from, igraph_integer_t to); -int igraph_es_vector_copy(igraph_es_t *es, const igraph_vector_t *v); +IGRAPH_EXPORT int igraph_es_vector_copy(igraph_es_t *es, const igraph_vector_t *v); -int igraph_es_pairs(igraph_es_t *es, const igraph_vector_t *v, - igraph_bool_t directed); -int igraph_es_pairs_small(igraph_es_t *es, igraph_bool_t directed, ...); +IGRAPH_EXPORT int igraph_es_pairs(igraph_es_t *es, const igraph_vector_t *v, + igraph_bool_t directed); +IGRAPH_EXPORT int igraph_es_pairs_small(igraph_es_t *es, igraph_bool_t directed, ...); -int igraph_es_multipairs(igraph_es_t *es, const igraph_vector_t *v, - igraph_bool_t directed); +IGRAPH_EXPORT int igraph_es_multipairs(igraph_es_t *es, const igraph_vector_t *v, + igraph_bool_t directed); -int igraph_es_path(igraph_es_t *es, const igraph_vector_t *v, - igraph_bool_t directed); -int igraph_es_path_small(igraph_es_t *es, igraph_bool_t directed, ...); +IGRAPH_EXPORT int igraph_es_path(igraph_es_t *es, const igraph_vector_t *v, + igraph_bool_t directed); +IGRAPH_EXPORT int igraph_es_path_small(igraph_es_t *es, igraph_bool_t directed, ...); -void igraph_es_destroy(igraph_es_t *es); +IGRAPH_EXPORT void igraph_es_destroy(igraph_es_t *es); -igraph_bool_t igraph_es_is_all(const igraph_es_t *es); +IGRAPH_EXPORT igraph_bool_t igraph_es_is_all(const igraph_es_t *es); -int igraph_es_copy(igraph_es_t* dest, const igraph_es_t* src); +IGRAPH_EXPORT int igraph_es_copy(igraph_es_t* dest, const igraph_es_t* src); -int igraph_es_as_vector(const igraph_t *graph, igraph_es_t es, - igraph_vector_t *v); -int igraph_es_size(const igraph_t *graph, const igraph_es_t *es, - igraph_integer_t *result); -int igraph_es_type(const igraph_es_t *es); +IGRAPH_EXPORT int igraph_es_as_vector(const igraph_t *graph, igraph_es_t es, + igraph_vector_t *v); +IGRAPH_EXPORT int igraph_es_size(const igraph_t *graph, const igraph_es_t *es, + igraph_integer_t *result); +IGRAPH_EXPORT int igraph_es_type(const igraph_es_t *es); /* -------------------------------------------------- */ @@ -321,16 +309,16 @@ int igraph_es_type(const igraph_es_t *es); #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; + 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 - * + * * 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 @@ -343,18 +331,18 @@ typedef struct igraph_eit_t { /** * \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 @@ -366,44 +354,44 @@ typedef struct igraph_eit_t { /** * \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])) + (igraph_integer_t)((((eit).type == IGRAPH_EIT_SEQ) ? (eit).pos : \ + VECTOR(*(eit).vec)[(eit).pos])) -int igraph_eit_create(const igraph_t *graph, - igraph_es_t es, igraph_eit_t *eit); -void igraph_eit_destroy(const igraph_eit_t *eit); +IGRAPH_EXPORT int igraph_eit_create(const igraph_t *graph, + igraph_es_t es, igraph_eit_t *eit); +IGRAPH_EXPORT void igraph_eit_destroy(const igraph_eit_t *eit); -int igraph_eit_as_vector(const igraph_eit_t *eit, igraph_vector_t *v); +IGRAPH_EXPORT int igraph_eit_as_vector(const igraph_eit_t *eit, igraph_vector_t *v); __END_DECLS diff --git a/src/rigraph/include/igraph_lapack.h b/src/rigraph/include/igraph_lapack.h new file mode 100644 index 0000000..79b3c02 --- /dev/null +++ b/src/rigraph/include/igraph_lapack.h @@ -0,0 +1,111 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2010-2012 Gabor Csardi + 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_decls.h" +#include "igraph_vector.h" +#include "igraph_matrix.h" + +__BEGIN_DECLS + +/** + * \section about_lapack LAPACK interface in igraph + * + * + * 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. + * + * + * + * igraph provides an interface to a very limited set of LAPACK + * functions, using the regular igraph data structures. + * + * + * + * See more about LAPACK at http://www.netlib.org/lapack/ + * + */ + +IGRAPH_EXPORT int igraph_lapack_dgetrf(igraph_matrix_t *a, igraph_vector_int_t *ipiv, + int *info); +IGRAPH_EXPORT int igraph_lapack_dgetrs(igraph_bool_t transpose, const igraph_matrix_t *a, + const igraph_vector_int_t *ipiv, igraph_matrix_t *b); +IGRAPH_EXPORT 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; + +IGRAPH_EXPORT 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? */ + +IGRAPH_EXPORT 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; + +IGRAPH_EXPORT 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); + +IGRAPH_EXPORT int igraph_lapack_dgehrd(const igraph_matrix_t *A, + int ilo, int ihi, + igraph_matrix_t *result); + +__END_DECLS + +#endif diff --git a/src/rigraph/include/igraph_layout.h b/src/rigraph/include/igraph_layout.h new file mode 100644 index 0000000..16c7b3c --- /dev/null +++ b/src/rigraph/include/igraph_layout.h @@ -0,0 +1,258 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2009-2012 Gabor Csardi + 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 + +/** + * \section about_layouts + * + * 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. + * + * 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. + */ + +/* -------------------------------------------------- */ +/* Layouts */ +/* -------------------------------------------------- */ + +IGRAPH_EXPORT int igraph_layout_random(const igraph_t *graph, igraph_matrix_t *res); +IGRAPH_EXPORT int igraph_layout_circle(const igraph_t *graph, igraph_matrix_t *res, + igraph_vs_t order); +IGRAPH_EXPORT int igraph_layout_star(const igraph_t *graph, igraph_matrix_t *res, + igraph_integer_t center, const igraph_vector_t *order); +IGRAPH_EXPORT int igraph_layout_grid(const igraph_t *graph, igraph_matrix_t *res, long int width); +IGRAPH_EXPORT 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_EXPORT 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_EXPORT 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); +IGRAPH_EXPORT 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); +IGRAPH_EXPORT 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); +IGRAPH_EXPORT 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); + +IGRAPH_EXPORT int igraph_layout_random_3d(const igraph_t *graph, igraph_matrix_t *res); +IGRAPH_EXPORT int igraph_layout_sphere(const igraph_t *graph, igraph_matrix_t *res); +IGRAPH_EXPORT int igraph_layout_grid_3d(const igraph_t *graph, igraph_matrix_t *res, + long int width, long int height); +IGRAPH_EXPORT 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_EXPORT 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_EXPORT 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); + +IGRAPH_EXPORT int igraph_layout_mds(const igraph_t *graph, igraph_matrix_t *res, + const igraph_matrix_t *dist, long int dim); + +IGRAPH_EXPORT 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; + +IGRAPH_EXPORT int igraph_layout_drl_options_init(igraph_layout_drl_options_t *options, + igraph_layout_drl_default_t templ); +IGRAPH_EXPORT int igraph_layout_drl(const igraph_t *graph, igraph_matrix_t *res, + igraph_bool_t use_seed, + const igraph_layout_drl_options_t *options, + const igraph_vector_t *weights, + const igraph_vector_bool_t *fixed); + +IGRAPH_EXPORT int igraph_layout_drl_3d(const igraph_t *graph, igraph_matrix_t *res, + igraph_bool_t use_seed, + const igraph_layout_drl_options_t *options, + const igraph_vector_t *weights, + const igraph_vector_bool_t *fixed); + +IGRAPH_EXPORT int igraph_layout_merge_dla(const igraph_vector_ptr_t *graphs, + const igraph_vector_ptr_t *coords, + igraph_matrix_t *res); + +IGRAPH_EXPORT 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_EXPORT 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/src/rigraph/include/igraph_lsap.h b/src/rigraph/include/igraph_lsap.h new file mode 100644 index 0000000..eeffede --- /dev/null +++ b/src/rigraph/include/igraph_lsap.h @@ -0,0 +1,17 @@ + +#ifndef IGRAPH_LSAP_H +#define IGRAPH_LSAP_H + +#include "igraph_decls.h" +#include "igraph_matrix.h" +#include "igraph_vector.h" +#include "igraph_types.h" + +__BEGIN_DECLS + +IGRAPH_EXPORT int igraph_solve_lsap(igraph_matrix_t *c, igraph_integer_t n, + igraph_vector_int_t *p); + +__END_DECLS + +#endif diff --git a/src/rigraph/include/igraph_matching.h b/src/rigraph/include/igraph_matching.h new file mode 100644 index 0000000..c9a1540 --- /dev/null +++ b/src/rigraph/include/igraph_matching.h @@ -0,0 +1,56 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + 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 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public 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 */ +/* -------------------------------------------------- */ + +IGRAPH_EXPORT int igraph_is_matching(const igraph_t* graph, + const igraph_vector_bool_t* types, const igraph_vector_long_t* matching, + igraph_bool_t* result); +IGRAPH_EXPORT 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); + +IGRAPH_EXPORT 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); + +IGRAPH_EXPORT 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/src/leidenalg/igraph-R/igraph_matrix.h b/src/rigraph/include/igraph_matrix.h similarity index 79% rename from src/leidenalg/igraph-R/igraph_matrix.h rename to src/rigraph/include/igraph_matrix.h index 34e050c..a8b8b5e 100644 --- a/src/leidenalg/igraph-R/igraph_matrix.h +++ b/src/rigraph/include/igraph_matrix.h @@ -1,22 +1,22 @@ /* -*- mode: C -*- */ -/* +/* IGraph library. Copyright (C) 2009-2012 Gabor Csardi 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 + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -24,16 +24,7 @@ #ifndef IGRAPH_MATRIX_H #define IGRAPH_MATRIX_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_decls.h" #include "igraph_vector.h" __BEGIN_DECLS @@ -80,16 +71,16 @@ __BEGIN_DECLS #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) + 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 redefines as a proper function later. + * 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. @@ -98,9 +89,11 @@ __BEGIN_DECLS */ #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); +IGRAPH_EXPORT igraph_bool_t igraph_matrix_all_e_tol(const igraph_matrix_t *lhs, + const igraph_matrix_t *rhs, + igraph_real_t tol); + +IGRAPH_EXPORT int igraph_matrix_zapsmall(igraph_matrix_t *m, igraph_real_t tol); __END_DECLS diff --git a/src/rigraph/include/igraph_matrix_pmt.h b/src/rigraph/include/igraph_matrix_pmt.h new file mode 100644 index 0000000..81a7353 --- /dev/null +++ b/src/rigraph/include/igraph_matrix_pmt.h @@ -0,0 +1,244 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2007-2012 Gabor Csardi + 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 */ +/*---------------*/ + +IGRAPH_EXPORT int FUNCTION(igraph_matrix, init)(TYPE(igraph_matrix) *m, + long int nrow, long int ncol); +IGRAPH_EXPORT int FUNCTION(igraph_matrix, copy)(TYPE(igraph_matrix) *to, + const TYPE(igraph_matrix) *from); +IGRAPH_EXPORT void FUNCTION(igraph_matrix, destroy)(TYPE(igraph_matrix) *m); +IGRAPH_EXPORT long int FUNCTION(igraph_matrix, capacity)(const TYPE(igraph_matrix) *m); + +/*--------------------*/ +/* Accessing elements */ +/*--------------------*/ + +/* MATRIX */ +IGRAPH_EXPORT BASE FUNCTION(igraph_matrix, e)(const TYPE(igraph_matrix) *m, + long int row, long int col); +IGRAPH_EXPORT BASE* FUNCTION(igraph_matrix, e_ptr)(const TYPE(igraph_matrix) *m, + long int row, long int col); +IGRAPH_EXPORT void FUNCTION(igraph_matrix, set)(TYPE(igraph_matrix)* m, long int row, long int col, + BASE value); + +/*------------------------------*/ +/* Initializing matrix elements */ +/*------------------------------*/ + +IGRAPH_EXPORT void FUNCTION(igraph_matrix, null)(TYPE(igraph_matrix) *m); +IGRAPH_EXPORT void FUNCTION(igraph_matrix, fill)(TYPE(igraph_matrix) *m, BASE e); + +/*-----------------------*/ +/* Matrix views */ +/*-----------------------*/ + +IGRAPH_EXPORT const TYPE(igraph_matrix) *FUNCTION(igraph_matrix, view)(const TYPE(igraph_matrix) *m, + const BASE *data, + long int nrow, + long int ncol); + +/*------------------*/ +/* Copying matrices */ +/*------------------*/ + +IGRAPH_EXPORT void FUNCTION(igraph_matrix, copy_to)(const TYPE(igraph_matrix) *m, BASE *to); +IGRAPH_EXPORT int FUNCTION(igraph_matrix, update)(TYPE(igraph_matrix) *to, + const TYPE(igraph_matrix) *from); +IGRAPH_EXPORT int FUNCTION(igraph_matrix, rbind)(TYPE(igraph_matrix) *to, + const TYPE(igraph_matrix) *from); +IGRAPH_EXPORT int FUNCTION(igraph_matrix, cbind)(TYPE(igraph_matrix) *to, + const TYPE(igraph_matrix) *from); +IGRAPH_EXPORT int FUNCTION(igraph_matrix, swap)(TYPE(igraph_matrix) *m1, TYPE(igraph_matrix) *m2); + +/*--------------------------*/ +/* Copying rows and columns */ +/*--------------------------*/ + +IGRAPH_EXPORT int FUNCTION(igraph_matrix, get_row)(const TYPE(igraph_matrix) *m, + TYPE(igraph_vector) *res, long int index); +IGRAPH_EXPORT int FUNCTION(igraph_matrix, get_col)(const TYPE(igraph_matrix) *m, + TYPE(igraph_vector) *res, long int index); +IGRAPH_EXPORT int FUNCTION(igraph_matrix, set_row)(TYPE(igraph_matrix) *m, + const TYPE(igraph_vector) *v, long int index); +IGRAPH_EXPORT int FUNCTION(igraph_matrix, set_col)(TYPE(igraph_matrix) *m, + const TYPE(igraph_vector) *v, long int index); +IGRAPH_EXPORT int FUNCTION(igraph_matrix, select_rows)(const TYPE(igraph_matrix) *m, + TYPE(igraph_matrix) *res, + const igraph_vector_t *rows); +IGRAPH_EXPORT int FUNCTION(igraph_matrix, select_cols)(const TYPE(igraph_matrix) *m, + TYPE(igraph_matrix) *res, + const igraph_vector_t *cols); +IGRAPH_EXPORT 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 */ +/*-----------------------------*/ + +IGRAPH_EXPORT int FUNCTION(igraph_matrix, swap_rows)(TYPE(igraph_matrix) *m, + long int i, long int j); +IGRAPH_EXPORT int FUNCTION(igraph_matrix, swap_cols)(TYPE(igraph_matrix) *m, + long int i, long int j); +IGRAPH_EXPORT int FUNCTION(igraph_matrix, swap_rowcol)(TYPE(igraph_matrix) *m, + long int i, long int j); +IGRAPH_EXPORT int FUNCTION(igraph_matrix, transpose)(TYPE(igraph_matrix) *m); + +/*-----------------------------*/ +/* Matrix operations */ +/*-----------------------------*/ + +IGRAPH_EXPORT int FUNCTION(igraph_matrix, add)(TYPE(igraph_matrix) *m1, + const TYPE(igraph_matrix) *m2); +IGRAPH_EXPORT int FUNCTION(igraph_matrix, sub)(TYPE(igraph_matrix) *m1, + const TYPE(igraph_matrix) *m2); +IGRAPH_EXPORT int FUNCTION(igraph_matrix, mul_elements)(TYPE(igraph_matrix) *m1, + const TYPE(igraph_matrix) *m2); +IGRAPH_EXPORT int FUNCTION(igraph_matrix, div_elements)(TYPE(igraph_matrix) *m1, + const TYPE(igraph_matrix) *m2); +IGRAPH_EXPORT void FUNCTION(igraph_matrix, scale)(TYPE(igraph_matrix) *m, BASE by); +IGRAPH_EXPORT void FUNCTION(igraph_matrix, add_constant)(TYPE(igraph_matrix) *m, BASE plus); + +/*-----------------------------*/ +/* Finding minimum and maximum */ +/*-----------------------------*/ + +#ifndef NOTORDERED +IGRAPH_EXPORT igraph_real_t FUNCTION(igraph_matrix, min)(const TYPE(igraph_matrix) *m); +IGRAPH_EXPORT igraph_real_t FUNCTION(igraph_matrix, max)(const TYPE(igraph_matrix) *m); +IGRAPH_EXPORT int FUNCTION(igraph_matrix, which_min)(const TYPE(igraph_matrix) *m, + long int *i, long int *j); +IGRAPH_EXPORT int FUNCTION(igraph_matrix, which_max)(const TYPE(igraph_matrix) *m, + long int *i, long int *j); +IGRAPH_EXPORT int FUNCTION(igraph_matrix, minmax)(const TYPE(igraph_matrix) *m, + BASE *min, BASE *max); +IGRAPH_EXPORT int FUNCTION(igraph_matrix, which_minmax)(const TYPE(igraph_matrix) *m, + long int *imin, long int *jmin, + long int *imax, long int *jmax); +#endif + +/*------------------------------*/ +/* Comparison */ +/*------------------------------*/ + +IGRAPH_EXPORT igraph_bool_t FUNCTION(igraph_matrix, all_e)(const TYPE(igraph_matrix) *lhs, + const TYPE(igraph_matrix) *rhs); +#ifndef NOTORDERED +IGRAPH_EXPORT igraph_bool_t FUNCTION(igraph_matrix, all_l)(const TYPE(igraph_matrix) *lhs, + const TYPE(igraph_matrix) *rhs); +IGRAPH_EXPORT igraph_bool_t FUNCTION(igraph_matrix, all_g)(const TYPE(igraph_matrix) *lhs, + const TYPE(igraph_matrix) *rhs); +IGRAPH_EXPORT igraph_bool_t FUNCTION(igraph_matrix, all_le)(const TYPE(igraph_matrix) *lhs, + const TYPE(igraph_matrix) *rhs); +IGRAPH_EXPORT igraph_bool_t FUNCTION(igraph_matrix, all_ge)(const TYPE(igraph_matrix) *lhs, + const TYPE(igraph_matrix) *rhs); +#endif + +/*-------------------*/ +/* Matrix properties */ +/*-------------------*/ + +IGRAPH_EXPORT igraph_bool_t FUNCTION(igraph_matrix, isnull)(const TYPE(igraph_matrix) *m); +IGRAPH_EXPORT igraph_bool_t FUNCTION(igraph_matrix, empty)(const TYPE(igraph_matrix) *m); +IGRAPH_EXPORT long int FUNCTION(igraph_matrix, size)(const TYPE(igraph_matrix) *m); +IGRAPH_EXPORT long int FUNCTION(igraph_matrix, nrow)(const TYPE(igraph_matrix) *m); +IGRAPH_EXPORT long int FUNCTION(igraph_matrix, ncol)(const TYPE(igraph_matrix) *m); +IGRAPH_EXPORT igraph_bool_t FUNCTION(igraph_matrix, is_symmetric)(const TYPE(igraph_matrix) *m); +IGRAPH_EXPORT BASE FUNCTION(igraph_matrix, sum)(const TYPE(igraph_matrix) *m); +IGRAPH_EXPORT BASE FUNCTION(igraph_matrix, prod)(const TYPE(igraph_matrix) *m); +IGRAPH_EXPORT int FUNCTION(igraph_matrix, rowsum)(const TYPE(igraph_matrix) *m, + TYPE(igraph_vector) *res); +IGRAPH_EXPORT int FUNCTION(igraph_matrix, colsum)(const TYPE(igraph_matrix) *m, + TYPE(igraph_vector) *res); +IGRAPH_EXPORT igraph_bool_t FUNCTION(igraph_matrix, is_equal)(const TYPE(igraph_matrix) *m1, + const TYPE(igraph_matrix) *m2); +#ifndef NOTORDERED +IGRAPH_EXPORT igraph_real_t FUNCTION(igraph_matrix, maxdifference)(const TYPE(igraph_matrix) *m1, + const TYPE(igraph_matrix) *m2); +#endif + +/*------------------------*/ +/* Searching for elements */ +/*------------------------*/ + +IGRAPH_EXPORT igraph_bool_t FUNCTION(igraph_matrix, contains)(const TYPE(igraph_matrix) *m, + BASE e); +IGRAPH_EXPORT 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 */ +/*------------------------*/ + +IGRAPH_EXPORT int FUNCTION(igraph_matrix, resize)(TYPE(igraph_matrix) *m, + long int nrow, long int ncol); +IGRAPH_EXPORT int FUNCTION(igraph_matrix, resize_min)(TYPE(igraph_matrix) *m); +IGRAPH_EXPORT int FUNCTION(igraph_matrix, add_cols)(TYPE(igraph_matrix) *m, long int n); +IGRAPH_EXPORT int FUNCTION(igraph_matrix, add_rows)(TYPE(igraph_matrix) *m, long int n); +IGRAPH_EXPORT int FUNCTION(igraph_matrix, remove_col)(TYPE(igraph_matrix) *m, long int col); +IGRAPH_EXPORT int FUNCTION(igraph_matrix, remove_row)(TYPE(igraph_matrix) *m, long int row); + +/*------------------------*/ +/* Print as text */ +/*------------------------*/ + +IGRAPH_EXPORT int FUNCTION(igraph_matrix, print)(const TYPE(igraph_matrix) *m); +IGRAPH_EXPORT int FUNCTION(igraph_matrix, printf)(const TYPE(igraph_matrix) *m, + const char *format); +IGRAPH_EXPORT int FUNCTION(igraph_matrix, fprint)(const TYPE(igraph_matrix) *m, + FILE *file); + +#ifdef BASE_COMPLEX + +IGRAPH_EXPORT int igraph_matrix_complex_real(const igraph_matrix_complex_t *v, + igraph_matrix_t *real); +IGRAPH_EXPORT int igraph_matrix_complex_imag(const igraph_matrix_complex_t *v, + igraph_matrix_t *imag); +IGRAPH_EXPORT int igraph_matrix_complex_realimag(const igraph_matrix_complex_t *v, + igraph_matrix_t *real, + igraph_matrix_t *imag); +IGRAPH_EXPORT int igraph_matrix_complex_create(igraph_matrix_complex_t *v, + const igraph_matrix_t *real, + const igraph_matrix_t *imag); +IGRAPH_EXPORT int igraph_matrix_complex_create_polar(igraph_matrix_complex_t *v, + const igraph_matrix_t *r, + const igraph_matrix_t *theta); + +#endif + +IGRAPH_EXPORT int FUNCTION(igraph_matrix, permdelete_rows)(TYPE(igraph_matrix) *m, + long int *index, long int nremove); +IGRAPH_EXPORT int FUNCTION(igraph_matrix, delete_rows_neg)(TYPE(igraph_matrix) *m, + const igraph_vector_t *neg, + long int nremove); diff --git a/src/rigraph/include/igraph_memory.h b/src/rigraph/include/igraph_memory.h new file mode 100644 index 0000000..9830572 --- /dev/null +++ b/src/rigraph/include/igraph_memory.h @@ -0,0 +1,45 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2003-2012 Gabor Csardi + 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_MEMORY_H +#define IGRAPH_MEMORY_H + +#include +#include "igraph_decls.h" + +__BEGIN_DECLS + +#define IGRAPH_CALLOC(n,t) (t*) calloc( (n) > 0 ? (size_t)(n) : (size_t)1, sizeof(t) ) +#define IGRAPH_REALLOC(p,n,t) (t*) realloc((void*)(p), (n) > 0 ? (size_t)((n)*sizeof(t)) : (size_t)1) +#define IGRAPH_FREE(p) (free( (void *)(p) ), (p) = NULL) + +#define igraph_Calloc IGRAPH_CALLOC +#define igraph_Realloc IGRAPH_REALLOC +#define igraph_Free IGRAPH_FREE + +IGRAPH_EXPORT void igraph_free(void *p); +IGRAPH_EXPORT void *igraph_malloc(size_t n); + +__END_DECLS + +#endif diff --git a/src/rigraph/include/igraph_microscopic_update.h b/src/rigraph/include/igraph_microscopic_update.h new file mode 100644 index 0000000..7432b71 --- /dev/null +++ b/src/rigraph/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 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public 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 + +IGRAPH_EXPORT 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_EXPORT 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_EXPORT 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_EXPORT 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/src/rigraph/include/igraph_mixing.h b/src/rigraph/include/igraph_mixing.h new file mode 100644 index 0000000..5bc62ec --- /dev/null +++ b/src/rigraph/include/igraph_mixing.h @@ -0,0 +1,51 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2009-2012 Gabor Csardi + 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 + +IGRAPH_EXPORT int igraph_assortativity_nominal(const igraph_t *graph, + const igraph_vector_t *types, + igraph_real_t *res, + igraph_bool_t directed); + +IGRAPH_EXPORT 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); + +IGRAPH_EXPORT int igraph_assortativity_degree(const igraph_t *graph, + igraph_real_t *res, + igraph_bool_t directed); + +__END_DECLS + +#endif diff --git a/src/leidenalg/igraph-R/igraph_motifs.h b/src/rigraph/include/igraph_motifs.h similarity index 56% rename from src/leidenalg/igraph-R/igraph_motifs.h rename to src/rigraph/include/igraph_motifs.h index 9149c6d..7768ee1 100644 --- a/src/leidenalg/igraph-R/igraph_motifs.h +++ b/src/rigraph/include/igraph_motifs.h @@ -1,22 +1,22 @@ /* -*- mode: C -*- */ -/* +/* IGraph library. Copyright (C) 2009-2012 Gabor Csardi 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 + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -24,18 +24,10 @@ #ifndef IGRAPH_MOTIFS_H #define IGRAPH_MOTIFS_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_decls.h" #include "igraph_types.h" #include "igraph_datatype.h" +#include "igraph_iterators.h" __BEGIN_DECLS @@ -46,7 +38,7 @@ __BEGIN_DECLS /** * \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 @@ -63,35 +55,42 @@ __BEGIN_DECLS * 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); - -int igraph_motifs_randesu(const igraph_t *graph, igraph_vector_t *hist, - int size, const igraph_vector_t *cut_prob); - -int igraph_motifs_randesu_callback(const igraph_t *graph, int size, - const igraph_vector_t *cut_prob, - igraph_motifs_handler_t *callback, - void* extra); - -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); -int igraph_motifs_randesu_no(const igraph_t *graph, igraph_integer_t *no, - int size, const igraph_vector_t *cut_prob); - -int igraph_dyad_census(const igraph_t *graph, igraph_integer_t *mut, - igraph_integer_t *asym, igraph_integer_t *null); -int igraph_triad_census(const igraph_t *igraph, igraph_vector_t *res); -int igraph_triad_census_24(const igraph_t *graph, igraph_integer_t *res2, - igraph_integer_t *res4); + igraph_vector_t *vids, + int isoclass, + void* extra); + +IGRAPH_EXPORT int igraph_motifs_randesu(const igraph_t *graph, igraph_vector_t *hist, + int size, const igraph_vector_t *cut_prob); + +IGRAPH_EXPORT int igraph_motifs_randesu_callback(const igraph_t *graph, int size, + const igraph_vector_t *cut_prob, + igraph_motifs_handler_t *callback, + void* extra); + +IGRAPH_EXPORT 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); +IGRAPH_EXPORT int igraph_motifs_randesu_no(const igraph_t *graph, igraph_integer_t *no, + int size, const igraph_vector_t *cut_prob); + +IGRAPH_EXPORT int igraph_dyad_census(const igraph_t *graph, igraph_integer_t *mut, + igraph_integer_t *asym, igraph_integer_t *null); +IGRAPH_EXPORT int igraph_triad_census(const igraph_t *igraph, igraph_vector_t *res); +IGRAPH_EXPORT int igraph_triad_census_24(const igraph_t *graph, igraph_real_t *res2, + igraph_real_t *res4); + +IGRAPH_EXPORT int igraph_adjacent_triangles(const igraph_t *graph, + igraph_vector_t *res, + const igraph_vs_t vids); + +IGRAPH_EXPORT int igraph_list_triangles(const igraph_t *graph, + igraph_vector_int_t *res); __END_DECLS diff --git a/src/rigraph/include/igraph_neighborhood.h b/src/rigraph/include/igraph_neighborhood.h new file mode 100644 index 0000000..88a1c4f --- /dev/null +++ b/src/rigraph/include/igraph_neighborhood.h @@ -0,0 +1,47 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2009-2012 Gabor Csardi + 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 + +IGRAPH_EXPORT 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); +IGRAPH_EXPORT 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); +IGRAPH_EXPORT 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/src/rigraph/include/igraph_nongraph.h b/src/rigraph/include/igraph_nongraph.h new file mode 100644 index 0000000..47ebedb --- /dev/null +++ b/src/rigraph/include/igraph_nongraph.h @@ -0,0 +1,92 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2009-2012 Gabor Csardi + 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; + +IGRAPH_EXPORT int igraph_running_mean(const igraph_vector_t *data, igraph_vector_t *res, + igraph_integer_t binwidth); +IGRAPH_EXPORT int igraph_random_sample(igraph_vector_t *res, igraph_real_t l, igraph_real_t h, + igraph_integer_t length); +IGRAPH_EXPORT int igraph_convex_hull(const igraph_matrix_t *data, igraph_vector_t *resverts, + igraph_matrix_t *rescoords); +IGRAPH_EXPORT 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); +IGRAPH_EXPORT 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); +IGRAPH_EXPORT 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/src/rigraph/include/igraph_operators.h b/src/rigraph/include/igraph_operators.h new file mode 100644 index 0000000..98e0928 --- /dev/null +++ b/src/rigraph/include/igraph_operators.h @@ -0,0 +1,87 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2009-2012 Gabor Csardi + 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_attributes.h" +#include "igraph_constants.h" +#include "igraph_types.h" +#include "igraph_datatype.h" +#include "igraph_vector_ptr.h" + +__BEGIN_DECLS + +/* -------------------------------------------------- */ +/* Graph operators */ +/* -------------------------------------------------- */ + +IGRAPH_EXPORT int igraph_add_edge(igraph_t *graph, igraph_integer_t from, igraph_integer_t to); +IGRAPH_EXPORT int igraph_disjoint_union(igraph_t *res, + const igraph_t *left, const igraph_t *right); +IGRAPH_EXPORT int igraph_disjoint_union_many(igraph_t *res, + const igraph_vector_ptr_t *graphs); +IGRAPH_EXPORT int igraph_union(igraph_t *res, const igraph_t *left, const igraph_t *right, + igraph_vector_t *edge_map1, igraph_vector_t *edge_map2); +IGRAPH_EXPORT int igraph_union_many(igraph_t *res, const igraph_vector_ptr_t *graphs, + igraph_vector_ptr_t *edgemaps); +IGRAPH_EXPORT int igraph_intersection(igraph_t *res, + const igraph_t *left, const igraph_t *right, + igraph_vector_t *edge_map1, + igraph_vector_t *edge_map2); +IGRAPH_EXPORT int igraph_intersection_many(igraph_t *res, + const igraph_vector_ptr_t *graphs, + igraph_vector_ptr_t *edgemaps); +IGRAPH_EXPORT int igraph_difference(igraph_t *res, + const igraph_t *orig, const igraph_t *sub); +IGRAPH_EXPORT int igraph_complementer(igraph_t *res, const igraph_t *graph, + igraph_bool_t loops); +IGRAPH_EXPORT int igraph_compose(igraph_t *res, const igraph_t *g1, const igraph_t *g2, + igraph_vector_t *edge_map1, igraph_vector_t *edge_map2); +IGRAPH_EXPORT int igraph_contract_vertices(igraph_t *graph, + const igraph_vector_t *mapping, + const igraph_attribute_combination_t + *vertex_comb); +IGRAPH_EXPORT int igraph_permute_vertices(const igraph_t *graph, igraph_t *res, + const igraph_vector_t *permutation); +IGRAPH_EXPORT int igraph_connect_neighborhood(igraph_t *graph, igraph_integer_t order, + igraph_neimode_t mode); +IGRAPH_EXPORT int igraph_rewire(igraph_t *graph, + igraph_integer_t n, igraph_rewiring_t mode); +IGRAPH_EXPORT int igraph_simplify(igraph_t *graph, igraph_bool_t multiple, + igraph_bool_t loops, + const igraph_attribute_combination_t *edge_comb); +IGRAPH_EXPORT 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); +IGRAPH_EXPORT int igraph_induced_subgraph(const igraph_t *graph, igraph_t *res, + const igraph_vs_t vids, igraph_subgraph_implementation_t impl); +IGRAPH_EXPORT int igraph_subgraph_edges(const igraph_t *graph, igraph_t *res, + const igraph_es_t eids, igraph_bool_t delete_vertices); + +__END_DECLS + +#endif diff --git a/src/rigraph/include/igraph_paths.h b/src/rigraph/include/igraph_paths.h new file mode 100644 index 0000000..067449d --- /dev/null +++ b/src/rigraph/include/igraph_paths.h @@ -0,0 +1,176 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2009-2021 The igraph development 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., 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 + +IGRAPH_EXPORT int igraph_diameter(const igraph_t *graph, igraph_real_t *res, + igraph_integer_t *from, igraph_integer_t *to, + igraph_vector_t *path, + igraph_bool_t directed, igraph_bool_t unconn); +IGRAPH_EXPORT 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); + +IGRAPH_EXPORT 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); +IGRAPH_EXPORT 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); +IGRAPH_EXPORT 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_EXPORT 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); +IGRAPH_EXPORT 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); +IGRAPH_EXPORT 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); +IGRAPH_EXPORT int igraph_get_shortest_path_bellman_ford(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_EXPORT 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); +IGRAPH_EXPORT int igraph_get_shortest_paths_bellman_ford(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); +IGRAPH_EXPORT 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_EXPORT 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); +IGRAPH_EXPORT 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); + +IGRAPH_EXPORT int igraph_average_path_length(const igraph_t *graph, + igraph_real_t *res, igraph_real_t *unconn_pairs, + igraph_bool_t directed, igraph_bool_t unconn); +IGRAPH_EXPORT int igraph_average_path_length_dijkstra(const igraph_t *graph, + igraph_real_t *res, igraph_real_t *unconn_pairs, + const igraph_vector_t *weights, + igraph_bool_t directed, igraph_bool_t unconn); +IGRAPH_EXPORT int igraph_path_length_hist(const igraph_t *graph, igraph_vector_t *res, + igraph_real_t *unconnected, igraph_bool_t directed); + +IGRAPH_EXPORT int igraph_global_efficiency(const igraph_t *graph, igraph_real_t *res, + const igraph_vector_t *weights, + igraph_bool_t directed); +IGRAPH_EXPORT int igraph_local_efficiency(const igraph_t *graph, igraph_vector_t *res, + const igraph_vs_t vids, + const igraph_vector_t *weights, + igraph_bool_t directed, igraph_neimode_t mode); +IGRAPH_EXPORT int igraph_average_local_efficiency(const igraph_t *graph, igraph_real_t *res, + const igraph_vector_t *weights, + igraph_bool_t directed, igraph_neimode_t mode); + +IGRAPH_EXPORT int igraph_eccentricity(const igraph_t *graph, + igraph_vector_t *res, + igraph_vs_t vids, + igraph_neimode_t mode); + +IGRAPH_EXPORT int igraph_radius(const igraph_t *graph, igraph_real_t *radius, + igraph_neimode_t mode); + +IGRAPH_EXPORT 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_EXPORT 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); + +IGRAPH_EXPORT 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/src/rigraph/include/igraph_pmt.h b/src/rigraph/include/igraph_pmt.h new file mode 100644 index 0000000..ab810d6 --- /dev/null +++ b/src/rigraph/include/igraph_pmt.h @@ -0,0 +1,144 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2007-2012 Gabor Csardi + 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 + #define NOTORDERED 1 + #define NOABS 1 + #define EQ(a,b) ((a && b) || (!a && !b)) + +#elif defined(BASE_INT) + #define BASE igraph_integer_t + #define SHORT int + #define OUT_FORMAT "%" IGRAPH_PRId + #define ZERO 0 + #define ONE 1 + #define MULTIPLICITY 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/src/leidenalg/igraph-R/igraph_pmt_off.h b/src/rigraph/include/igraph_pmt_off.h similarity index 70% rename from src/leidenalg/igraph-R/igraph_pmt_off.h rename to src/rigraph/include/igraph_pmt_off.h index 99690e1..95a957e 100644 --- a/src/leidenalg/igraph-R/igraph_pmt_off.h +++ b/src/rigraph/include/igraph_pmt_off.h @@ -1,154 +1,158 @@ /* -*- mode: C -*- */ -/* +/* IGraph library. Copyright (C) 2007-2012 Gabor Csardi 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 + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifdef ATOMIC -#undef ATOMIC + #undef ATOMIC #endif #ifdef ATOMIC_IO -#undef ATOMIC_IO + #undef ATOMIC_IO #endif #ifdef BASE -#undef BASE + #undef BASE #endif #ifdef BASE_EPSILON -#undef BASE_EPSILON + #undef BASE_EPSILON #endif #ifdef CONCAT2 -#undef CONCAT2 + #undef CONCAT2 #endif #ifdef CONCAT2x -#undef CONCAT2x + #undef CONCAT2x #endif #ifdef CONCAT3 -#undef CONCAT3 + #undef CONCAT3 #endif #ifdef CONCAT3x -#undef CONCAT3x + #undef CONCAT3x #endif #ifdef CONCAT4 -#undef CONCAT4 + #undef CONCAT4 #endif #ifdef CONCAT4x -#undef CONCAT4x + #undef CONCAT4x #endif #ifdef FP -#undef FP + #undef FP #endif #ifdef FUNCTION -#undef FUNCTION + #undef FUNCTION #endif #ifdef IN_FORMAT -#undef IN_FORMAT + #undef IN_FORMAT #endif #ifdef MULTIPLICITY -#undef MULTIPLICITY + #undef MULTIPLICITY #endif #ifdef ONE -#undef ONE + #undef ONE #endif #ifdef OUT_FORMAT -#undef OUT_FORMAT + #undef OUT_FORMAT #endif #ifdef SHORT -#undef SHORT + #undef SHORT #endif #ifdef TYPE -#undef TYPE + #undef TYPE #endif #ifdef ZERO -#undef ZERO + #undef ZERO #endif #ifdef HEAPMORE -#undef HEAPMORE + #undef HEAPMORE #endif #ifdef HEAPLESS -#undef HEAPLESS + #undef HEAPLESS #endif #ifdef HEAPMOREEQ -#undef HEAPMOREEQ + #undef HEAPMOREEQ #endif #ifdef HEAPLESSEQ -#undef HEAPLESSEQ + #undef HEAPLESSEQ #endif #ifdef SUM -#undef SUM + #undef SUM #endif #ifdef SQ -#undef SQ + #undef SQ #endif #ifdef PROD -#undef PROD + #undef PROD #endif #ifdef NOTORDERED -#undef NOTORDERED + #undef NOTORDERED #endif #ifdef EQ -#undef EQ + #undef EQ #endif #ifdef DIFF -#undef DIFF + #undef DIFF #endif #ifdef DIV -#undef DIV + #undef DIV #endif #ifdef NOABS -#undef NOABS + #undef NOABS #endif #ifdef PRINTFUNC -#undef PRINTFUNC + #undef PRINTFUNC #endif #ifdef FPRINTFUNC -#undef PRINTFUNC + #undef PRINTFUNC +#endif + +#ifdef UNSIGNED + #undef UNSIGNED #endif diff --git a/src/leidenalg/igraph-R/igraph_progress.h b/src/rigraph/include/igraph_progress.h similarity index 73% rename from src/leidenalg/igraph-R/igraph_progress.h rename to src/rigraph/include/igraph_progress.h index be1c424..f7a0f25 100644 --- a/src/leidenalg/igraph-R/igraph_progress.h +++ b/src/rigraph/include/igraph_progress.h @@ -1,22 +1,22 @@ /* -*- mode: C -*- */ -/* +/* IGraph library. Copyright (C) 2009-2012 Gabor Csardi 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 + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -24,16 +24,7 @@ #ifndef IGRAPH_PROGRESS_H #define IGRAPH_PROGRESS_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_decls.h" #include "igraph_types.h" __BEGIN_DECLS @@ -42,19 +33,19 @@ __BEGIN_DECLS * \section about_progress_handlers About progress handlers * * 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. + * 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. * - * + * * - * 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 + * 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 + * 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(). @@ -63,11 +54,11 @@ __BEGIN_DECLS /** * \section writing_progress_handlers Writing progress handlers - * + * * - * 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() + * 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. * * @@ -75,7 +66,7 @@ __BEGIN_DECLS * 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 + * percentage argument. Note, however, that if an error happens in the * middle of a computation, then the 100 percent call might be * omitted. * @@ -83,41 +74,41 @@ __BEGIN_DECLS /** * \section igraph_functions_with_progress Writing igraph functions with progress reporting - * - * + * + * * 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. * - * + * * * 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 + * igraph_progress() with a zero \p percentage argument, before the + * computation; and another call with 100 \p percentage value * after the computation is completed. * - * + * * * 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 + * 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. + * having at most O(n) progress checks and at most 100 + * \ref igraph_progress() calls. * */ /** * \section progress_and_threads Multi-threaded programs - * + * * - * In multi-threaded programs, each thread has its own progress + * 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 + * thread-safe. See the \ref IGRAPH_THREAD_SAFE macro for checking * whether an igraph build is thread-safe. * */ @@ -126,67 +117,66 @@ __BEGIN_DECLS /* 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, + * 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 + * \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 + * always use the name \p message argument if reporting from the * same function. - * \param percent Numeric, the percentage that was completed by the + * \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 + * \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 + * \return If the return value of the progress handler is not + * \c IGRAPH_SUCCESS, 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); + void *data); -extern igraph_progress_handler_t igraph_progress_handler_stderr; +IGRAPH_EXPORT extern igraph_progress_handler_t igraph_progress_handler_stderr; -igraph_progress_handler_t * -igraph_set_progress_handler(igraph_progress_handler_t new_handler); +IGRAPH_EXPORT igraph_progress_handler_t * igraph_set_progress_handler(igraph_progress_handler_t new_handler); -int igraph_progress(const char *message, igraph_real_t percent, void *data); +IGRAPH_EXPORT int igraph_progress(const char *message, igraph_real_t percent, void *data); -int igraph_progressf(const char *message, igraph_real_t percent, void *data, - ...); +IGRAPH_EXPORT 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, + * \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) + do { \ + if (igraph_progress((message), (percent), (data)) != IGRAPH_SUCCESS) { \ + IGRAPH_FINALLY_FREE(); \ + return IGRAPH_INTERRUPTED; \ + } \ + } while (0) __END_DECLS diff --git a/src/leidenalg/igraph-R/igraph_psumtree.h b/src/rigraph/include/igraph_psumtree.h similarity index 55% rename from src/leidenalg/igraph-R/igraph_psumtree.h rename to src/rigraph/include/igraph_psumtree.h index 9dd8554..c27d576 100644 --- a/src/leidenalg/igraph-R/igraph_psumtree.h +++ b/src/rigraph/include/igraph_psumtree.h @@ -1,22 +1,22 @@ /* -*- mode: C -*- */ -/* +/* IGraph library. Copyright (C) 2009-2012 Gabor Csardi 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 + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -24,34 +24,27 @@ #ifndef IGRAPH_PSUMTREE_H #define IGRAPH_PSUMTREE_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_decls.h" #include "igraph_vector.h" __BEGIN_DECLS typedef struct { - igraph_vector_t v; - long int size; - long int offset; + igraph_vector_t v; + long int size; + long int offset; } igraph_psumtree_t; -int igraph_psumtree_init(igraph_psumtree_t *t, long int size); -void igraph_psumtree_destroy(igraph_psumtree_t *t); -igraph_real_t igraph_psumtree_get(const igraph_psumtree_t *t, long int idx); -long int igraph_psumtree_size(const igraph_psumtree_t *t); -int igraph_psumtree_search(const igraph_psumtree_t *t, long int *idx, - igraph_real_t elem); -int igraph_psumtree_update(igraph_psumtree_t *t, long int idx, - igraph_real_t new_value); -igraph_real_t igraph_psumtree_sum(const igraph_psumtree_t *t); + +IGRAPH_EXPORT int igraph_psumtree_init(igraph_psumtree_t *t, long int size); +IGRAPH_EXPORT void igraph_psumtree_reset(igraph_psumtree_t *t); +IGRAPH_EXPORT void igraph_psumtree_destroy(igraph_psumtree_t *t); +IGRAPH_EXPORT igraph_real_t igraph_psumtree_get(const igraph_psumtree_t *t, long int idx); +IGRAPH_EXPORT long int igraph_psumtree_size(const igraph_psumtree_t *t); +IGRAPH_EXPORT int igraph_psumtree_search(const igraph_psumtree_t *t, long int *idx, + igraph_real_t elem); +IGRAPH_EXPORT int igraph_psumtree_update(igraph_psumtree_t *t, long int idx, + igraph_real_t new_value); +IGRAPH_EXPORT igraph_real_t igraph_psumtree_sum(const igraph_psumtree_t *t); __END_DECLS diff --git a/src/leidenalg/igraph-R/igraph_qsort.h b/src/rigraph/include/igraph_qsort.h similarity index 67% rename from src/leidenalg/igraph-R/igraph_qsort.h rename to src/rigraph/include/igraph_qsort.h index bb347da..94e5e7f 100644 --- a/src/leidenalg/igraph-R/igraph_qsort.h +++ b/src/rigraph/include/igraph_qsort.h @@ -1,22 +1,22 @@ /* -*- mode: C -*- */ -/* +/* IGraph library. Copyright (C) 2011-2012 Gabor Csardi 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 + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -24,22 +24,16 @@ #ifndef IGRAPH_QSORT_H #define IGRAPH_QSORT_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_decls.h" + +#include __BEGIN_DECLS -void igraph_qsort(void *base, size_t nel, size_t width, - int (*compar)(const void *, const void *)); -void igraph_qsort_r(void *base, size_t nel, size_t width, void *thunk, - int (*compar)(void *, const void *, const void *)); +IGRAPH_EXPORT void igraph_qsort(void *base, size_t nel, size_t width, + int (*compar)(const void *, const void *)); +IGRAPH_EXPORT void igraph_qsort_r(void *base, size_t nel, size_t width, void *thunk, + int (*compar)(void *, const void *, const void *)); __END_DECLS diff --git a/src/rigraph/include/igraph_random.h b/src/rigraph/include/igraph_random.h new file mode 100644 index 0000000..cbdb5c3 --- /dev/null +++ b/src/rigraph/include/igraph_random.h @@ -0,0 +1,134 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2003-2012 Gabor Csardi + 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_RANDOM_H +#define IGRAPH_RANDOM_H + +#include "igraph_decls.h" + +__BEGIN_DECLS + +#include +#include + +#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; /* 'min' must always be set to 0 */ + 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; + +/* --------------------------------- */ + +IGRAPH_EXPORT int igraph_rng_init(igraph_rng_t *rng, const igraph_rng_type_t *type); +IGRAPH_EXPORT void igraph_rng_destroy(igraph_rng_t *rng); + +IGRAPH_EXPORT int igraph_rng_seed(igraph_rng_t *rng, unsigned long int seed); +IGRAPH_EXPORT unsigned long int igraph_rng_max(igraph_rng_t *rng); +IGRAPH_EXPORT IGRAPH_DEPRECATED unsigned long int igraph_rng_min(igraph_rng_t *rng); +IGRAPH_EXPORT const char *igraph_rng_name(igraph_rng_t *rng); + +IGRAPH_EXPORT long int igraph_rng_get_integer(igraph_rng_t *rng, + long int l, long int h); +IGRAPH_EXPORT igraph_real_t igraph_rng_get_normal(igraph_rng_t *rng, + igraph_real_t m, igraph_real_t s); +IGRAPH_EXPORT igraph_real_t igraph_rng_get_unif(igraph_rng_t *rng, + igraph_real_t l, igraph_real_t h); +IGRAPH_EXPORT igraph_real_t igraph_rng_get_unif01(igraph_rng_t *rng); +IGRAPH_EXPORT igraph_real_t igraph_rng_get_geom(igraph_rng_t *rng, igraph_real_t p); +IGRAPH_EXPORT igraph_real_t igraph_rng_get_binom(igraph_rng_t *rng, long int n, + igraph_real_t p); +IGRAPH_EXPORT igraph_real_t igraph_rng_get_exp(igraph_rng_t *rng, igraph_real_t rate); +IGRAPH_EXPORT unsigned long int igraph_rng_get_int31(igraph_rng_t *rng); +IGRAPH_EXPORT igraph_real_t igraph_rng_get_gamma(igraph_rng_t *rng, igraph_real_t shape, + igraph_real_t scale); +IGRAPH_EXPORT int igraph_rng_get_dirichlet(igraph_rng_t *rng, + const igraph_vector_t *alpha, + igraph_vector_t *result); + +/* --------------------------------- */ + +IGRAPH_EXPORT extern const igraph_rng_type_t igraph_rngtype_glibc2; +IGRAPH_EXPORT extern const igraph_rng_type_t igraph_rngtype_rand; +IGRAPH_EXPORT extern const igraph_rng_type_t igraph_rngtype_mt19937; + +IGRAPH_EXPORT igraph_rng_t *igraph_rng_default(void); +IGRAPH_EXPORT 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 */ + +IGRAPH_EXPORT 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/src/rigraph/include/igraph_scan.h b/src/rigraph/include/igraph_scan.h new file mode 100644 index 0000000..0b1480f --- /dev/null +++ b/src/rigraph/include/igraph_scan.h @@ -0,0 +1,69 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2013 Gabor Csardi + 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 + +IGRAPH_EXPORT int igraph_local_scan_0(const igraph_t *graph, igraph_vector_t *res, + const igraph_vector_t *weights, igraph_neimode_t mode); + +IGRAPH_EXPORT 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); + +IGRAPH_EXPORT int igraph_local_scan_1_ecount(const igraph_t *graph, igraph_vector_t *res, + const igraph_vector_t *weights, + igraph_neimode_t mode); + +IGRAPH_EXPORT 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); + +IGRAPH_EXPORT 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); + +IGRAPH_EXPORT 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); + +IGRAPH_EXPORT 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/src/rigraph/include/igraph_scg.h b/src/rigraph/include/igraph_scg.h new file mode 100644 index 0000000..7b466a4 --- /dev/null +++ b/src/rigraph/include/igraph_scg.h @@ -0,0 +1,143 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2011-2012 Gabor Csardi + 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_decls.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; + +IGRAPH_EXPORT 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); + +IGRAPH_EXPORT 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); + +IGRAPH_EXPORT 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); + +IGRAPH_EXPORT 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_EXPORT 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_EXPORT 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/src/leidenalg/igraph-R/igraph_separators.h b/src/rigraph/include/igraph_separators.h similarity index 60% rename from src/leidenalg/igraph-R/igraph_separators.h rename to src/rigraph/include/igraph_separators.h index 19ad531..aed1d5a 100644 --- a/src/leidenalg/igraph-R/igraph_separators.h +++ b/src/rigraph/include/igraph_separators.h @@ -1,22 +1,22 @@ /* -*- mode: C -*- */ -/* +/* IGraph library. Copyright (C) 2010-2012 Gabor Csardi 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 + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -24,16 +24,7 @@ #ifndef IGRAPH_SEPARATORS_H #define IGRAPH_SEPARATORS_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_decls.h" #include "igraph_constants.h" #include "igraph_types.h" #include "igraph_vector.h" @@ -43,19 +34,19 @@ __BEGIN_DECLS -int igraph_is_separator(const igraph_t *graph, - const igraph_vs_t candidate, - igraph_bool_t *res); +IGRAPH_EXPORT int igraph_is_separator(const igraph_t *graph, + const igraph_vs_t candidate, + igraph_bool_t *res); -int igraph_all_minimal_st_separators(const igraph_t *graph, - igraph_vector_ptr_t *separators); +IGRAPH_EXPORT int igraph_all_minimal_st_separators(const igraph_t *graph, + igraph_vector_ptr_t *separators); -int igraph_is_minimal_separator(const igraph_t *graph, - const igraph_vs_t candidate, - igraph_bool_t *res); +IGRAPH_EXPORT int igraph_is_minimal_separator(const igraph_t *graph, + const igraph_vs_t candidate, + igraph_bool_t *res); -int igraph_minimum_size_separators(const igraph_t *graph, - igraph_vector_ptr_t *separators); +IGRAPH_EXPORT int igraph_minimum_size_separators(const igraph_t *graph, + igraph_vector_ptr_t *separators); __END_DECLS diff --git a/src/rigraph/include/igraph_sparsemat.h b/src/rigraph/include/igraph_sparsemat.h new file mode 100644 index 0000000..6070ca8 --- /dev/null +++ b/src/rigraph/include/igraph_sparsemat.h @@ -0,0 +1,288 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2009-2012 Gabor Csardi + 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_decls.h" +#include "igraph_types.h" +#include "igraph_vector.h" +#include "igraph_datatype.h" +#include "igraph_arpack.h" + +#include + +__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; + +IGRAPH_EXPORT int igraph_sparsemat_init(igraph_sparsemat_t *A, int rows, int cols, int nzmax); +IGRAPH_EXPORT int igraph_sparsemat_copy(igraph_sparsemat_t *to, + const igraph_sparsemat_t *from); +IGRAPH_EXPORT void igraph_sparsemat_destroy(igraph_sparsemat_t *A); +IGRAPH_EXPORT int igraph_sparsemat_realloc(igraph_sparsemat_t *A, int nzmax); + +IGRAPH_EXPORT long int igraph_sparsemat_nrow(const igraph_sparsemat_t *A); +IGRAPH_EXPORT long int igraph_sparsemat_ncol(const igraph_sparsemat_t *B); +IGRAPH_EXPORT igraph_sparsemat_type_t igraph_sparsemat_type(const igraph_sparsemat_t *A); +IGRAPH_EXPORT igraph_bool_t igraph_sparsemat_is_triplet(const igraph_sparsemat_t *A); +IGRAPH_EXPORT igraph_bool_t igraph_sparsemat_is_cc(const igraph_sparsemat_t *A); + +IGRAPH_EXPORT 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); + +IGRAPH_EXPORT 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_EXPORT int igraph_sparsemat_entry(igraph_sparsemat_t *A, int row, int col, + igraph_real_t elem); +IGRAPH_EXPORT int igraph_sparsemat_compress(const igraph_sparsemat_t *A, + igraph_sparsemat_t *res); +IGRAPH_EXPORT int igraph_sparsemat_transpose(const igraph_sparsemat_t *A, + igraph_sparsemat_t *res, int values); +IGRAPH_EXPORT igraph_bool_t igraph_sparsemat_is_symmetric(const igraph_sparsemat_t *A); +IGRAPH_EXPORT int igraph_sparsemat_dupl(igraph_sparsemat_t *A); +IGRAPH_EXPORT int igraph_sparsemat_fkeep(igraph_sparsemat_t *A, + igraph_integer_t (*fkeep)(igraph_integer_t, igraph_integer_t, igraph_real_t, void*), + void *other); +IGRAPH_EXPORT int igraph_sparsemat_dropzeros(igraph_sparsemat_t *A); +IGRAPH_EXPORT int igraph_sparsemat_droptol(igraph_sparsemat_t *A, igraph_real_t tol); +IGRAPH_EXPORT int igraph_sparsemat_multiply(const igraph_sparsemat_t *A, + const igraph_sparsemat_t *B, + igraph_sparsemat_t *res); +IGRAPH_EXPORT 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); +IGRAPH_EXPORT int igraph_sparsemat_gaxpy(const igraph_sparsemat_t *A, + const igraph_vector_t *x, + igraph_vector_t *res); + +IGRAPH_EXPORT int igraph_sparsemat_lsolve(const igraph_sparsemat_t *A, + const igraph_vector_t *b, + igraph_vector_t *res); +IGRAPH_EXPORT int igraph_sparsemat_ltsolve(const igraph_sparsemat_t *A, + const igraph_vector_t *b, + igraph_vector_t *res); +IGRAPH_EXPORT int igraph_sparsemat_usolve(const igraph_sparsemat_t *A, + const igraph_vector_t *b, + igraph_vector_t *res); +IGRAPH_EXPORT int igraph_sparsemat_utsolve(const igraph_sparsemat_t *A, + const igraph_vector_t *b, + igraph_vector_t *res); + +IGRAPH_EXPORT int igraph_sparsemat_cholsol(const igraph_sparsemat_t *A, + const igraph_vector_t *b, + igraph_vector_t *res, + int order); + +IGRAPH_EXPORT int igraph_sparsemat_lusol(const igraph_sparsemat_t *A, + const igraph_vector_t *b, + igraph_vector_t *res, + int order, + igraph_real_t tol); + +IGRAPH_EXPORT int igraph_sparsemat_print(const igraph_sparsemat_t *A, + FILE *outstream); + +IGRAPH_EXPORT int igraph_sparsemat_eye(igraph_sparsemat_t *A, int n, int nzmax, + igraph_real_t value, + igraph_bool_t compress); + +IGRAPH_EXPORT int igraph_sparsemat_diag(igraph_sparsemat_t *A, int nzmax, + const igraph_vector_t *values, + igraph_bool_t compress); + +IGRAPH_EXPORT int igraph_sparsemat(igraph_t *graph, const igraph_sparsemat_t *A, + igraph_bool_t directed); + +IGRAPH_EXPORT int igraph_weighted_sparsemat(igraph_t *graph, const igraph_sparsemat_t *A, + igraph_bool_t directed, const char *attr, + igraph_bool_t loops); + +IGRAPH_EXPORT int igraph_get_sparsemat(const igraph_t *graph, igraph_sparsemat_t *res); + +IGRAPH_EXPORT int igraph_matrix_as_sparsemat(igraph_sparsemat_t *res, + const igraph_matrix_t *mat, + igraph_real_t tol); + +IGRAPH_EXPORT 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; + +IGRAPH_EXPORT 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); + +IGRAPH_EXPORT 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); + +IGRAPH_EXPORT int igraph_sparsemat_lu(const igraph_sparsemat_t *A, + const igraph_sparsemat_symbolic_t *dis, + igraph_sparsemat_numeric_t *din, double tol); + +IGRAPH_EXPORT int igraph_sparsemat_qr(const igraph_sparsemat_t *A, + const igraph_sparsemat_symbolic_t *dis, + igraph_sparsemat_numeric_t *din); + +IGRAPH_EXPORT 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); + +IGRAPH_EXPORT 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); + +IGRAPH_EXPORT int igraph_sparsemat_symbqr(long int order, const igraph_sparsemat_t *A, + igraph_sparsemat_symbolic_t *dis); + +IGRAPH_EXPORT int igraph_sparsemat_symblu(long int order, const igraph_sparsemat_t *A, + igraph_sparsemat_symbolic_t *dis); + + +IGRAPH_EXPORT void igraph_sparsemat_symbolic_destroy(igraph_sparsemat_symbolic_t *dis); +IGRAPH_EXPORT void igraph_sparsemat_numeric_destroy(igraph_sparsemat_numeric_t *din); + +IGRAPH_EXPORT igraph_real_t igraph_sparsemat_max(igraph_sparsemat_t *A); +IGRAPH_EXPORT igraph_real_t igraph_sparsemat_min(igraph_sparsemat_t *A); +IGRAPH_EXPORT int igraph_sparsemat_minmax(igraph_sparsemat_t *A, + igraph_real_t *min, igraph_real_t *max); + +IGRAPH_EXPORT long int igraph_sparsemat_count_nonzero(igraph_sparsemat_t *A); +IGRAPH_EXPORT long int igraph_sparsemat_count_nonzerotol(igraph_sparsemat_t *A, + igraph_real_t tol); +IGRAPH_EXPORT int igraph_sparsemat_rowsums(const igraph_sparsemat_t *A, + igraph_vector_t *res); +IGRAPH_EXPORT int igraph_sparsemat_colsums(const igraph_sparsemat_t *A, + igraph_vector_t *res); + +IGRAPH_EXPORT int igraph_sparsemat_rowmins(igraph_sparsemat_t *A, + igraph_vector_t *res); +IGRAPH_EXPORT int igraph_sparsemat_colmins(igraph_sparsemat_t *A, + igraph_vector_t *res); + +IGRAPH_EXPORT int igraph_sparsemat_rowmaxs(igraph_sparsemat_t *A, + igraph_vector_t *res); +IGRAPH_EXPORT int igraph_sparsemat_colmaxs(igraph_sparsemat_t *A, + igraph_vector_t *res); + +IGRAPH_EXPORT int igraph_sparsemat_which_min_rows(igraph_sparsemat_t *A, + igraph_vector_t *res, + igraph_vector_int_t *pos); +IGRAPH_EXPORT int igraph_sparsemat_which_min_cols(igraph_sparsemat_t *A, + igraph_vector_t *res, + igraph_vector_int_t *pos); + +IGRAPH_EXPORT int igraph_sparsemat_scale(igraph_sparsemat_t *A, igraph_real_t by); + + +IGRAPH_EXPORT int igraph_sparsemat_add_rows(igraph_sparsemat_t *A, long int n); +IGRAPH_EXPORT int igraph_sparsemat_add_cols(igraph_sparsemat_t *A, long int n); +IGRAPH_EXPORT int igraph_sparsemat_resize(igraph_sparsemat_t *A, long int nrow, + long int ncol, int nzmax); +IGRAPH_EXPORT int igraph_sparsemat_nonzero_storage(const igraph_sparsemat_t *A); +IGRAPH_EXPORT int igraph_sparsemat_getelements(const igraph_sparsemat_t *A, + igraph_vector_int_t *i, + igraph_vector_int_t *j, + igraph_vector_t *x); +IGRAPH_EXPORT int igraph_sparsemat_getelements_sorted(const igraph_sparsemat_t *A, + igraph_vector_int_t *i, + igraph_vector_int_t *j, + igraph_vector_t *x); +IGRAPH_EXPORT int igraph_sparsemat_scale_rows(igraph_sparsemat_t *A, + const igraph_vector_t *fact); +IGRAPH_EXPORT int igraph_sparsemat_scale_cols(igraph_sparsemat_t *A, + const igraph_vector_t *fact); +IGRAPH_EXPORT int igraph_sparsemat_multiply_by_dense(const igraph_sparsemat_t *A, + const igraph_matrix_t *B, + igraph_matrix_t *res); +IGRAPH_EXPORT int igraph_sparsemat_dense_multiply(const igraph_matrix_t *A, + const igraph_sparsemat_t *B, + igraph_matrix_t *res); + +IGRAPH_EXPORT int igraph_sparsemat_view(igraph_sparsemat_t *A, int nzmax, int m, int n, + int *p, int *i, double *x, int nz); +IGRAPH_EXPORT IGRAPH_DEPRECATED int igraph_i_sparsemat_view(igraph_sparsemat_t *A, int nzmax, int m, int n, + int *p, int *i, double *x, int nz); + +IGRAPH_EXPORT int igraph_sparsemat_sort(const igraph_sparsemat_t *A, + igraph_sparsemat_t *sorted); + +IGRAPH_EXPORT int igraph_sparsemat_nzmax(const igraph_sparsemat_t *A); + +IGRAPH_EXPORT int igraph_sparsemat_neg(igraph_sparsemat_t *A); + +IGRAPH_EXPORT int igraph_sparsemat_iterator_init(igraph_sparsemat_iterator_t *it, + igraph_sparsemat_t *sparsemat); +IGRAPH_EXPORT int igraph_sparsemat_iterator_reset(igraph_sparsemat_iterator_t *it); +IGRAPH_EXPORT igraph_bool_t igraph_sparsemat_iterator_end(const igraph_sparsemat_iterator_t *it); +IGRAPH_EXPORT int igraph_sparsemat_iterator_row(const igraph_sparsemat_iterator_t *it); +IGRAPH_EXPORT int igraph_sparsemat_iterator_col(const igraph_sparsemat_iterator_t *it); +IGRAPH_EXPORT int igraph_sparsemat_iterator_idx(const igraph_sparsemat_iterator_t *it); +IGRAPH_EXPORT igraph_real_t igraph_sparsemat_iterator_get(const igraph_sparsemat_iterator_t *it); +IGRAPH_EXPORT int igraph_sparsemat_iterator_next(igraph_sparsemat_iterator_t *it); + +__END_DECLS + +#endif diff --git a/src/rigraph/include/igraph_spmatrix.h b/src/rigraph/include/igraph_spmatrix.h new file mode 100644 index 0000000..141c39a --- /dev/null +++ b/src/rigraph/include/igraph_spmatrix.h @@ -0,0 +1,109 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2009-2012 Gabor Csardi + 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 + * + * 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. + * + * 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. + */ +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) + +IGRAPH_EXPORT int igraph_spmatrix_init(igraph_spmatrix_t *m, long int nrow, long int ncol); +IGRAPH_EXPORT void igraph_spmatrix_destroy(igraph_spmatrix_t *m); +IGRAPH_EXPORT int igraph_spmatrix_resize(igraph_spmatrix_t *m, long int nrow, long int ncol); +IGRAPH_EXPORT igraph_real_t igraph_spmatrix_e(const igraph_spmatrix_t *m, long int row, long int col); +IGRAPH_EXPORT int igraph_spmatrix_set(igraph_spmatrix_t *m, long int row, long int col, + igraph_real_t value); +IGRAPH_EXPORT int igraph_spmatrix_add_e(igraph_spmatrix_t *m, long int row, long int col, + igraph_real_t value); +IGRAPH_EXPORT int igraph_spmatrix_add_col_values(igraph_spmatrix_t *m, long int to, long int from); +IGRAPH_EXPORT long int igraph_spmatrix_count_nonzero(const igraph_spmatrix_t *m); +IGRAPH_EXPORT long int igraph_spmatrix_size(const igraph_spmatrix_t *m); +IGRAPH_EXPORT long int igraph_spmatrix_nrow(const igraph_spmatrix_t *m); +IGRAPH_EXPORT long int igraph_spmatrix_ncol(const igraph_spmatrix_t *m); +IGRAPH_EXPORT int igraph_spmatrix_copy_to(const igraph_spmatrix_t *m, igraph_real_t *to); +IGRAPH_EXPORT int igraph_spmatrix_null(igraph_spmatrix_t *m); +IGRAPH_EXPORT int igraph_spmatrix_add_cols(igraph_spmatrix_t *m, long int n); +IGRAPH_EXPORT int igraph_spmatrix_add_rows(igraph_spmatrix_t *m, long int n); +IGRAPH_EXPORT int igraph_spmatrix_clear_col(igraph_spmatrix_t *m, long int col); +IGRAPH_EXPORT int igraph_spmatrix_clear_row(igraph_spmatrix_t *m, long int row); +IGRAPH_EXPORT int igraph_spmatrix_copy(igraph_spmatrix_t *to, const igraph_spmatrix_t *from); +IGRAPH_EXPORT igraph_real_t igraph_spmatrix_max_nonzero(const igraph_spmatrix_t *m, + igraph_real_t *ridx, igraph_real_t *cidx); +IGRAPH_EXPORT igraph_real_t igraph_spmatrix_max(const igraph_spmatrix_t *m, + igraph_real_t *ridx, igraph_real_t *cidx); +IGRAPH_EXPORT void igraph_spmatrix_scale(igraph_spmatrix_t *m, igraph_real_t by); +IGRAPH_EXPORT int igraph_spmatrix_colsums(const igraph_spmatrix_t *m, igraph_vector_t *res); +IGRAPH_EXPORT int igraph_spmatrix_rowsums(const igraph_spmatrix_t *m, igraph_vector_t *res); + +IGRAPH_EXPORT int igraph_spmatrix_print(const igraph_spmatrix_t *matrix); +IGRAPH_EXPORT int igraph_spmatrix_fprint(const igraph_spmatrix_t *matrix, FILE* file); + + +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; + +IGRAPH_EXPORT int igraph_spmatrix_iter_create(igraph_spmatrix_iter_t *mit, const igraph_spmatrix_t *m); +IGRAPH_EXPORT int igraph_spmatrix_iter_reset(igraph_spmatrix_iter_t *mit); +IGRAPH_EXPORT int igraph_spmatrix_iter_next(igraph_spmatrix_iter_t *mit); +IGRAPH_EXPORT igraph_bool_t igraph_spmatrix_iter_end(igraph_spmatrix_iter_t *mit); +IGRAPH_EXPORT void igraph_spmatrix_iter_destroy(igraph_spmatrix_iter_t *mit); + +__END_DECLS + +#endif diff --git a/src/leidenalg/igraph-R/igraph_stack.h b/src/rigraph/include/igraph_stack.h similarity index 84% rename from src/leidenalg/igraph-R/igraph_stack.h rename to src/rigraph/include/igraph_stack.h index c6a821c..a186296 100644 --- a/src/leidenalg/igraph-R/igraph_stack.h +++ b/src/rigraph/include/igraph_stack.h @@ -1,22 +1,22 @@ /* -*- mode: C -*- */ -/* +/* IGraph library. Copyright (C) 2009-2012 Gabor Csardi 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 + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -24,16 +24,7 @@ #ifndef IGRAPH_STACK_H #define IGRAPH_STACK_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_decls.h" #include "igraph_types.h" __BEGIN_DECLS @@ -54,6 +45,12 @@ __BEGIN_DECLS #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" @@ -74,8 +71,8 @@ __BEGIN_DECLS #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); +IGRAPH_EXPORT void igraph_stack_ptr_free_all(igraph_stack_ptr_t* s); +IGRAPH_EXPORT void igraph_stack_ptr_destroy_all(igraph_stack_ptr_t* s); __END_DECLS diff --git a/src/leidenalg/igraph-R/igraph_stack_pmt.h b/src/rigraph/include/igraph_stack_pmt.h similarity index 51% rename from src/leidenalg/igraph-R/igraph_stack_pmt.h rename to src/rigraph/include/igraph_stack_pmt.h index 2db6aa0..1a9459b 100644 --- a/src/leidenalg/igraph-R/igraph_stack_pmt.h +++ b/src/rigraph/include/igraph_stack_pmt.h @@ -1,22 +1,22 @@ /* -*- mode: C -*- */ -/* +/* IGraph library. Copyright (C) 2007-2012 Gabor Csardi 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 + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -29,19 +29,19 @@ */ typedef struct TYPE(igraph_stack) { - BASE* stor_begin; - BASE* stor_end; - BASE* end; + BASE* stor_begin; + BASE* stor_end; + BASE* end; } TYPE(igraph_stack); -int FUNCTION(igraph_stack,init)(TYPE(igraph_stack)* s, long int size); -void FUNCTION(igraph_stack,destroy)(TYPE(igraph_stack)* s); -int FUNCTION(igraph_stack,reserve)(TYPE(igraph_stack)* s, long int size); -igraph_bool_t FUNCTION(igraph_stack,empty)(TYPE(igraph_stack)* s); -long int FUNCTION(igraph_stack,size)(const TYPE(igraph_stack)* s); -void FUNCTION(igraph_stack,clear)(TYPE(igraph_stack)* s); -int FUNCTION(igraph_stack,push)(TYPE(igraph_stack)* s, BASE elem); -BASE FUNCTION(igraph_stack,pop)(TYPE(igraph_stack)* s); -BASE FUNCTION(igraph_stack,top)(const TYPE(igraph_stack)* s); -int FUNCTION(igraph_stack,print)(const TYPE(igraph_stack)* s); -int FUNCTION(igraph_stack,fprint)(const TYPE(igraph_stack)* s, FILE *file); +IGRAPH_EXPORT int FUNCTION(igraph_stack, init)(TYPE(igraph_stack)* s, long int size); +IGRAPH_EXPORT void FUNCTION(igraph_stack, destroy)(TYPE(igraph_stack)* s); +IGRAPH_EXPORT int FUNCTION(igraph_stack, reserve)(TYPE(igraph_stack)* s, long int size); +IGRAPH_EXPORT igraph_bool_t FUNCTION(igraph_stack, empty)(TYPE(igraph_stack)* s); +IGRAPH_EXPORT long int FUNCTION(igraph_stack, size)(const TYPE(igraph_stack)* s); +IGRAPH_EXPORT void FUNCTION(igraph_stack, clear)(TYPE(igraph_stack)* s); +IGRAPH_EXPORT int FUNCTION(igraph_stack, push)(TYPE(igraph_stack)* s, BASE elem); +IGRAPH_EXPORT BASE FUNCTION(igraph_stack, pop)(TYPE(igraph_stack)* s); +IGRAPH_EXPORT BASE FUNCTION(igraph_stack, top)(const TYPE(igraph_stack)* s); +IGRAPH_EXPORT int FUNCTION(igraph_stack, print)(const TYPE(igraph_stack)* s); +IGRAPH_EXPORT int FUNCTION(igraph_stack, fprint)(const TYPE(igraph_stack)* s, FILE *file); diff --git a/src/leidenalg/igraph-R/igraph_statusbar.h b/src/rigraph/include/igraph_statusbar.h similarity index 65% rename from src/leidenalg/igraph-R/igraph_statusbar.h rename to src/rigraph/include/igraph_statusbar.h index 1fc845e..c3cf4f3 100644 --- a/src/leidenalg/igraph-R/igraph_statusbar.h +++ b/src/rigraph/include/igraph_statusbar.h @@ -1,69 +1,61 @@ /* -*- mode: C -*- */ -/* +/* IGraph library. Copyright (C) 2010-2012 Gabor Csardi 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 + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#ifndef IGRAPH_STATUSBAR -#define IGRAPH_STATUSBAR - -#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 +#ifndef IGRAPH_STATUSBAR_H +#define IGRAPH_STATUSBAR_H + +#include "igraph_decls.h" __BEGIN_DECLS /** * \section about_status_handlers Status reporting - * + * * - * 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, + * 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. * - * + * * - * Status handler functions must be of type \ref igraph_status_handler_t + * 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. * - * + * * - * Igraph functions report their status via a call to the + * igraph functions report their status via a call to the * \ref IGRAPH_STATUS() or the \ref IGRAPH_STATUSF() macro. * */ - -/** + +/** * \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. @@ -72,63 +64,62 @@ __BEGIN_DECLS typedef int igraph_status_handler_t(const char *message, void *data); -extern igraph_status_handler_t igraph_status_handler_stderr; +IGRAPH_EXPORT extern igraph_status_handler_t igraph_status_handler_stderr; -igraph_status_handler_t * -igraph_set_status_handler(igraph_status_handler_t new_handler); +IGRAPH_EXPORT igraph_status_handler_t * igraph_set_status_handler(igraph_status_handler_t new_handler); -int igraph_status(const char *message, void *data); +IGRAPH_EXPORT 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 + * 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_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) + do { \ + if (igraph_status((message), (data)) != IGRAPH_SUCCESS) { \ + IGRAPH_FINALLY_FREE(); \ + return IGRAPH_INTERRUPTED; \ + } \ + } while (0) -int igraph_statusf(const char *message, void *data, ...); +IGRAPH_EXPORT int igraph_statusf(const char *message, void *data, ...); -/** +/** * \define IGRAPH_STATUSF - * Report the status from an igraph function + * 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() + * 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_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) + do { \ + if (igraph_statusf args != IGRAPH_SUCCESS) { \ + IGRAPH_FINALLY_FREE(); \ + return IGRAPH_INTERRUPTED; \ + } \ + } while (0) __END_DECLS diff --git a/src/rigraph/include/igraph_structural.h b/src/rigraph/include/igraph_structural.h new file mode 100644 index 0000000..100aa47 --- /dev/null +++ b/src/rigraph/include/igraph_structural.h @@ -0,0 +1,128 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2009-2012 Gabor Csardi + 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 */ +/* -------------------------------------------------- */ + +IGRAPH_EXPORT int igraph_are_connected(const igraph_t *graph, igraph_integer_t v1, igraph_integer_t v2, igraph_bool_t *res); +IGRAPH_EXPORT int igraph_count_multiple(const igraph_t *graph, igraph_vector_t *res, igraph_es_t es); +IGRAPH_EXPORT int igraph_density(const igraph_t *graph, igraph_real_t *res, + igraph_bool_t loops); +IGRAPH_EXPORT int igraph_diversity(const igraph_t *graph, const igraph_vector_t *weights, + igraph_vector_t *res, const igraph_vs_t vs); +IGRAPH_EXPORT int igraph_girth(const igraph_t *graph, igraph_integer_t *girth, + igraph_vector_t *circle); +IGRAPH_EXPORT int igraph_has_loop(const igraph_t *graph, igraph_bool_t *res); +IGRAPH_EXPORT int igraph_has_multiple(const igraph_t *graph, igraph_bool_t *res); +IGRAPH_EXPORT int igraph_is_loop(const igraph_t *graph, igraph_vector_bool_t *res, + igraph_es_t es); +IGRAPH_EXPORT int igraph_is_multiple(const igraph_t *graph, igraph_vector_bool_t *res, + igraph_es_t es); +IGRAPH_EXPORT int igraph_is_mutual(const igraph_t *graph, igraph_vector_bool_t *res, igraph_es_t es); +IGRAPH_EXPORT int igraph_is_simple(const igraph_t *graph, igraph_bool_t *res); +IGRAPH_EXPORT int igraph_is_tree(const igraph_t *graph, igraph_bool_t *res, igraph_integer_t *root, igraph_neimode_t mode); +IGRAPH_EXPORT int igraph_maxdegree(const igraph_t *graph, igraph_integer_t *res, + igraph_vs_t vids, igraph_neimode_t mode, + igraph_bool_t loops); +IGRAPH_EXPORT int igraph_reciprocity(const igraph_t *graph, igraph_real_t *res, + igraph_bool_t ignore_loops, + igraph_reciprocity_t mode); +IGRAPH_EXPORT 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); +IGRAPH_EXPORT 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); + +/* -------------------------------------------------- */ +/* Structural properties */ +/* -------------------------------------------------- */ + +IGRAPH_EXPORT int igraph_minimum_spanning_tree(const igraph_t *graph, igraph_vector_t *res, + const igraph_vector_t *weights); +IGRAPH_EXPORT int igraph_minimum_spanning_tree_unweighted(const igraph_t *graph, + igraph_t *mst); +IGRAPH_EXPORT int igraph_minimum_spanning_tree_prim(const igraph_t *graph, igraph_t *mst, + const igraph_vector_t *weights); +IGRAPH_EXPORT int igraph_random_spanning_tree(const igraph_t *graph, igraph_vector_t *res, + igraph_integer_t vid); + +IGRAPH_EXPORT int igraph_subcomponent(const igraph_t *graph, igraph_vector_t *res, igraph_real_t vid, + igraph_neimode_t mode); + +IGRAPH_EXPORT 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); + +IGRAPH_EXPORT int igraph_maximum_cardinality_search(const igraph_t *graph, + igraph_vector_t *alpha, + igraph_vector_t *alpham1); +IGRAPH_EXPORT 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); +IGRAPH_EXPORT 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); + +IGRAPH_EXPORT int igraph_feedback_arc_set(const igraph_t *graph, igraph_vector_t *result, + const igraph_vector_t *weights, igraph_fas_algorithm_t algo); + +/* -------------------------------------------------- */ +/* Spectral Properties */ +/* -------------------------------------------------- */ + +IGRAPH_EXPORT int igraph_laplacian(const igraph_t *graph, igraph_matrix_t *res, + igraph_sparsemat_t *sparseres, + igraph_bool_t normalized, + const igraph_vector_t *weights); + +__END_DECLS + +#endif diff --git a/src/rigraph/include/igraph_strvector.h b/src/rigraph/include/igraph_strvector.h new file mode 100644 index 0000000..525fef8 --- /dev/null +++ b/src/rigraph/include/igraph_strvector.h @@ -0,0 +1,97 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2009-2012 Gabor Csardi + 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_strvector_destroy, v); } while (0) + +IGRAPH_EXPORT int igraph_strvector_init(igraph_strvector_t *sv, long int len); +IGRAPH_EXPORT void igraph_strvector_destroy(igraph_strvector_t *sv); +IGRAPH_EXPORT long int igraph_strvector_size(const igraph_strvector_t *sv); +IGRAPH_EXPORT void igraph_strvector_get(const igraph_strvector_t *sv, + long int idx, char **value); +IGRAPH_EXPORT int igraph_strvector_set(igraph_strvector_t *sv, long int idx, + const char *value); +IGRAPH_EXPORT int igraph_strvector_set2(igraph_strvector_t *sv, long int idx, + const char *value, int len); +IGRAPH_EXPORT void igraph_strvector_clear(igraph_strvector_t *sv); +IGRAPH_EXPORT void igraph_strvector_remove_section(igraph_strvector_t *v, long int from, + long int to); +IGRAPH_EXPORT void igraph_strvector_remove(igraph_strvector_t *v, long int elem); +IGRAPH_EXPORT void igraph_strvector_move_interval(igraph_strvector_t *v, long int begin, + long int end, long int to); +IGRAPH_EXPORT int igraph_strvector_copy(igraph_strvector_t *to, + const igraph_strvector_t *from); +IGRAPH_EXPORT int igraph_strvector_append(igraph_strvector_t *to, + const igraph_strvector_t *from); +IGRAPH_EXPORT int igraph_strvector_resize(igraph_strvector_t* v, long int newsize); +IGRAPH_EXPORT int igraph_strvector_add(igraph_strvector_t *v, const char *value); +IGRAPH_EXPORT void igraph_strvector_permdelete(igraph_strvector_t *v, const igraph_vector_t *index, + long int nremove); +IGRAPH_EXPORT void igraph_strvector_remove_negidx(igraph_strvector_t *v, const igraph_vector_t *neg, + long int nremove); +IGRAPH_EXPORT int igraph_strvector_print(const igraph_strvector_t *v, FILE *file, + const char *sep); + +IGRAPH_EXPORT int igraph_strvector_index(const igraph_strvector_t *v, + igraph_strvector_t *newv, + const igraph_vector_t *idx); + + +__END_DECLS + +#endif diff --git a/src/leidenalg/igraph-R/igraph_threading.h b/src/rigraph/include/igraph_threading.h similarity index 68% rename from src/leidenalg/igraph-R/igraph_threading.h rename to src/rigraph/include/igraph_threading.h index 1df33d1..ebbdb17 100644 --- a/src/leidenalg/igraph-R/igraph_threading.h +++ b/src/rigraph/include/igraph_threading.h @@ -1,22 +1,22 @@ /* -*- mode: C -*- */ -/* +/* IGraph library. Copyright (C) 2011-2012 Gabor Csardi 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 + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -24,28 +24,24 @@ #ifndef IGRAPH_THREADING_H #define IGRAPH_THREADING_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_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. + * + * Specifies whether igraph was built in thread-safe mode. + * + * This macro is defined to 1 if the current build of the igraph library is + * built in thread-safe mode, and 0 if it is not. A thread-safe igraph library + * attempts to use thread-local data structures instead of global ones, but + * note that this is not (and can not) be guaranteed for third-party libraries + * that igraph links to. */ -#define IGRAPH_THREAD_SAFE 1 +#define IGRAPH_THREAD_SAFE 0 __END_DECLS #endif - diff --git a/src/rigraph/include/igraph_threading.h.in b/src/rigraph/include/igraph_threading.h.in new file mode 100644 index 0000000..1ddeb11 --- /dev/null +++ b/src/rigraph/include/igraph_threading.h.in @@ -0,0 +1,47 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2011-2012 Gabor Csardi + 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 + * + * Specifies whether igraph was built in thread-safe mode. + * + * This macro is defined to 1 if the current build of the igraph library is + * built in thread-safe mode, and 0 if it is not. A thread-safe igraph library + * attempts to use thread-local data structures instead of global ones, but + * note that this is not (and can not) be guaranteed for third-party libraries + * that igraph links to. + */ + +#cmakedefine01 IGRAPH_THREAD_SAFE + +__END_DECLS + +#endif diff --git a/src/rigraph/include/igraph_topology.h b/src/rigraph/include/igraph_topology.h new file mode 100644 index 0000000..90e656b --- /dev/null +++ b/src/rigraph/include/igraph_topology.h @@ -0,0 +1,285 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2009-2012 Gabor Csardi + 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 + +/* -------------------------------------------------- */ +/* Directed acyclic graphs */ +/* -------------------------------------------------- */ + +IGRAPH_EXPORT int igraph_topological_sorting(const igraph_t *graph, igraph_vector_t *res, + igraph_neimode_t mode); +IGRAPH_EXPORT int igraph_is_dag(const igraph_t *graph, igraph_bool_t *res); +IGRAPH_EXPORT int igraph_transitive_closure_dag(const igraph_t *graph, + igraph_t *closure); + +/* -------------------------------------------------- */ +/* Graph isomorphisms */ +/* -------------------------------------------------- */ + +/* Common functions */ +IGRAPH_EXPORT 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 */ +IGRAPH_EXPORT int igraph_isomorphic(const igraph_t *graph1, const igraph_t *graph2, + igraph_bool_t *iso); +IGRAPH_EXPORT int igraph_subisomorphic(const igraph_t *graph1, const igraph_t *graph2, + igraph_bool_t *iso); + +/* LAD */ +IGRAPH_EXPORT int igraph_subisomorphic_lad(const igraph_t *pattern, const igraph_t *target, + const 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. + * + * 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. + * + * 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); + +IGRAPH_EXPORT 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_EXPORT 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); +IGRAPH_EXPORT 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_EXPORT 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_EXPORT 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_EXPORT 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); +IGRAPH_EXPORT 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_EXPORT 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 + * \brief Splitting heuristics for Bliss. + * + * \c IGRAPH_BLISS_FL provides good performance for many graphs, and is a reasonable + * default choice. \c IGRAPH_BLISS_FSM is recommended for graphs that have some + * combinatorial structure, and is the default of the Bliss library's command + * line tool. + * + * \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; + +IGRAPH_EXPORT 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); +IGRAPH_EXPORT 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); + +IGRAPH_EXPORT int igraph_automorphisms(const igraph_t *graph, const igraph_vector_int_t *colors, + igraph_bliss_sh_t sh, igraph_bliss_info_t *info); + +IGRAPH_EXPORT 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 */ +IGRAPH_EXPORT int igraph_isomorphic_34(const igraph_t *graph1, const igraph_t *graph2, + igraph_bool_t *iso); +IGRAPH_EXPORT int igraph_isoclass(const igraph_t *graph, igraph_integer_t *isoclass); +IGRAPH_EXPORT int igraph_isoclass_subgraph(const igraph_t *graph, const igraph_vector_t *vids, + igraph_integer_t *isoclass); +IGRAPH_EXPORT 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/src/rigraph/include/igraph_transitivity.h b/src/rigraph/include/igraph_transitivity.h new file mode 100644 index 0000000..33aee54 --- /dev/null +++ b/src/rigraph/include/igraph_transitivity.h @@ -0,0 +1,63 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2009-2012 Gabor Csardi + 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 + +IGRAPH_EXPORT int igraph_transitivity_undirected(const igraph_t *graph, + igraph_real_t *res, + igraph_transitivity_mode_t mode); +IGRAPH_EXPORT int igraph_transitivity_local_undirected(const igraph_t *graph, + igraph_vector_t *res, + const igraph_vs_t vids, + igraph_transitivity_mode_t mode); +IGRAPH_EXPORT int igraph_transitivity_local_undirected1(const igraph_t *graph, + igraph_vector_t *res, + const igraph_vs_t vids, + igraph_transitivity_mode_t mode); +IGRAPH_EXPORT int igraph_transitivity_local_undirected2(const igraph_t *graph, + igraph_vector_t *res, + const igraph_vs_t vids, + igraph_transitivity_mode_t mode); +IGRAPH_EXPORT int igraph_transitivity_local_undirected4(const igraph_t *graph, + igraph_vector_t *res, + igraph_transitivity_mode_t mode); +IGRAPH_EXPORT int igraph_transitivity_avglocal_undirected(const igraph_t *graph, + igraph_real_t *res, + igraph_transitivity_mode_t mode); +IGRAPH_EXPORT 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/src/leidenalg/igraph-R/igraph_types.h b/src/rigraph/include/igraph_types.h similarity index 50% rename from src/leidenalg/igraph-R/igraph_types.h rename to src/rigraph/include/igraph_types.h index 62bc058..07e4bef 100644 --- a/src/leidenalg/igraph-R/igraph_types.h +++ b/src/rigraph/include/igraph_types.h @@ -1,43 +1,35 @@ /* -*- mode: C -*- */ -/* +/* IGraph library. Copyright (C) 2003-2012 Gabor Csardi 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 + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#ifndef REST_TYPES_H -#define REST_TYPES_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 +#ifndef IGRAPH_TYPES_H +#define IGRAPH_TYPES_H + +#include "igraph_decls.h" __BEGIN_DECLS #ifndef _GNU_SOURCE -# define _GNU_SOURCE 1 + #define _GNU_SOURCE 1 #endif #include "igraph_error.h" @@ -49,47 +41,34 @@ typedef int igraph_integer_t; typedef double igraph_real_t; typedef int igraph_bool_t; +/* printf format specifier for igraph_integer_t */ +#define IGRAPH_PRId "d" + /* Replacements for printf that print doubles in the same way on all platforms * (even for NaN and infinities) */ -int igraph_real_printf(igraph_real_t val); -int igraph_real_fprintf(FILE *file, igraph_real_t val); -int igraph_real_snprintf(char* str, size_t size, igraph_real_t val); +IGRAPH_EXPORT int igraph_real_printf(igraph_real_t val); +IGRAPH_EXPORT int igraph_real_fprintf(FILE *file, igraph_real_t val); +IGRAPH_EXPORT 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 */ -int igraph_real_printf_precise(igraph_real_t val); -int igraph_real_fprintf_precise(FILE *file, igraph_real_t val); -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 +IGRAPH_EXPORT int igraph_real_printf_precise(igraph_real_t val); +IGRAPH_EXPORT int igraph_real_fprintf_precise(FILE *file, igraph_real_t val); +IGRAPH_EXPORT int igraph_real_snprintf_precise(char* str, size_t size, igraph_real_t val); + +#define IGRAPH_INFINITY INFINITY +#define IGRAPH_POSINFINITY INFINITY +#define IGRAPH_NEGINFINITY (-INFINITY) -int igraph_finite(double x); +IGRAPH_EXPORT int igraph_finite(double x); #define IGRAPH_FINITE(x) igraph_finite(x) -int igraph_is_nan(double x); -int igraph_is_inf(double x); -int igraph_is_posinf(double x); -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 +IGRAPH_EXPORT int igraph_is_nan(double x); +IGRAPH_EXPORT int igraph_is_inf(double x); +IGRAPH_EXPORT int igraph_is_posinf(double x); +IGRAPH_EXPORT int igraph_is_neginf(double x); + +#define IGRAPH_NAN NAN __END_DECLS diff --git a/src/rigraph/include/igraph_vector.h b/src/rigraph/include/igraph_vector.h new file mode 100644 index 0000000..8077328 --- /dev/null +++ b/src/rigraph/include/igraph_vector.h @@ -0,0 +1,183 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2009-2012 Gabor Csardi + 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" + +__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_CHAR_INIT_FINALLY +#define IGRAPH_VECTOR_CHAR_INIT_FINALLY(v, size) \ + do { IGRAPH_CHECK(igraph_vector_char_init(v, size)); \ + IGRAPH_FINALLY(igraph_vector_char_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 */ +/* -------------------------------------------------- */ + +IGRAPH_EXPORT int igraph_vector_floor(const igraph_vector_t *from, igraph_vector_long_t *to); +IGRAPH_EXPORT int igraph_vector_round(const igraph_vector_t *from, igraph_vector_long_t *to); + +IGRAPH_EXPORT igraph_bool_t igraph_vector_e_tol(const igraph_vector_t *lhs, + const igraph_vector_t *rhs, + igraph_real_t tol); + +IGRAPH_EXPORT int igraph_vector_zapsmall(igraph_vector_t *v, igraph_real_t tol); + +IGRAPH_EXPORT int igraph_vector_order(const igraph_vector_t* v, const igraph_vector_t *v2, + igraph_vector_t* res, igraph_real_t maxval); +IGRAPH_EXPORT int igraph_vector_order1(const igraph_vector_t* v, + igraph_vector_t* res, igraph_real_t maxval); +IGRAPH_EXPORT int igraph_vector_order1_int(const igraph_vector_t* v, + igraph_vector_int_t* res, igraph_real_t maxval); +IGRAPH_EXPORT int igraph_vector_order2(igraph_vector_t *v); +IGRAPH_EXPORT int igraph_vector_rank(const igraph_vector_t *v, igraph_vector_t *res, + long int nodes); +IGRAPH_EXPORT int igraph_vector_is_nan(const igraph_vector_t *v, + igraph_vector_bool_t *is_nan); +IGRAPH_EXPORT igraph_bool_t igraph_vector_is_any_nan(const igraph_vector_t *v); + +__END_DECLS + +#endif diff --git a/src/rigraph/include/igraph_vector_pmt.h b/src/rigraph/include/igraph_vector_pmt.h new file mode 100644 index 0000000..1962d29 --- /dev/null +++ b/src/rigraph/include/igraph_vector_pmt.h @@ -0,0 +1,290 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2007-2012 Gabor Csardi + 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 */ +/*--------------------*/ + +IGRAPH_EXPORT int FUNCTION(igraph_vector, init)(TYPE(igraph_vector)* v, long int size); +IGRAPH_EXPORT int FUNCTION(igraph_vector, init_copy)(TYPE(igraph_vector)* v, + const BASE* data, long int length); + +#ifndef NOTORDERED +IGRAPH_EXPORT int FUNCTION(igraph_vector, init_seq)(TYPE(igraph_vector)*v, BASE from, BASE to); +#endif + +IGRAPH_EXPORT int FUNCTION(igraph_vector, copy)(TYPE(igraph_vector) *to, + const TYPE(igraph_vector) *from); +IGRAPH_EXPORT void FUNCTION(igraph_vector, destroy)(TYPE(igraph_vector)* v); + +IGRAPH_EXPORT 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 #define. + * \param v The vector object. + * + * Time complexity: O(1). + */ +#define VECTOR(v) ((v).stor_begin) +#endif + +IGRAPH_EXPORT BASE FUNCTION(igraph_vector, e)(const TYPE(igraph_vector)* v, long int pos); +IGRAPH_EXPORT BASE* FUNCTION(igraph_vector, e_ptr)(const TYPE(igraph_vector)* v, long int pos); +IGRAPH_EXPORT void FUNCTION(igraph_vector, set)(TYPE(igraph_vector)* v, long int pos, BASE value); +IGRAPH_EXPORT BASE FUNCTION(igraph_vector, tail)(const TYPE(igraph_vector) *v); + +/*-----------------------*/ +/* Initializing elements */ +/*-----------------------*/ + +IGRAPH_EXPORT void FUNCTION(igraph_vector, null)(TYPE(igraph_vector)* v); +IGRAPH_EXPORT void FUNCTION(igraph_vector, fill)(TYPE(igraph_vector)* v, BASE e); + +/*-----------------------*/ +/* Vector views */ +/*-----------------------*/ + +IGRAPH_EXPORT const TYPE(igraph_vector) *FUNCTION(igraph_vector, view)(const TYPE(igraph_vector) *v, + const BASE *data, + long int length); + +/*-----------------------*/ +/* Copying vectors */ +/*-----------------------*/ + +IGRAPH_EXPORT void FUNCTION(igraph_vector, copy_to)(const TYPE(igraph_vector) *v, BASE* to); +IGRAPH_EXPORT int FUNCTION(igraph_vector, update)(TYPE(igraph_vector) *to, + const TYPE(igraph_vector) *from); +IGRAPH_EXPORT int FUNCTION(igraph_vector, append)(TYPE(igraph_vector) *to, + const TYPE(igraph_vector) *from); +IGRAPH_EXPORT int FUNCTION(igraph_vector, swap)(TYPE(igraph_vector) *v1, TYPE(igraph_vector) *v2); + +/*-----------------------*/ +/* Exchanging elements */ +/*-----------------------*/ + +IGRAPH_EXPORT int FUNCTION(igraph_vector, swap_elements)(TYPE(igraph_vector) *v, + long int i, long int j); +IGRAPH_EXPORT int FUNCTION(igraph_vector, reverse)(TYPE(igraph_vector) *v); +IGRAPH_EXPORT int FUNCTION(igraph_vector, shuffle)(TYPE(igraph_vector) *v); + +/*-----------------------*/ +/* Vector operations */ +/*-----------------------*/ + +IGRAPH_EXPORT void FUNCTION(igraph_vector, add_constant)(TYPE(igraph_vector) *v, BASE plus); +IGRAPH_EXPORT void FUNCTION(igraph_vector, scale)(TYPE(igraph_vector) *v, BASE by); +IGRAPH_EXPORT int FUNCTION(igraph_vector, add)(TYPE(igraph_vector) *v1, + const TYPE(igraph_vector) *v2); +IGRAPH_EXPORT int FUNCTION(igraph_vector, sub)(TYPE(igraph_vector) *v1, + const TYPE(igraph_vector) *v2); +IGRAPH_EXPORT int FUNCTION(igraph_vector, mul)(TYPE(igraph_vector) *v1, + const TYPE(igraph_vector) *v2); +IGRAPH_EXPORT int FUNCTION(igraph_vector, div)(TYPE(igraph_vector) *v1, + const TYPE(igraph_vector) *v2); +IGRAPH_EXPORT int FUNCTION(igraph_vector, cumsum)(TYPE(igraph_vector) *to, + const TYPE(igraph_vector) *from); + +#ifndef NOABS + IGRAPH_EXPORT int FUNCTION(igraph_vector, abs)(TYPE(igraph_vector) *v); +#endif + +/*------------------------------*/ +/* Comparison */ +/*------------------------------*/ + +IGRAPH_EXPORT igraph_bool_t FUNCTION(igraph_vector, all_e)(const TYPE(igraph_vector) *lhs, + const TYPE(igraph_vector) *rhs); +#ifndef NOTORDERED +IGRAPH_EXPORT igraph_bool_t FUNCTION(igraph_vector, all_l)(const TYPE(igraph_vector) *lhs, + const TYPE(igraph_vector) *rhs); +IGRAPH_EXPORT igraph_bool_t FUNCTION(igraph_vector, all_g)(const TYPE(igraph_vector) *lhs, + const TYPE(igraph_vector) *rhs); +IGRAPH_EXPORT igraph_bool_t FUNCTION(igraph_vector, all_le)(const TYPE(igraph_vector) *lhs, + const TYPE(igraph_vector) *rhs); +IGRAPH_EXPORT igraph_bool_t FUNCTION(igraph_vector, all_ge)(const TYPE(igraph_vector) *lhs, + const TYPE(igraph_vector) *rhs); +IGRAPH_EXPORT int FUNCTION(igraph_vector, lex_cmp)(const void *lhs, + const void *rhs); +IGRAPH_EXPORT int FUNCTION(igraph_vector, colex_cmp)(const void *lhs, + const void *rhs); +#endif + +/*------------------------------*/ +/* Finding minimum and maximum */ +/*------------------------------*/ + +#ifndef NOTORDERED +IGRAPH_EXPORT BASE FUNCTION(igraph_vector, min)(const TYPE(igraph_vector)* v); +IGRAPH_EXPORT BASE FUNCTION(igraph_vector, max)(const TYPE(igraph_vector)* v); +IGRAPH_EXPORT long int FUNCTION(igraph_vector, which_min)(const TYPE(igraph_vector)* v); +IGRAPH_EXPORT long int FUNCTION(igraph_vector, which_max)(const TYPE(igraph_vector)* v); +IGRAPH_EXPORT int FUNCTION(igraph_vector, minmax)(const TYPE(igraph_vector) *v, + BASE *min, BASE *max); +IGRAPH_EXPORT int FUNCTION(igraph_vector, which_minmax)(const TYPE(igraph_vector) *v, + long int *which_min, long int *which_max); +#endif + +/*-------------------*/ +/* Vector properties */ +/*-------------------*/ + +IGRAPH_EXPORT igraph_bool_t FUNCTION(igraph_vector, empty) (const TYPE(igraph_vector)* v); +IGRAPH_EXPORT long int FUNCTION(igraph_vector, size) (const TYPE(igraph_vector)* v); +IGRAPH_EXPORT igraph_bool_t FUNCTION(igraph_vector, isnull)(const TYPE(igraph_vector) *v); +IGRAPH_EXPORT BASE FUNCTION(igraph_vector, sum)(const TYPE(igraph_vector) *v); +IGRAPH_EXPORT igraph_real_t FUNCTION(igraph_vector, sumsq)(const TYPE(igraph_vector) *v); +IGRAPH_EXPORT BASE FUNCTION(igraph_vector, prod)(const TYPE(igraph_vector) *v); +#ifndef NOTORDERED +IGRAPH_EXPORT igraph_bool_t FUNCTION(igraph_vector, isininterval)(const TYPE(igraph_vector) *v, + BASE low, BASE high); +IGRAPH_EXPORT igraph_bool_t FUNCTION(igraph_vector, any_smaller)(const TYPE(igraph_vector) *v, + BASE limit); +#endif +IGRAPH_EXPORT igraph_bool_t FUNCTION(igraph_vector, is_equal)(const TYPE(igraph_vector) *lhs, + const TYPE(igraph_vector) *rhs); +#ifndef NOTORDERED +IGRAPH_EXPORT igraph_real_t FUNCTION(igraph_vector, maxdifference)(const TYPE(igraph_vector) *m1, + const TYPE(igraph_vector) *m2); +#endif + +/*------------------------*/ +/* Searching for elements */ +/*------------------------*/ + +IGRAPH_EXPORT igraph_bool_t FUNCTION(igraph_vector, contains)(const TYPE(igraph_vector) *v, BASE e); +IGRAPH_EXPORT igraph_bool_t FUNCTION(igraph_vector, search)(const TYPE(igraph_vector) *v, + long int from, BASE what, + long int *pos); +#ifndef NOTORDERED +IGRAPH_EXPORT igraph_bool_t FUNCTION(igraph_vector, binsearch_slice)(const TYPE(igraph_vector) *v, + BASE what, long int *pos, + long int start, long int end); +IGRAPH_EXPORT igraph_bool_t FUNCTION(igraph_vector, binsearch)(const TYPE(igraph_vector) *v, + BASE what, long int *pos); +IGRAPH_EXPORT igraph_bool_t FUNCTION(igraph_vector, binsearch2)(const TYPE(igraph_vector) *v, + BASE what); +#endif + +/*------------------------*/ +/* Resizing operations */ +/*------------------------*/ + +IGRAPH_EXPORT void FUNCTION(igraph_vector, clear)(TYPE(igraph_vector)* v); +IGRAPH_EXPORT int FUNCTION(igraph_vector, resize)(TYPE(igraph_vector)* v, long int newsize); +IGRAPH_EXPORT int FUNCTION(igraph_vector, resize_min)(TYPE(igraph_vector)*v); +IGRAPH_EXPORT int FUNCTION(igraph_vector, reserve)(TYPE(igraph_vector)* v, long int size); +IGRAPH_EXPORT int FUNCTION(igraph_vector, push_back)(TYPE(igraph_vector)* v, BASE e); +IGRAPH_EXPORT BASE FUNCTION(igraph_vector, pop_back)(TYPE(igraph_vector)* v); +IGRAPH_EXPORT int FUNCTION(igraph_vector, insert)(TYPE(igraph_vector) *v, long int pos, BASE value); +IGRAPH_EXPORT void FUNCTION(igraph_vector, remove)(TYPE(igraph_vector) *v, long int elem); +IGRAPH_EXPORT void FUNCTION(igraph_vector, remove_section)(TYPE(igraph_vector) *v, + long int from, long int to); + +/*-----------*/ +/* Sorting */ +/*-----------*/ + +#ifndef NOTORDERED + +IGRAPH_EXPORT void FUNCTION(igraph_vector, sort)(TYPE(igraph_vector) *v); +IGRAPH_EXPORT void FUNCTION(igraph_vector, reverse_sort)(TYPE(igraph_vector) *v); +IGRAPH_EXPORT long int FUNCTION(igraph_vector, qsort_ind)(TYPE(igraph_vector) *v, + igraph_vector_t *inds, igraph_bool_t descending); + +#endif + +/*-----------*/ +/* Printing */ +/*-----------*/ + +IGRAPH_EXPORT int FUNCTION(igraph_vector, print)(const TYPE(igraph_vector) *v); +IGRAPH_EXPORT int FUNCTION(igraph_vector, printf)(const TYPE(igraph_vector) *v, + const char *format); +IGRAPH_EXPORT int FUNCTION(igraph_vector, fprint)(const TYPE(igraph_vector) *v, FILE *file); + +#ifdef BASE_COMPLEX + +IGRAPH_EXPORT int igraph_vector_complex_real(const igraph_vector_complex_t *v, + igraph_vector_t *real); +IGRAPH_EXPORT int igraph_vector_complex_imag(const igraph_vector_complex_t *v, + igraph_vector_t *imag); +IGRAPH_EXPORT int igraph_vector_complex_realimag(const igraph_vector_complex_t *v, + igraph_vector_t *real, + igraph_vector_t *imag); +IGRAPH_EXPORT int igraph_vector_complex_create(igraph_vector_complex_t *v, + const igraph_vector_t *real, + const igraph_vector_t *imag); +IGRAPH_EXPORT int igraph_vector_complex_create_polar(igraph_vector_complex_t *v, + const igraph_vector_t *r, + const igraph_vector_t *theta); + +#endif + +IGRAPH_EXPORT int FUNCTION(igraph_vector, init_real)(TYPE(igraph_vector)*v, int no, ...); +IGRAPH_EXPORT int FUNCTION(igraph_vector, init_int)(TYPE(igraph_vector)*v, int no, ...); +IGRAPH_EXPORT int FUNCTION(igraph_vector, init_real_end)(TYPE(igraph_vector)*v, double endmark, ...); +IGRAPH_EXPORT int FUNCTION(igraph_vector, init_int_end)(TYPE(igraph_vector)*v, int endmark, ...); + +IGRAPH_EXPORT int FUNCTION(igraph_vector, move_interval)(TYPE(igraph_vector) *v, + long int begin, long int end, long int to); +IGRAPH_EXPORT int FUNCTION(igraph_vector, move_interval2)(TYPE(igraph_vector) *v, + long int begin, long int end, long int to); +IGRAPH_EXPORT void FUNCTION(igraph_vector, permdelete)(TYPE(igraph_vector) *v, + const igraph_vector_t *index, + long int nremove); +#ifndef NOTORDERED +IGRAPH_EXPORT int FUNCTION(igraph_vector, filter_smaller)(TYPE(igraph_vector) *v, BASE elem); +#endif +IGRAPH_EXPORT int FUNCTION(igraph_vector, get_interval)(const TYPE(igraph_vector) *v, + TYPE(igraph_vector) *res, + long int from, long int to); +#ifndef NOTORDERED +IGRAPH_EXPORT int FUNCTION(igraph_vector, difference_sorted)(const TYPE(igraph_vector) *v1, + const TYPE(igraph_vector) *v2, TYPE(igraph_vector) *result); +IGRAPH_EXPORT int FUNCTION(igraph_vector, intersect_sorted)(const TYPE(igraph_vector) *v1, + const TYPE(igraph_vector) *v2, TYPE(igraph_vector) *result); +#endif +IGRAPH_EXPORT int FUNCTION(igraph_vector, index)(const TYPE(igraph_vector) *v, + TYPE(igraph_vector) *newv, + const igraph_vector_t *idx); + +IGRAPH_EXPORT int FUNCTION(igraph_vector, index_int)(TYPE(igraph_vector) *v, + const igraph_vector_int_t *idx); diff --git a/src/rigraph/include/igraph_vector_ptr.h b/src/rigraph/include/igraph_vector_ptr.h new file mode 100644 index 0000000..c2667be --- /dev/null +++ b/src/rigraph/include/igraph_vector_ptr.h @@ -0,0 +1,100 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2009-2012 Gabor Csardi + 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) + +IGRAPH_EXPORT int igraph_vector_ptr_init (igraph_vector_ptr_t* v, long int size); +IGRAPH_EXPORT int igraph_vector_ptr_init_copy (igraph_vector_ptr_t* v, void** data, long int length); +IGRAPH_EXPORT const igraph_vector_ptr_t *igraph_vector_ptr_view (const igraph_vector_ptr_t *v, + void *const *data, long int length); +IGRAPH_EXPORT void igraph_vector_ptr_destroy (igraph_vector_ptr_t* v); +IGRAPH_EXPORT void igraph_vector_ptr_free_all (igraph_vector_ptr_t* v); +IGRAPH_EXPORT void igraph_vector_ptr_destroy_all (igraph_vector_ptr_t* v); +IGRAPH_EXPORT int igraph_vector_ptr_reserve (igraph_vector_ptr_t* v, long int size); +IGRAPH_EXPORT igraph_bool_t igraph_vector_ptr_empty (const igraph_vector_ptr_t* v); +IGRAPH_EXPORT long int igraph_vector_ptr_size (const igraph_vector_ptr_t* v); +IGRAPH_EXPORT void igraph_vector_ptr_clear (igraph_vector_ptr_t* v); +IGRAPH_EXPORT void igraph_vector_ptr_null (igraph_vector_ptr_t* v); +IGRAPH_EXPORT int igraph_vector_ptr_push_back (igraph_vector_ptr_t* v, void* e); +IGRAPH_EXPORT int igraph_vector_ptr_append (igraph_vector_ptr_t *to, + const igraph_vector_ptr_t *from); +IGRAPH_EXPORT void *igraph_vector_ptr_pop_back (igraph_vector_ptr_t *v); +IGRAPH_EXPORT int igraph_vector_ptr_insert(igraph_vector_ptr_t *v, long int pos, void* e); +IGRAPH_EXPORT void* igraph_vector_ptr_e (const igraph_vector_ptr_t* v, long int pos); +IGRAPH_EXPORT void igraph_vector_ptr_set (igraph_vector_ptr_t* v, long int pos, void* value); +IGRAPH_EXPORT int igraph_vector_ptr_resize(igraph_vector_ptr_t* v, long int newsize); +IGRAPH_EXPORT void igraph_vector_ptr_copy_to(const igraph_vector_ptr_t *v, void** to); +IGRAPH_EXPORT int igraph_vector_ptr_copy(igraph_vector_ptr_t *to, const igraph_vector_ptr_t *from); +IGRAPH_EXPORT void igraph_vector_ptr_remove(igraph_vector_ptr_t *v, long int pos); +IGRAPH_EXPORT void igraph_vector_ptr_sort(igraph_vector_ptr_t *v, int(*compar)(const void*, const void*)); +IGRAPH_EXPORT int igraph_vector_ptr_index_int(igraph_vector_ptr_t *v, + const igraph_vector_int_t *idx); + +IGRAPH_EXPORT igraph_finally_func_t* igraph_vector_ptr_get_item_destructor(const igraph_vector_ptr_t *v); +IGRAPH_EXPORT 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/src/leidenalg/igraph-R/igraph_version.h b/src/rigraph/include/igraph_vector_type.h similarity index 74% rename from src/leidenalg/igraph-R/igraph_version.h rename to src/rigraph/include/igraph_vector_type.h index cd9d619..fef9d9f 100644 --- a/src/leidenalg/igraph-R/igraph_version.h +++ b/src/rigraph/include/igraph_vector_type.h @@ -1,36 +1,33 @@ /* -*- mode: C -*- */ -/* +/* IGraph library. - Copyright (C) 2010-2012 Gabor Csardi + Copyright (C) 2013 Gabor Csardi 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 + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#ifndef IGRAPH_VERSION_H -#define IGRAPH_VERSION_H - -#define IGRAPH_VERSION "0.6" - -int igraph_version(const char **version_string, - int *major, - int *minor, - int *subminor); - -#endif - +/** + * 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/src/rigraph/include/igraph_version.h b/src/rigraph/include/igraph_version.h new file mode 100644 index 0000000..222dd9d --- /dev/null +++ b/src/rigraph/include/igraph_version.h @@ -0,0 +1,44 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2010-2012 Gabor Csardi + 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 "1.3.0.9016" +#define IGRAPH_VERSION_MAJOR @PACKAGE_VERSION_MAJOR@ +#define IGRAPH_VERSION_MINOR @PACKAGE_VERSION_MINOR@ +#define IGRAPH_VERSION_PATCH @PACKAGE_VERSION_PATCH@ +#define IGRAPH_VERSION_PRERELEASE "@PACKAGE_VERSION_PRERELEASE@" + +IGRAPH_EXPORT int igraph_version(const char **version_string, + int *major, + int *minor, + int *subminor); + +__END_DECLS + +#endif diff --git a/src/rigraph/include/igraph_version.h.in b/src/rigraph/include/igraph_version.h.in new file mode 100644 index 0000000..cae3cfa --- /dev/null +++ b/src/rigraph/include/igraph_version.h.in @@ -0,0 +1,44 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2010-2012 Gabor Csardi + 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 "@PACKAGE_VERSION@" +#define IGRAPH_VERSION_MAJOR @PACKAGE_VERSION_MAJOR@ +#define IGRAPH_VERSION_MINOR @PACKAGE_VERSION_MINOR@ +#define IGRAPH_VERSION_PATCH @PACKAGE_VERSION_PATCH@ +#define IGRAPH_VERSION_PRERELEASE "@PACKAGE_VERSION_PRERELEASE@" + +IGRAPH_EXPORT int igraph_version(const char **version_string, + int *major, + int *minor, + int *subminor); + +__END_DECLS + +#endif diff --git a/src/leidenalg/igraph-R/igraph_visitor.h b/src/rigraph/include/igraph_visitor.h similarity index 68% rename from src/leidenalg/igraph-R/igraph_visitor.h rename to src/rigraph/include/igraph_visitor.h index 8abdcf7..936ff65 100644 --- a/src/leidenalg/igraph-R/igraph_visitor.h +++ b/src/rigraph/include/igraph_visitor.h @@ -1,22 +1,22 @@ /* -*- mode: C -*- */ -/* +/* IGraph library. Copyright (C) 2009-2012 Gabor Csardi 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 + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -24,16 +24,7 @@ #ifndef IGRAPH_VISITOR_H #define IGRAPH_VISITOR_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_decls.h" #include "igraph_constants.h" #include "igraph_types.h" #include "igraph_datatype.h" @@ -47,7 +38,7 @@ __BEGIN_DECLS /** * \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 @@ -71,37 +62,37 @@ __BEGIN_DECLS * 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. - * + * contain 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); - -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); + igraph_integer_t vid, + igraph_integer_t pred, + igraph_integer_t succ, + igraph_integer_t rank, + igraph_integer_t dist, + void *extra); + +IGRAPH_EXPORT 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_EXPORT int igraph_bfs_simple(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 @@ -118,23 +109,23 @@ int igraph_i_bfs(igraph_t *graph, igraph_integer_t vid, igraph_neimode_t mode, * 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. - * + * contain 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); - -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); + igraph_integer_t vid, + igraph_integer_t dist, + void *extra); + +IGRAPH_EXPORT 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 diff --git a/src/rigraph/init.c b/src/rigraph/init.c new file mode 100644 index 0000000..867478f --- /dev/null +++ b/src/rigraph/init.c @@ -0,0 +1,803 @@ +#include +#include +#include // for NULL +#include + +/* + The following symbols/expresssions for .NAME have been omitted + + call + + Most likely possible values need to be added below. +*/ + +/* FIXME: + Check these declarations against the C/Fortran source code. +*/ + +/* .C calls */ +extern void igraphhcass2(void *, void *, void *, void *, void *, void *); + +/* .Call calls */ +extern SEXP make_lazy(SEXP, SEXP, SEXP); +extern SEXP make_lazy_dots(SEXP, SEXP); +extern SEXP promise_env_(SEXP); +extern SEXP promise_expr_(SEXP); +extern SEXP UUID_gen(SEXP); + +/* non-autogenerated declarations */ +extern SEXP R_igraph_address(SEXP); +extern SEXP R_igraph_add_edges(SEXP, SEXP); +extern SEXP R_igraph_add_env(SEXP); +extern SEXP R_igraph_add_version_to_env(SEXP); +extern SEXP R_igraph_add_vertices(SEXP, SEXP); +extern SEXP R_igraph_adjacent_vertices(SEXP, SEXP, SEXP); +extern SEXP R_igraph_arpack(SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_bfs(SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_bipartite_projection(SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_community_edge_betweenness(SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_community_fastgreedy(SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_community_leading_eigenvector(SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_community_to_membership2(SEXP, SEXP, SEXP); +extern SEXP R_igraph_compose(SEXP, SEXP, SEXP); +extern SEXP R_igraph_dfs(SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_diameter(SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_disjoint_union(SEXP); +extern SEXP R_igraph_erdos_renyi_game(SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_es_adj(SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_es_pairs(SEXP, SEXP, SEXP); +extern SEXP R_igraph_es_path(SEXP, SEXP, SEXP); +extern SEXP R_igraph_farthest_points(SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_finalizer(); +extern SEXP R_igraph_get_adjedgelist(SEXP, SEXP); +extern SEXP R_igraph_get_adjlist(SEXP, SEXP); +extern SEXP R_igraph_get_all_simple_paths_pp(SEXP); +extern SEXP R_igraph_get_attr_mode(SEXP, SEXP); +extern SEXP R_igraph_get_diameter(SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_get_edge(SEXP, SEXP); +extern SEXP R_igraph_get_eids(SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_get_graph_id(SEXP); +extern SEXP R_igraph_get_shortest_paths(SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_getsphere(SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_girth(SEXP, SEXP); +extern SEXP R_igraph_graph_adjacency(SEXP, SEXP); +extern SEXP R_igraph_graph_version(SEXP); +extern SEXP R_igraph_graphlets_project(SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_grg_game(SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_i_levc_arp(SEXP, SEXP, SEXP); +extern SEXP R_igraph_identical_graphs(SEXP, SEXP, SEXP); +extern SEXP R_igraph_incident_edges(SEXP, SEXP, SEXP); +extern SEXP R_igraph_is_chordal(SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_laplacian(SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_layout_fruchterman_reingold(SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_layout_fruchterman_reingold_3d(SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_layout_graphopt(SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_layout_kamada_kawai(SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_layout_kamada_kawai_3d(SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_layout_reingold_tilford(SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_make_weak_ref(SEXP, SEXP, SEXP); +extern SEXP R_igraph_maximal_cliques(SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_maximal_cliques_count(SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_maximal_cliques_file(SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_mybracket(SEXP, SEXP); +extern SEXP R_igraph_mybracket2(SEXP, SEXP, SEXP); +extern SEXP R_igraph_mybracket2_copy(SEXP, SEXP, SEXP); +extern SEXP R_igraph_mybracket2_names(SEXP, SEXP, SEXP); +extern SEXP R_igraph_mybracket2_set(SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_mybracket3_set(SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_neighborhood(SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_neighborhood_size(SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_no_clusters(SEXP, SEXP); +extern SEXP R_igraph_psumtree_draw(SEXP, SEXP, SEXP); +extern SEXP R_igraph_scg_adjacency(SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_scg_laplacian(SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_scg_semiprojectors(SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_scg_stochastic(SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_set_verbose(SEXP); +extern SEXP R_igraph_shortest_paths(SEXP, SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_solve_lsap(SEXP, SEXP); +extern SEXP R_igraph_spinglass_community(SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_spinglass_my_community(SEXP, SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_st_vertex_connectivity(SEXP, SEXP, SEXP); +extern SEXP R_igraph_subisomorphic_lad(SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_transitivity_local_undirected_all(SEXP, SEXP); +extern SEXP R_igraph_version(); +extern SEXP R_igraph_vs_adj(SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_vs_nei(SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_walktrap_community(SEXP, SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_weak_ref_key(SEXP); +extern SEXP R_igraph_weak_ref_run_finalizer(SEXP); +extern SEXP R_igraph_weak_ref_value(SEXP); + +/* autogenerated declarations */ +extern SEXP R_igraph_adhesion(SEXP, SEXP); +extern SEXP R_igraph_adjacency_spectral_embedding(SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_adjacent_triangles(SEXP, SEXP); +extern SEXP R_igraph_adjlist(SEXP, SEXP, SEXP); +extern SEXP R_igraph_all_minimal_st_separators(SEXP); +extern SEXP R_igraph_all_st_cuts(SEXP, SEXP, SEXP); +extern SEXP R_igraph_all_st_mincuts(SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_are_connected(SEXP, SEXP, SEXP); +extern SEXP R_igraph_arpack_unpack_complex(SEXP, SEXP, SEXP); +extern SEXP R_igraph_articulation_points(SEXP); +extern SEXP R_igraph_assortativity(SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_assortativity_degree(SEXP, SEXP); +extern SEXP R_igraph_assortativity_nominal(SEXP, SEXP, SEXP); +extern SEXP R_igraph_asymmetric_preference_game(SEXP, SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_atlas(SEXP); +extern SEXP R_igraph_authority_score(SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_automorphism_group(SEXP, SEXP, SEXP); +extern SEXP R_igraph_automorphisms(SEXP, SEXP, SEXP); +extern SEXP R_igraph_average_local_efficiency(SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_average_path_length_dijkstra(SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_avg_nearest_neighbor_degree(SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_barabasi_aging_game(SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_barabasi_game(SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_betweenness(SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_betweenness_cutoff(SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_bibcoupling(SEXP, SEXP); +extern SEXP R_igraph_biconnected_components(SEXP); +extern SEXP R_igraph_bipartite_game_gnm(SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_bipartite_game_gnp(SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_bipartite_projection_size(SEXP, SEXP); +extern SEXP R_igraph_bridges(SEXP); +extern SEXP R_igraph_callaway_traits_game(SEXP, SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_canonical_permutation(SEXP, SEXP, SEXP); +extern SEXP R_igraph_centralization(SEXP, SEXP, SEXP); +extern SEXP R_igraph_centralization_betweenness(SEXP, SEXP, SEXP); +extern SEXP R_igraph_centralization_betweenness_tmax(SEXP, SEXP, SEXP); +extern SEXP R_igraph_centralization_closeness(SEXP, SEXP, SEXP); +extern SEXP R_igraph_centralization_closeness_tmax(SEXP, SEXP, SEXP); +extern SEXP R_igraph_centralization_degree(SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_centralization_degree_tmax(SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_centralization_eigenvector_centrality(SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_centralization_eigenvector_centrality_tmax(SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_cited_type_game(SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_citing_cited_type_game(SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_clique_number(SEXP); +extern SEXP R_igraph_clique_size_hist(SEXP, SEXP, SEXP); +extern SEXP R_igraph_cliques(SEXP, SEXP, SEXP); +extern SEXP R_igraph_closeness(SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_closeness_cutoff(SEXP, SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_clusters(SEXP, SEXP); +extern SEXP R_igraph_cocitation(SEXP, SEXP); +extern SEXP R_igraph_cohesion(SEXP, SEXP); +extern SEXP R_igraph_cohesive_blocks(SEXP); +extern SEXP R_igraph_community_fluid_communities(SEXP, SEXP); +extern SEXP R_igraph_community_infomap(SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_community_label_propagation(SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_community_leiden(SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_community_multilevel(SEXP, SEXP, SEXP); +extern SEXP R_igraph_community_optimal_modularity(SEXP, SEXP); +extern SEXP R_igraph_compare_communities(SEXP, SEXP, SEXP); +extern SEXP R_igraph_complementer(SEXP, SEXP); +extern SEXP R_igraph_connect_neighborhood(SEXP, SEXP, SEXP); +extern SEXP R_igraph_constraint(SEXP, SEXP, SEXP); +extern SEXP R_igraph_contract_vertices(SEXP, SEXP, SEXP); +extern SEXP R_igraph_convex_hull(SEXP); +extern SEXP R_igraph_coreness(SEXP, SEXP); +extern SEXP R_igraph_correlated_game(SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_correlated_pair_game(SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_count_isomorphisms_vf2(SEXP, SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_count_multiple(SEXP, SEXP); +extern SEXP R_igraph_count_subisomorphisms_vf2(SEXP, SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_create(SEXP, SEXP, SEXP); +extern SEXP R_igraph_create_bipartite(SEXP, SEXP, SEXP); +extern SEXP R_igraph_de_bruijn(SEXP, SEXP); +extern SEXP R_igraph_decompose(SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_degree(SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_degree_sequence_game(SEXP, SEXP, SEXP); +extern SEXP R_igraph_delete_edges(SEXP, SEXP); +extern SEXP R_igraph_delete_vertices(SEXP, SEXP); +extern SEXP R_igraph_density(SEXP, SEXP); +extern SEXP R_igraph_difference(SEXP, SEXP); +extern SEXP R_igraph_dim_select(SEXP); +extern SEXP R_igraph_diversity(SEXP, SEXP, SEXP); +extern SEXP R_igraph_dominator_tree(SEXP, SEXP, SEXP); +extern SEXP R_igraph_dot_product_game(SEXP, SEXP); +extern SEXP R_igraph_dyad_census(SEXP); +extern SEXP R_igraph_eccentricity(SEXP, SEXP, SEXP); +extern SEXP R_igraph_ecount(SEXP); +extern SEXP R_igraph_edge_betweenness(SEXP, SEXP, SEXP); +extern SEXP R_igraph_edge_betweenness_cutoff(SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_edge_connectivity(SEXP, SEXP); +extern SEXP R_igraph_edge_disjoint_paths(SEXP, SEXP, SEXP); +extern SEXP R_igraph_edges(SEXP, SEXP); +extern SEXP R_igraph_eigen_adjacency(SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_eigenvector_centrality(SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_empty(SEXP, SEXP); +extern SEXP R_igraph_establishment_game(SEXP, SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_eulerian_cycle(SEXP); +extern SEXP R_igraph_eulerian_path(SEXP); +extern SEXP R_igraph_extended_chordal_ring(SEXP, SEXP, SEXP); +extern SEXP R_igraph_famous(SEXP); +extern SEXP R_igraph_feedback_arc_set(SEXP, SEXP, SEXP); +extern SEXP R_igraph_forest_fire_game(SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_from_prufer(SEXP); +extern SEXP R_igraph_full(SEXP, SEXP, SEXP); +extern SEXP R_igraph_full_bipartite(SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_full_citation(SEXP, SEXP); +extern SEXP R_igraph_get_adjacency(SEXP, SEXP, SEXP); +extern SEXP R_igraph_get_all_shortest_paths(SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_get_all_shortest_paths_dijkstra(SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_get_all_simple_paths(SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_get_edgelist(SEXP, SEXP); +extern SEXP R_igraph_get_incidence(SEXP, SEXP); +extern SEXP R_igraph_get_isomorphisms_vf2(SEXP, SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_get_stochastic(SEXP, SEXP); +extern SEXP R_igraph_get_stochastic_sparsemat(SEXP, SEXP); +extern SEXP R_igraph_get_subisomorphisms_vf2(SEXP, SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_global_efficiency(SEXP, SEXP, SEXP); +extern SEXP R_igraph_graphlets(SEXP, SEXP, SEXP); +extern SEXP R_igraph_graphlets_candidate_basis(SEXP, SEXP); +extern SEXP R_igraph_growing_random_game(SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_harmonic_centrality_cutoff(SEXP, SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_has_loop(SEXP); +extern SEXP R_igraph_has_multiple(SEXP); +extern SEXP R_igraph_hrg_consensus(SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_hrg_create(SEXP, SEXP); +extern SEXP R_igraph_hrg_dendrogram(SEXP); +extern SEXP R_igraph_hrg_fit(SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_hrg_game(SEXP); +extern SEXP R_igraph_hrg_predict(SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_hsbm_game(SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_hsbm_list_game(SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_hub_score(SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_incidence(SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_incident(SEXP, SEXP, SEXP); +extern SEXP R_igraph_independence_number(SEXP); +extern SEXP R_igraph_independent_vertex_sets(SEXP, SEXP, SEXP); +extern SEXP R_igraph_induced_subgraph(SEXP, SEXP, SEXP); +extern SEXP R_igraph_intersection(SEXP, SEXP); +extern SEXP R_igraph_is_bipartite(SEXP); +extern SEXP R_igraph_is_connected(SEXP, SEXP); +extern SEXP R_igraph_is_dag(SEXP); +extern SEXP R_igraph_is_directed(SEXP); +extern SEXP R_igraph_is_eulerian(SEXP); +extern SEXP R_igraph_is_graphical(SEXP, SEXP, SEXP); +extern SEXP R_igraph_is_loop(SEXP, SEXP); +extern SEXP R_igraph_is_matching(SEXP, SEXP, SEXP); +extern SEXP R_igraph_is_maximal_matching(SEXP, SEXP, SEXP); +extern SEXP R_igraph_is_minimal_separator(SEXP, SEXP); +extern SEXP R_igraph_is_multiple(SEXP, SEXP); +extern SEXP R_igraph_is_mutual(SEXP, SEXP); +extern SEXP R_igraph_is_separator(SEXP, SEXP); +extern SEXP R_igraph_is_simple(SEXP); +extern SEXP R_igraph_is_tree(SEXP, SEXP); +extern SEXP R_igraph_isoclass(SEXP); +extern SEXP R_igraph_isoclass_create(SEXP, SEXP, SEXP); +extern SEXP R_igraph_isoclass_subgraph(SEXP, SEXP); +extern SEXP R_igraph_isomorphic(SEXP, SEXP); +extern SEXP R_igraph_isomorphic_34(SEXP, SEXP); +extern SEXP R_igraph_isomorphic_bliss(SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_isomorphic_vf2(SEXP, SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_k_regular_game(SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_kautz(SEXP, SEXP); +extern SEXP R_igraph_laplacian_spectral_embedding(SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_largest_cliques(SEXP); +extern SEXP R_igraph_largest_independent_vertex_sets(SEXP); +extern SEXP R_igraph_largest_weighted_cliques(SEXP, SEXP); +extern SEXP R_igraph_lastcit_game(SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_lattice(SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_layout_bipartite(SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_layout_circle(SEXP, SEXP); +extern SEXP R_igraph_layout_davidson_harel(SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_layout_drl(SEXP, SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_layout_drl_3d(SEXP, SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_layout_gem(SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_layout_grid(SEXP, SEXP); +extern SEXP R_igraph_layout_grid_3d(SEXP, SEXP, SEXP); +extern SEXP R_igraph_layout_lgl(SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_layout_mds(SEXP, SEXP, SEXP); +extern SEXP R_igraph_layout_merge_dla(SEXP, SEXP); +extern SEXP R_igraph_layout_random(SEXP); +extern SEXP R_igraph_layout_random_3d(SEXP); +extern SEXP R_igraph_layout_sphere(SEXP); +extern SEXP R_igraph_layout_star(SEXP, SEXP, SEXP); +extern SEXP R_igraph_layout_sugiyama(SEXP, SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_lcf_vector(SEXP, SEXP, SEXP); +extern SEXP R_igraph_linegraph(SEXP); +extern SEXP R_igraph_list_triangles(SEXP); +extern SEXP R_igraph_local_efficiency(SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_local_scan_0(SEXP, SEXP, SEXP); +extern SEXP R_igraph_local_scan_0_them(SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_local_scan_1_ecount(SEXP, SEXP, SEXP); +extern SEXP R_igraph_local_scan_1_ecount_them(SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_local_scan_k_ecount(SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_local_scan_k_ecount_them(SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_local_scan_neighborhood_ecount(SEXP, SEXP, SEXP); +extern SEXP R_igraph_maxflow(SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_maximal_cliques_hist(SEXP, SEXP, SEXP); +extern SEXP R_igraph_maximal_independent_vertex_sets(SEXP); +extern SEXP R_igraph_maximum_bipartite_matching(SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_maximum_cardinality_search(SEXP); +extern SEXP R_igraph_mincut(SEXP, SEXP); +extern SEXP R_igraph_mincut_value(SEXP, SEXP); +extern SEXP R_igraph_minimum_size_separators(SEXP); +extern SEXP R_igraph_minimum_spanning_tree_prim(SEXP, SEXP); +extern SEXP R_igraph_minimum_spanning_tree_unweighted(SEXP); +extern SEXP R_igraph_modularity(SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_modularity_matrix(SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_motifs_randesu(SEXP, SEXP, SEXP); +extern SEXP R_igraph_motifs_randesu_estimate(SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_motifs_randesu_no(SEXP, SEXP, SEXP); +extern SEXP R_igraph_neighborhood_graphs(SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_neighbors(SEXP, SEXP, SEXP); +extern SEXP R_igraph_path_length_hist(SEXP, SEXP); +extern SEXP R_igraph_permute_vertices(SEXP, SEXP); +extern SEXP R_igraph_personalized_pagerank(SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_power_law_fit(SEXP, SEXP, SEXP); +extern SEXP R_igraph_preference_game(SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_radius(SEXP, SEXP); +extern SEXP R_igraph_random_edge_walk(SEXP, SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_random_sample(SEXP, SEXP, SEXP); +extern SEXP R_igraph_random_spanning_tree(SEXP, SEXP); +extern SEXP R_igraph_random_walk(SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_read_graph_dimacs(SEXP, SEXP); +extern SEXP R_igraph_read_graph_dl(SEXP, SEXP); +extern SEXP R_igraph_read_graph_edgelist(SEXP, SEXP, SEXP); +extern SEXP R_igraph_read_graph_gml(SEXP); +extern SEXP R_igraph_read_graph_graphdb(SEXP, SEXP); +extern SEXP R_igraph_read_graph_graphml(SEXP, SEXP); +extern SEXP R_igraph_read_graph_lgl(SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_read_graph_ncol(SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_read_graph_pajek(SEXP); +extern SEXP R_igraph_realize_degree_sequence(SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_recent_degree_aging_game(SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_recent_degree_game(SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_reciprocity(SEXP, SEXP, SEXP); +extern SEXP R_igraph_rewire(SEXP, SEXP, SEXP); +extern SEXP R_igraph_rewire_directed_edges(SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_rewire_edges(SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_ring(SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_running_mean(SEXP, SEXP); +extern SEXP R_igraph_sample_dirichlet(SEXP, SEXP); +extern SEXP R_igraph_sample_sphere_surface(SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_sample_sphere_volume(SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_sbm_game(SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_scg_grouping(SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_scg_norm_eps(SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_similarity_dice(SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_similarity_inverse_log_weighted(SEXP, SEXP, SEXP); +extern SEXP R_igraph_similarity_jaccard(SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_simple_interconnected_islands_game(SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_simplify(SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_simplify_and_colorize(SEXP); +extern SEXP R_igraph_sir(SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_split_join_distance(SEXP, SEXP); +extern SEXP R_igraph_st_edge_connectivity(SEXP, SEXP, SEXP); +extern SEXP R_igraph_st_mincut(SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_st_mincut_value(SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_star(SEXP, SEXP, SEXP); +extern SEXP R_igraph_static_fitness_game(SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_static_power_law_game(SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_strength(SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_subcomponent(SEXP, SEXP, SEXP); +extern SEXP R_igraph_subgraph_edges(SEXP, SEXP, SEXP); +extern SEXP R_igraph_subisomorphic_vf2(SEXP, SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_to_directed(SEXP, SEXP); +extern SEXP R_igraph_to_prufer(SEXP); +extern SEXP R_igraph_to_undirected(SEXP, SEXP, SEXP); +extern SEXP R_igraph_topological_sorting(SEXP, SEXP); +extern SEXP R_igraph_transitivity_avglocal_undirected(SEXP, SEXP); +extern SEXP R_igraph_transitivity_barrat(SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_transitivity_local_undirected(SEXP, SEXP, SEXP); +extern SEXP R_igraph_transitivity_undirected(SEXP, SEXP); +extern SEXP R_igraph_tree(SEXP, SEXP, SEXP); +extern SEXP R_igraph_tree_game(SEXP, SEXP, SEXP); +extern SEXP R_igraph_triad_census(SEXP); +extern SEXP R_igraph_unfold_tree(SEXP, SEXP, SEXP); +extern SEXP R_igraph_union(SEXP, SEXP); +extern SEXP R_igraph_vcount(SEXP); +extern SEXP R_igraph_vertex_coloring_greedy(SEXP, SEXP); +extern SEXP R_igraph_vertex_connectivity(SEXP, SEXP); +extern SEXP R_igraph_vertex_disjoint_paths(SEXP, SEXP, SEXP); +extern SEXP R_igraph_watts_strogatz_game(SEXP, SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_weighted_adjacency(SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_weighted_clique_number(SEXP, SEXP); +extern SEXP R_igraph_weighted_cliques(SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_write_graph_dimacs(SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_write_graph_dot(SEXP, SEXP); +extern SEXP R_igraph_write_graph_edgelist(SEXP, SEXP); +extern SEXP R_igraph_write_graph_gml(SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_write_graph_graphml(SEXP, SEXP, SEXP); +extern SEXP R_igraph_write_graph_leda(SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_write_graph_lgl(SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_write_graph_ncol(SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_write_graph_pajek(SEXP, SEXP); + +static const R_CMethodDef CEntries[] = { + {"igraphhcass2", (DL_FUNC) &igraphhcass2, 6}, + {NULL, NULL, 0} +}; + +static const R_CallMethodDef CallEntries[] = { + {"make_lazy", (DL_FUNC) &make_lazy, 3}, + {"make_lazy_dots", (DL_FUNC) &make_lazy_dots, 2}, + {"promise_env_", (DL_FUNC) &promise_env_, 1}, + {"promise_expr_", (DL_FUNC) &promise_expr_, 1}, + {"UUID_gen", (DL_FUNC) &UUID_gen, 1}, + + /* non-autogenerated entries */ + {"R_igraph_add_edges", (DL_FUNC) &R_igraph_add_edges, 2}, + {"R_igraph_add_env", (DL_FUNC) &R_igraph_add_env, 1}, + {"R_igraph_add_version_to_env", (DL_FUNC) &R_igraph_add_version_to_env, 1}, + {"R_igraph_add_vertices", (DL_FUNC) &R_igraph_add_vertices, 2}, + {"R_igraph_address", (DL_FUNC) &R_igraph_address, 1}, + {"R_igraph_adjacent_vertices", (DL_FUNC) &R_igraph_adjacent_vertices, 3}, + {"R_igraph_arpack", (DL_FUNC) &R_igraph_arpack, 5}, + {"R_igraph_bfs", (DL_FUNC) &R_igraph_bfs, 15}, + {"R_igraph_bipartite_projection", (DL_FUNC) &R_igraph_bipartite_projection, 4}, + {"R_igraph_community_edge_betweenness", (DL_FUNC) &R_igraph_community_edge_betweenness, 8}, + {"R_igraph_community_fastgreedy", (DL_FUNC) &R_igraph_community_fastgreedy, 5}, + {"R_igraph_community_to_membership2", (DL_FUNC) &R_igraph_community_to_membership2, 3}, + {"R_igraph_community_leading_eigenvector", (DL_FUNC) &R_igraph_community_leading_eigenvector, 9}, + {"R_igraph_compose", (DL_FUNC) &R_igraph_compose, 3}, + {"R_igraph_dfs", (DL_FUNC) &R_igraph_dfs, 12}, + {"R_igraph_diameter", (DL_FUNC) &R_igraph_diameter, 4}, + {"R_igraph_disjoint_union", (DL_FUNC) &R_igraph_disjoint_union, 1}, + {"R_igraph_erdos_renyi_game", (DL_FUNC) &R_igraph_erdos_renyi_game, 5}, + {"R_igraph_es_adj", (DL_FUNC) &R_igraph_es_adj, 4}, + {"R_igraph_es_pairs", (DL_FUNC) &R_igraph_es_pairs, 3}, + {"R_igraph_es_path", (DL_FUNC) &R_igraph_es_path, 3}, + {"R_igraph_farthest_points", (DL_FUNC) &R_igraph_farthest_points, 4}, + {"R_igraph_finalizer", (DL_FUNC) &R_igraph_finalizer, 0}, + {"R_igraph_get_adjedgelist", (DL_FUNC) &R_igraph_get_adjedgelist, 2}, + {"R_igraph_get_adjlist", (DL_FUNC) &R_igraph_get_adjlist, 2}, + {"R_igraph_get_all_simple_paths_pp", (DL_FUNC) &R_igraph_get_all_simple_paths_pp, 1}, + {"R_igraph_get_attr_mode", (DL_FUNC) &R_igraph_get_attr_mode, 2}, + {"R_igraph_get_diameter", (DL_FUNC) &R_igraph_get_diameter, 4}, + {"R_igraph_get_edge", (DL_FUNC) &R_igraph_get_edge, 2}, + {"R_igraph_get_eids", (DL_FUNC) &R_igraph_get_eids, 5}, + {"R_igraph_get_graph_id", (DL_FUNC) &R_igraph_get_graph_id, 1}, + {"R_igraph_get_shortest_paths", (DL_FUNC) &R_igraph_get_shortest_paths, 10}, + {"R_igraph_getsphere", (DL_FUNC) &R_igraph_getsphere, 8}, + {"R_igraph_girth", (DL_FUNC) &R_igraph_girth, 2}, + {"R_igraph_graph_adjacency", (DL_FUNC) &R_igraph_graph_adjacency, 2}, + {"R_igraph_graph_version", (DL_FUNC) &R_igraph_graph_version, 1}, + {"R_igraph_graphlets_project", (DL_FUNC) &R_igraph_graphlets_project, 5}, + {"R_igraph_grg_game", (DL_FUNC) &R_igraph_grg_game, 4}, + {"R_igraph_i_levc_arp", (DL_FUNC) &R_igraph_i_levc_arp, 3}, + {"R_igraph_identical_graphs", (DL_FUNC) &R_igraph_identical_graphs, 3}, + {"R_igraph_incident_edges", (DL_FUNC) &R_igraph_incident_edges, 3}, + {"R_igraph_is_chordal", (DL_FUNC) &R_igraph_is_chordal, 5}, + {"R_igraph_laplacian", (DL_FUNC) &R_igraph_laplacian, 4}, + {"R_igraph_layout_fruchterman_reingold", (DL_FUNC) &R_igraph_layout_fruchterman_reingold, 10}, + {"R_igraph_layout_fruchterman_reingold_3d", (DL_FUNC) &R_igraph_layout_fruchterman_reingold_3d, 11}, + {"R_igraph_layout_graphopt", (DL_FUNC) &R_igraph_layout_graphopt, 8}, + {"R_igraph_layout_kamada_kawai", (DL_FUNC) &R_igraph_layout_kamada_kawai, 10}, + {"R_igraph_layout_kamada_kawai_3d", (DL_FUNC) &R_igraph_layout_kamada_kawai_3d, 12}, + {"R_igraph_layout_reingold_tilford", (DL_FUNC) &R_igraph_layout_reingold_tilford, 5}, + {"R_igraph_make_weak_ref", (DL_FUNC) &R_igraph_make_weak_ref, 3}, + {"R_igraph_maximal_cliques", (DL_FUNC) &R_igraph_maximal_cliques, 4}, + {"R_igraph_maximal_cliques_count", (DL_FUNC) &R_igraph_maximal_cliques_count, 4}, + {"R_igraph_maximal_cliques_file", (DL_FUNC) &R_igraph_maximal_cliques_file, 5}, + {"R_igraph_mybracket", (DL_FUNC) &R_igraph_mybracket, 2}, + {"R_igraph_mybracket2", (DL_FUNC) &R_igraph_mybracket2, 3}, + {"R_igraph_mybracket2_copy", (DL_FUNC) &R_igraph_mybracket2_copy, 3}, + {"R_igraph_mybracket2_names", (DL_FUNC) &R_igraph_mybracket2_names, 3}, + {"R_igraph_mybracket2_set", (DL_FUNC) &R_igraph_mybracket2_set, 4}, + {"R_igraph_mybracket3_set", (DL_FUNC) &R_igraph_mybracket3_set, 5}, + {"R_igraph_neighborhood", (DL_FUNC) &R_igraph_neighborhood, 5}, + {"R_igraph_neighborhood_size", (DL_FUNC) &R_igraph_neighborhood_size, 5}, + {"R_igraph_no_clusters", (DL_FUNC) &R_igraph_no_clusters, 2}, + {"R_igraph_psumtree_draw", (DL_FUNC) &R_igraph_psumtree_draw, 3}, + {"R_igraph_scg_adjacency", (DL_FUNC) &R_igraph_scg_adjacency, 14}, + {"R_igraph_scg_laplacian", (DL_FUNC) &R_igraph_scg_laplacian, 16}, + {"R_igraph_scg_semiprojectors", (DL_FUNC) &R_igraph_scg_semiprojectors, 5}, + {"R_igraph_scg_stochastic", (DL_FUNC) &R_igraph_scg_stochastic, 17}, + {"R_igraph_set_verbose", (DL_FUNC) &R_igraph_set_verbose, 1}, + {"R_igraph_shortest_paths", (DL_FUNC) &R_igraph_shortest_paths, 6}, + {"R_igraph_solve_lsap", (DL_FUNC) &R_igraph_solve_lsap, 2}, + {"R_igraph_spinglass_community", (DL_FUNC) &R_igraph_spinglass_community, 11}, + {"R_igraph_spinglass_my_community", (DL_FUNC) &R_igraph_spinglass_my_community, 6}, + {"R_igraph_st_vertex_connectivity", (DL_FUNC) &R_igraph_st_vertex_connectivity, 3}, + {"R_igraph_subisomorphic_lad", (DL_FUNC) &R_igraph_subisomorphic_lad, 7}, + {"R_igraph_transitivity_local_undirected_all", (DL_FUNC) &R_igraph_transitivity_local_undirected_all, 2}, + {"R_igraph_version", (DL_FUNC) &R_igraph_version, 0}, + {"R_igraph_vs_adj", (DL_FUNC) &R_igraph_vs_adj, 4}, + {"R_igraph_vs_nei", (DL_FUNC) &R_igraph_vs_nei, 4}, + {"R_igraph_walktrap_community", (DL_FUNC) &R_igraph_walktrap_community, 6}, + {"R_igraph_weak_ref_key", (DL_FUNC) &R_igraph_weak_ref_key, 1}, + {"R_igraph_weak_ref_run_finalizer", (DL_FUNC) &R_igraph_weak_ref_run_finalizer, 1}, + {"R_igraph_weak_ref_value", (DL_FUNC) &R_igraph_weak_ref_value, 1}, + + /* autogenerated entries */ + {"R_igraph_adhesion", (DL_FUNC) &R_igraph_adhesion, 2}, + {"R_igraph_adjacency_spectral_embedding", (DL_FUNC) &R_igraph_adjacency_spectral_embedding, 7}, + {"R_igraph_adjacent_triangles", (DL_FUNC) &R_igraph_adjacent_triangles, 2}, + {"R_igraph_adjlist", (DL_FUNC) &R_igraph_adjlist, 3}, + {"R_igraph_all_minimal_st_separators", (DL_FUNC) &R_igraph_all_minimal_st_separators, 1}, + {"R_igraph_all_st_cuts", (DL_FUNC) &R_igraph_all_st_cuts, 3}, + {"R_igraph_all_st_mincuts", (DL_FUNC) &R_igraph_all_st_mincuts, 4}, + {"R_igraph_are_connected", (DL_FUNC) &R_igraph_are_connected, 3}, + {"R_igraph_arpack_unpack_complex", (DL_FUNC) &R_igraph_arpack_unpack_complex, 3}, + {"R_igraph_articulation_points", (DL_FUNC) &R_igraph_articulation_points, 1}, + {"R_igraph_assortativity", (DL_FUNC) &R_igraph_assortativity, 4}, + {"R_igraph_assortativity_degree", (DL_FUNC) &R_igraph_assortativity_degree, 2}, + {"R_igraph_assortativity_nominal", (DL_FUNC) &R_igraph_assortativity_nominal, 3}, + {"R_igraph_asymmetric_preference_game", (DL_FUNC) &R_igraph_asymmetric_preference_game, 6}, + {"R_igraph_atlas", (DL_FUNC) &R_igraph_atlas, 1}, + {"R_igraph_authority_score", (DL_FUNC) &R_igraph_authority_score, 4}, + {"R_igraph_automorphism_group", (DL_FUNC) &R_igraph_automorphism_group, 3}, + {"R_igraph_automorphisms", (DL_FUNC) &R_igraph_automorphisms, 3}, + {"R_igraph_average_local_efficiency", (DL_FUNC) &R_igraph_average_local_efficiency, 4}, + {"R_igraph_average_path_length_dijkstra", (DL_FUNC) &R_igraph_average_path_length_dijkstra, 4}, + {"R_igraph_avg_nearest_neighbor_degree", (DL_FUNC) &R_igraph_avg_nearest_neighbor_degree, 5}, + {"R_igraph_barabasi_aging_game", (DL_FUNC) &R_igraph_barabasi_aging_game, 12}, + {"R_igraph_barabasi_game", (DL_FUNC) &R_igraph_barabasi_game, 9}, + {"R_igraph_betweenness", (DL_FUNC) &R_igraph_betweenness, 4}, + {"R_igraph_betweenness_cutoff", (DL_FUNC) &R_igraph_betweenness_cutoff, 5}, + {"R_igraph_bibcoupling", (DL_FUNC) &R_igraph_bibcoupling, 2}, + {"R_igraph_biconnected_components", (DL_FUNC) &R_igraph_biconnected_components, 1}, + {"R_igraph_bipartite_game_gnm", (DL_FUNC) &R_igraph_bipartite_game_gnm, 5}, + {"R_igraph_bipartite_game_gnp", (DL_FUNC) &R_igraph_bipartite_game_gnp, 5}, + {"R_igraph_bipartite_projection_size", (DL_FUNC) &R_igraph_bipartite_projection_size, 2}, + {"R_igraph_bridges", (DL_FUNC) &R_igraph_bridges, 1}, + {"R_igraph_callaway_traits_game", (DL_FUNC) &R_igraph_callaway_traits_game, 6}, + {"R_igraph_canonical_permutation", (DL_FUNC) &R_igraph_canonical_permutation, 3}, + {"R_igraph_centralization", (DL_FUNC) &R_igraph_centralization, 3}, + {"R_igraph_centralization_betweenness", (DL_FUNC) &R_igraph_centralization_betweenness, 3}, + {"R_igraph_centralization_betweenness_tmax", (DL_FUNC) &R_igraph_centralization_betweenness_tmax, 3}, + {"R_igraph_centralization_closeness", (DL_FUNC) &R_igraph_centralization_closeness, 3}, + {"R_igraph_centralization_closeness_tmax", (DL_FUNC) &R_igraph_centralization_closeness_tmax, 3}, + {"R_igraph_centralization_degree", (DL_FUNC) &R_igraph_centralization_degree, 4}, + {"R_igraph_centralization_degree_tmax", (DL_FUNC) &R_igraph_centralization_degree_tmax, 4}, + {"R_igraph_centralization_eigenvector_centrality", (DL_FUNC) &R_igraph_centralization_eigenvector_centrality, 5}, + {"R_igraph_centralization_eigenvector_centrality_tmax", (DL_FUNC) &R_igraph_centralization_eigenvector_centrality_tmax, 4}, + {"R_igraph_cited_type_game", (DL_FUNC) &R_igraph_cited_type_game, 5}, + {"R_igraph_citing_cited_type_game", (DL_FUNC) &R_igraph_citing_cited_type_game, 5}, + {"R_igraph_clique_number", (DL_FUNC) &R_igraph_clique_number, 1}, + {"R_igraph_clique_size_hist", (DL_FUNC) &R_igraph_clique_size_hist, 3}, + {"R_igraph_cliques", (DL_FUNC) &R_igraph_cliques, 3}, + {"R_igraph_closeness", (DL_FUNC) &R_igraph_closeness, 5}, + {"R_igraph_closeness_cutoff", (DL_FUNC) &R_igraph_closeness_cutoff, 6}, + {"R_igraph_clusters", (DL_FUNC) &R_igraph_clusters, 2}, + {"R_igraph_cocitation", (DL_FUNC) &R_igraph_cocitation, 2}, + {"R_igraph_cohesion", (DL_FUNC) &R_igraph_cohesion, 2}, + {"R_igraph_cohesive_blocks", (DL_FUNC) &R_igraph_cohesive_blocks, 1}, + {"R_igraph_community_fluid_communities", (DL_FUNC) &R_igraph_community_fluid_communities, 2}, + {"R_igraph_community_infomap", (DL_FUNC) &R_igraph_community_infomap, 4}, + {"R_igraph_community_label_propagation", (DL_FUNC) &R_igraph_community_label_propagation, 4}, + {"R_igraph_community_leiden", (DL_FUNC) &R_igraph_community_leiden, 7}, + {"R_igraph_community_multilevel", (DL_FUNC) &R_igraph_community_multilevel, 3}, + {"R_igraph_community_optimal_modularity", (DL_FUNC) &R_igraph_community_optimal_modularity, 2}, + {"R_igraph_compare_communities", (DL_FUNC) &R_igraph_compare_communities, 3}, + {"R_igraph_complementer", (DL_FUNC) &R_igraph_complementer, 2}, + {"R_igraph_connect_neighborhood", (DL_FUNC) &R_igraph_connect_neighborhood, 3}, + {"R_igraph_constraint", (DL_FUNC) &R_igraph_constraint, 3}, + {"R_igraph_contract_vertices", (DL_FUNC) &R_igraph_contract_vertices, 3}, + {"R_igraph_convex_hull", (DL_FUNC) &R_igraph_convex_hull, 1}, + {"R_igraph_coreness", (DL_FUNC) &R_igraph_coreness, 2}, + {"R_igraph_correlated_game", (DL_FUNC) &R_igraph_correlated_game, 4}, + {"R_igraph_correlated_pair_game", (DL_FUNC) &R_igraph_correlated_pair_game, 5}, + {"R_igraph_count_isomorphisms_vf2", (DL_FUNC) &R_igraph_count_isomorphisms_vf2, 6}, + {"R_igraph_count_multiple", (DL_FUNC) &R_igraph_count_multiple, 2}, + {"R_igraph_count_subisomorphisms_vf2", (DL_FUNC) &R_igraph_count_subisomorphisms_vf2, 6}, + {"R_igraph_create", (DL_FUNC) &R_igraph_create, 3}, + {"R_igraph_create_bipartite", (DL_FUNC) &R_igraph_create_bipartite, 3}, + {"R_igraph_de_bruijn", (DL_FUNC) &R_igraph_de_bruijn, 2}, + {"R_igraph_decompose", (DL_FUNC) &R_igraph_decompose, 4}, + {"R_igraph_degree", (DL_FUNC) &R_igraph_degree, 4}, + {"R_igraph_degree_sequence_game", (DL_FUNC) &R_igraph_degree_sequence_game, 3}, + {"R_igraph_delete_edges", (DL_FUNC) &R_igraph_delete_edges, 2}, + {"R_igraph_delete_vertices", (DL_FUNC) &R_igraph_delete_vertices, 2}, + {"R_igraph_density", (DL_FUNC) &R_igraph_density, 2}, + {"R_igraph_difference", (DL_FUNC) &R_igraph_difference, 2}, + {"R_igraph_dim_select", (DL_FUNC) &R_igraph_dim_select, 1}, + {"R_igraph_diversity", (DL_FUNC) &R_igraph_diversity, 3}, + {"R_igraph_dominator_tree", (DL_FUNC) &R_igraph_dominator_tree, 3}, + {"R_igraph_dot_product_game", (DL_FUNC) &R_igraph_dot_product_game, 2}, + {"R_igraph_dyad_census", (DL_FUNC) &R_igraph_dyad_census, 1}, + {"R_igraph_eccentricity", (DL_FUNC) &R_igraph_eccentricity, 3}, + {"R_igraph_ecount", (DL_FUNC) &R_igraph_ecount, 1}, + {"R_igraph_edge_betweenness", (DL_FUNC) &R_igraph_edge_betweenness, 3}, + {"R_igraph_edge_betweenness_cutoff", (DL_FUNC) &R_igraph_edge_betweenness_cutoff, 4}, + {"R_igraph_edge_connectivity", (DL_FUNC) &R_igraph_edge_connectivity, 2}, + {"R_igraph_edge_disjoint_paths", (DL_FUNC) &R_igraph_edge_disjoint_paths, 3}, + {"R_igraph_edges", (DL_FUNC) &R_igraph_edges, 2}, + {"R_igraph_eigen_adjacency", (DL_FUNC) &R_igraph_eigen_adjacency, 4}, + {"R_igraph_eigenvector_centrality", (DL_FUNC) &R_igraph_eigenvector_centrality, 5}, + {"R_igraph_empty", (DL_FUNC) &R_igraph_empty, 2}, + {"R_igraph_establishment_game", (DL_FUNC) &R_igraph_establishment_game, 6}, + {"R_igraph_eulerian_cycle", (DL_FUNC) &R_igraph_eulerian_cycle, 1}, + {"R_igraph_eulerian_path", (DL_FUNC) &R_igraph_eulerian_path, 1}, + {"R_igraph_extended_chordal_ring", (DL_FUNC) &R_igraph_extended_chordal_ring, 3}, + {"R_igraph_famous", (DL_FUNC) &R_igraph_famous, 1}, + {"R_igraph_feedback_arc_set", (DL_FUNC) &R_igraph_feedback_arc_set, 3}, + {"R_igraph_forest_fire_game", (DL_FUNC) &R_igraph_forest_fire_game, 5}, + {"R_igraph_from_prufer", (DL_FUNC) &R_igraph_from_prufer, 1}, + {"R_igraph_full", (DL_FUNC) &R_igraph_full, 3}, + {"R_igraph_full_bipartite", (DL_FUNC) &R_igraph_full_bipartite, 4}, + {"R_igraph_full_citation", (DL_FUNC) &R_igraph_full_citation, 2}, + {"R_igraph_get_adjacency", (DL_FUNC) &R_igraph_get_adjacency, 3}, + {"R_igraph_get_all_shortest_paths", (DL_FUNC) &R_igraph_get_all_shortest_paths, 4}, + {"R_igraph_get_all_shortest_paths_dijkstra", (DL_FUNC) &R_igraph_get_all_shortest_paths_dijkstra, 5}, + {"R_igraph_get_all_simple_paths", (DL_FUNC) &R_igraph_get_all_simple_paths, 5}, + {"R_igraph_get_edgelist", (DL_FUNC) &R_igraph_get_edgelist, 2}, + {"R_igraph_get_incidence", (DL_FUNC) &R_igraph_get_incidence, 2}, + {"R_igraph_get_isomorphisms_vf2", (DL_FUNC) &R_igraph_get_isomorphisms_vf2, 6}, + {"R_igraph_get_stochastic", (DL_FUNC) &R_igraph_get_stochastic, 2}, + {"R_igraph_get_stochastic_sparsemat", (DL_FUNC) &R_igraph_get_stochastic_sparsemat, 2}, + {"R_igraph_get_subisomorphisms_vf2", (DL_FUNC) &R_igraph_get_subisomorphisms_vf2, 6}, + {"R_igraph_global_efficiency", (DL_FUNC) &R_igraph_global_efficiency, 3}, + {"R_igraph_graphlets", (DL_FUNC) &R_igraph_graphlets, 3}, + {"R_igraph_graphlets_candidate_basis", (DL_FUNC) &R_igraph_graphlets_candidate_basis, 2}, + {"R_igraph_growing_random_game", (DL_FUNC) &R_igraph_growing_random_game, 4}, + {"R_igraph_harmonic_centrality_cutoff", (DL_FUNC) &R_igraph_harmonic_centrality_cutoff, 6}, + {"R_igraph_has_loop", (DL_FUNC) &R_igraph_has_loop, 1}, + {"R_igraph_has_multiple", (DL_FUNC) &R_igraph_has_multiple, 1}, + {"R_igraph_hrg_consensus", (DL_FUNC) &R_igraph_hrg_consensus, 4}, + {"R_igraph_hrg_create", (DL_FUNC) &R_igraph_hrg_create, 2}, + {"R_igraph_hrg_dendrogram", (DL_FUNC) &R_igraph_hrg_dendrogram, 1}, + {"R_igraph_hrg_fit", (DL_FUNC) &R_igraph_hrg_fit, 4}, + {"R_igraph_hrg_game", (DL_FUNC) &R_igraph_hrg_game, 1}, + {"R_igraph_hrg_predict", (DL_FUNC) &R_igraph_hrg_predict, 5}, + {"R_igraph_hsbm_game", (DL_FUNC) &R_igraph_hsbm_game, 5}, + {"R_igraph_hsbm_list_game", (DL_FUNC) &R_igraph_hsbm_list_game, 5}, + {"R_igraph_hub_score", (DL_FUNC) &R_igraph_hub_score, 4}, + {"R_igraph_incidence", (DL_FUNC) &R_igraph_incidence, 4}, + {"R_igraph_incident", (DL_FUNC) &R_igraph_incident, 3}, + {"R_igraph_independence_number", (DL_FUNC) &R_igraph_independence_number, 1}, + {"R_igraph_independent_vertex_sets", (DL_FUNC) &R_igraph_independent_vertex_sets, 3}, + {"R_igraph_induced_subgraph", (DL_FUNC) &R_igraph_induced_subgraph, 3}, + {"R_igraph_intersection", (DL_FUNC) &R_igraph_intersection, 2}, + {"R_igraph_is_bipartite", (DL_FUNC) &R_igraph_is_bipartite, 1}, + {"R_igraph_is_connected", (DL_FUNC) &R_igraph_is_connected, 2}, + {"R_igraph_is_dag", (DL_FUNC) &R_igraph_is_dag, 1}, + {"R_igraph_is_directed", (DL_FUNC) &R_igraph_is_directed, 1}, + {"R_igraph_is_eulerian", (DL_FUNC) &R_igraph_is_eulerian, 1}, + {"R_igraph_is_graphical", (DL_FUNC) &R_igraph_is_graphical, 3}, + {"R_igraph_is_loop", (DL_FUNC) &R_igraph_is_loop, 2}, + {"R_igraph_is_matching", (DL_FUNC) &R_igraph_is_matching, 3}, + {"R_igraph_is_maximal_matching", (DL_FUNC) &R_igraph_is_maximal_matching, 3}, + {"R_igraph_is_minimal_separator", (DL_FUNC) &R_igraph_is_minimal_separator, 2}, + {"R_igraph_is_multiple", (DL_FUNC) &R_igraph_is_multiple, 2}, + {"R_igraph_is_mutual", (DL_FUNC) &R_igraph_is_mutual, 2}, + {"R_igraph_is_separator", (DL_FUNC) &R_igraph_is_separator, 2}, + {"R_igraph_is_simple", (DL_FUNC) &R_igraph_is_simple, 1}, + {"R_igraph_is_tree", (DL_FUNC) &R_igraph_is_tree, 2}, + {"R_igraph_isoclass", (DL_FUNC) &R_igraph_isoclass, 1}, + {"R_igraph_isoclass_create", (DL_FUNC) &R_igraph_isoclass_create, 3}, + {"R_igraph_isoclass_subgraph", (DL_FUNC) &R_igraph_isoclass_subgraph, 2}, + {"R_igraph_isomorphic", (DL_FUNC) &R_igraph_isomorphic, 2}, + {"R_igraph_isomorphic_34", (DL_FUNC) &R_igraph_isomorphic_34, 2}, + {"R_igraph_isomorphic_bliss", (DL_FUNC) &R_igraph_isomorphic_bliss, 5}, + {"R_igraph_isomorphic_vf2", (DL_FUNC) &R_igraph_isomorphic_vf2, 6}, + {"R_igraph_k_regular_game", (DL_FUNC) &R_igraph_k_regular_game, 4}, + {"R_igraph_kautz", (DL_FUNC) &R_igraph_kautz, 2}, + {"R_igraph_laplacian_spectral_embedding", (DL_FUNC) &R_igraph_laplacian_spectral_embedding, 7}, + {"R_igraph_largest_cliques", (DL_FUNC) &R_igraph_largest_cliques, 1}, + {"R_igraph_largest_independent_vertex_sets", (DL_FUNC) &R_igraph_largest_independent_vertex_sets, 1}, + {"R_igraph_largest_weighted_cliques", (DL_FUNC) &R_igraph_largest_weighted_cliques, 2}, + {"R_igraph_lastcit_game", (DL_FUNC) &R_igraph_lastcit_game, 5}, + {"R_igraph_lattice", (DL_FUNC) &R_igraph_lattice, 5}, + {"R_igraph_layout_bipartite", (DL_FUNC) &R_igraph_layout_bipartite, 5}, + {"R_igraph_layout_circle", (DL_FUNC) &R_igraph_layout_circle, 2}, + {"R_igraph_layout_davidson_harel", (DL_FUNC) &R_igraph_layout_davidson_harel, 11}, + {"R_igraph_layout_drl", (DL_FUNC) &R_igraph_layout_drl, 6}, + {"R_igraph_layout_drl_3d", (DL_FUNC) &R_igraph_layout_drl_3d, 6}, + {"R_igraph_layout_gem", (DL_FUNC) &R_igraph_layout_gem, 7}, + {"R_igraph_layout_grid", (DL_FUNC) &R_igraph_layout_grid, 2}, + {"R_igraph_layout_grid_3d", (DL_FUNC) &R_igraph_layout_grid_3d, 3}, + {"R_igraph_layout_lgl", (DL_FUNC) &R_igraph_layout_lgl, 8}, + {"R_igraph_layout_mds", (DL_FUNC) &R_igraph_layout_mds, 3}, + {"R_igraph_layout_merge_dla", (DL_FUNC) &R_igraph_layout_merge_dla, 2}, + {"R_igraph_layout_random", (DL_FUNC) &R_igraph_layout_random, 1}, + {"R_igraph_layout_random_3d", (DL_FUNC) &R_igraph_layout_random_3d, 1}, + {"R_igraph_layout_sphere", (DL_FUNC) &R_igraph_layout_sphere, 1}, + {"R_igraph_layout_star", (DL_FUNC) &R_igraph_layout_star, 3}, + {"R_igraph_layout_sugiyama", (DL_FUNC) &R_igraph_layout_sugiyama, 6}, + {"R_igraph_lcf_vector", (DL_FUNC) &R_igraph_lcf_vector, 3}, + {"R_igraph_linegraph", (DL_FUNC) &R_igraph_linegraph, 1}, + {"R_igraph_list_triangles", (DL_FUNC) &R_igraph_list_triangles, 1}, + {"R_igraph_local_efficiency", (DL_FUNC) &R_igraph_local_efficiency, 5}, + {"R_igraph_local_scan_0", (DL_FUNC) &R_igraph_local_scan_0, 3}, + {"R_igraph_local_scan_0_them", (DL_FUNC) &R_igraph_local_scan_0_them, 4}, + {"R_igraph_local_scan_1_ecount", (DL_FUNC) &R_igraph_local_scan_1_ecount, 3}, + {"R_igraph_local_scan_1_ecount_them", (DL_FUNC) &R_igraph_local_scan_1_ecount_them, 4}, + {"R_igraph_local_scan_k_ecount", (DL_FUNC) &R_igraph_local_scan_k_ecount, 4}, + {"R_igraph_local_scan_k_ecount_them", (DL_FUNC) &R_igraph_local_scan_k_ecount_them, 5}, + {"R_igraph_local_scan_neighborhood_ecount", (DL_FUNC) &R_igraph_local_scan_neighborhood_ecount, 3}, + {"R_igraph_maxflow", (DL_FUNC) &R_igraph_maxflow, 4}, + {"R_igraph_maximal_cliques_hist", (DL_FUNC) &R_igraph_maximal_cliques_hist, 3}, + {"R_igraph_maximal_independent_vertex_sets", (DL_FUNC) &R_igraph_maximal_independent_vertex_sets, 1}, + {"R_igraph_maximum_bipartite_matching", (DL_FUNC) &R_igraph_maximum_bipartite_matching, 4}, + {"R_igraph_maximum_cardinality_search", (DL_FUNC) &R_igraph_maximum_cardinality_search, 1}, + {"R_igraph_mincut", (DL_FUNC) &R_igraph_mincut, 2}, + {"R_igraph_mincut_value", (DL_FUNC) &R_igraph_mincut_value, 2}, + {"R_igraph_minimum_size_separators", (DL_FUNC) &R_igraph_minimum_size_separators, 1}, + {"R_igraph_minimum_spanning_tree_prim", (DL_FUNC) &R_igraph_minimum_spanning_tree_prim, 2}, + {"R_igraph_minimum_spanning_tree_unweighted", (DL_FUNC) &R_igraph_minimum_spanning_tree_unweighted, 1}, + {"R_igraph_modularity", (DL_FUNC) &R_igraph_modularity, 5}, + {"R_igraph_modularity_matrix", (DL_FUNC) &R_igraph_modularity_matrix, 4}, + {"R_igraph_motifs_randesu", (DL_FUNC) &R_igraph_motifs_randesu, 3}, + {"R_igraph_motifs_randesu_estimate", (DL_FUNC) &R_igraph_motifs_randesu_estimate, 5}, + {"R_igraph_motifs_randesu_no", (DL_FUNC) &R_igraph_motifs_randesu_no, 3}, + {"R_igraph_neighborhood_graphs", (DL_FUNC) &R_igraph_neighborhood_graphs, 5}, + {"R_igraph_neighbors", (DL_FUNC) &R_igraph_neighbors, 3}, + {"R_igraph_path_length_hist", (DL_FUNC) &R_igraph_path_length_hist, 2}, + {"R_igraph_permute_vertices", (DL_FUNC) &R_igraph_permute_vertices, 2}, + {"R_igraph_personalized_pagerank", (DL_FUNC) &R_igraph_personalized_pagerank, 8}, + {"R_igraph_power_law_fit", (DL_FUNC) &R_igraph_power_law_fit, 3}, + {"R_igraph_preference_game", (DL_FUNC) &R_igraph_preference_game, 7}, + {"R_igraph_radius", (DL_FUNC) &R_igraph_radius, 2}, + {"R_igraph_random_edge_walk", (DL_FUNC) &R_igraph_random_edge_walk, 6}, + {"R_igraph_random_sample", (DL_FUNC) &R_igraph_random_sample, 3}, + {"R_igraph_random_spanning_tree", (DL_FUNC) &R_igraph_random_spanning_tree, 2}, + {"R_igraph_random_walk", (DL_FUNC) &R_igraph_random_walk, 5}, + {"R_igraph_read_graph_dimacs", (DL_FUNC) &R_igraph_read_graph_dimacs, 2}, + {"R_igraph_read_graph_dl", (DL_FUNC) &R_igraph_read_graph_dl, 2}, + {"R_igraph_read_graph_edgelist", (DL_FUNC) &R_igraph_read_graph_edgelist, 3}, + {"R_igraph_read_graph_gml", (DL_FUNC) &R_igraph_read_graph_gml, 1}, + {"R_igraph_read_graph_graphdb", (DL_FUNC) &R_igraph_read_graph_graphdb, 2}, + {"R_igraph_read_graph_graphml", (DL_FUNC) &R_igraph_read_graph_graphml, 2}, + {"R_igraph_read_graph_lgl", (DL_FUNC) &R_igraph_read_graph_lgl, 4}, + {"R_igraph_read_graph_ncol", (DL_FUNC) &R_igraph_read_graph_ncol, 5}, + {"R_igraph_read_graph_pajek", (DL_FUNC) &R_igraph_read_graph_pajek, 1}, + {"R_igraph_realize_degree_sequence", (DL_FUNC) &R_igraph_realize_degree_sequence, 4}, + {"R_igraph_recent_degree_aging_game", (DL_FUNC) &R_igraph_recent_degree_aging_game, 10}, + {"R_igraph_recent_degree_game", (DL_FUNC) &R_igraph_recent_degree_game, 8}, + {"R_igraph_reciprocity", (DL_FUNC) &R_igraph_reciprocity, 3}, + {"R_igraph_rewire", (DL_FUNC) &R_igraph_rewire, 3}, + {"R_igraph_rewire_directed_edges", (DL_FUNC) &R_igraph_rewire_directed_edges, 4}, + {"R_igraph_rewire_edges", (DL_FUNC) &R_igraph_rewire_edges, 4}, + {"R_igraph_ring", (DL_FUNC) &R_igraph_ring, 4}, + {"R_igraph_running_mean", (DL_FUNC) &R_igraph_running_mean, 2}, + {"R_igraph_sample_dirichlet", (DL_FUNC) &R_igraph_sample_dirichlet, 2}, + {"R_igraph_sample_sphere_surface", (DL_FUNC) &R_igraph_sample_sphere_surface, 4}, + {"R_igraph_sample_sphere_volume", (DL_FUNC) &R_igraph_sample_sphere_volume, 4}, + {"R_igraph_sbm_game", (DL_FUNC) &R_igraph_sbm_game, 5}, + {"R_igraph_scg_grouping", (DL_FUNC) &R_igraph_scg_grouping, 7}, + {"R_igraph_scg_norm_eps", (DL_FUNC) &R_igraph_scg_norm_eps, 5}, + {"R_igraph_similarity_dice", (DL_FUNC) &R_igraph_similarity_dice, 4}, + {"R_igraph_similarity_inverse_log_weighted", (DL_FUNC) &R_igraph_similarity_inverse_log_weighted, 3}, + {"R_igraph_similarity_jaccard", (DL_FUNC) &R_igraph_similarity_jaccard, 4}, + {"R_igraph_simple_interconnected_islands_game", (DL_FUNC) &R_igraph_simple_interconnected_islands_game, 4}, + {"R_igraph_simplify", (DL_FUNC) &R_igraph_simplify, 4}, + {"R_igraph_simplify_and_colorize", (DL_FUNC) &R_igraph_simplify_and_colorize, 1}, + {"R_igraph_sir", (DL_FUNC) &R_igraph_sir, 4}, + {"R_igraph_split_join_distance", (DL_FUNC) &R_igraph_split_join_distance, 2}, + {"R_igraph_st_edge_connectivity", (DL_FUNC) &R_igraph_st_edge_connectivity, 3}, + {"R_igraph_st_mincut", (DL_FUNC) &R_igraph_st_mincut, 4}, + {"R_igraph_st_mincut_value", (DL_FUNC) &R_igraph_st_mincut_value, 4}, + {"R_igraph_star", (DL_FUNC) &R_igraph_star, 3}, + {"R_igraph_static_fitness_game", (DL_FUNC) &R_igraph_static_fitness_game, 5}, + {"R_igraph_static_power_law_game", (DL_FUNC) &R_igraph_static_power_law_game, 7}, + {"R_igraph_strength", (DL_FUNC) &R_igraph_strength, 5}, + {"R_igraph_subcomponent", (DL_FUNC) &R_igraph_subcomponent, 3}, + {"R_igraph_subgraph_edges", (DL_FUNC) &R_igraph_subgraph_edges, 3}, + {"R_igraph_subisomorphic_vf2", (DL_FUNC) &R_igraph_subisomorphic_vf2, 6}, + {"R_igraph_to_directed", (DL_FUNC) &R_igraph_to_directed, 2}, + {"R_igraph_to_prufer", (DL_FUNC) &R_igraph_to_prufer, 1}, + {"R_igraph_to_undirected", (DL_FUNC) &R_igraph_to_undirected, 3}, + {"R_igraph_topological_sorting", (DL_FUNC) &R_igraph_topological_sorting, 2}, + {"R_igraph_transitivity_avglocal_undirected", (DL_FUNC) &R_igraph_transitivity_avglocal_undirected, 2}, + {"R_igraph_transitivity_barrat", (DL_FUNC) &R_igraph_transitivity_barrat, 4}, + {"R_igraph_transitivity_local_undirected", (DL_FUNC) &R_igraph_transitivity_local_undirected, 3}, + {"R_igraph_transitivity_undirected", (DL_FUNC) &R_igraph_transitivity_undirected, 2}, + {"R_igraph_tree", (DL_FUNC) &R_igraph_tree, 3}, + {"R_igraph_tree_game", (DL_FUNC) &R_igraph_tree_game, 3}, + {"R_igraph_triad_census", (DL_FUNC) &R_igraph_triad_census, 1}, + {"R_igraph_unfold_tree", (DL_FUNC) &R_igraph_unfold_tree, 3}, + {"R_igraph_union", (DL_FUNC) &R_igraph_union, 2}, + {"R_igraph_vcount", (DL_FUNC) &R_igraph_vcount, 1}, + {"R_igraph_vertex_coloring_greedy", (DL_FUNC) &R_igraph_vertex_coloring_greedy, 2}, + {"R_igraph_vertex_connectivity", (DL_FUNC) &R_igraph_vertex_connectivity, 2}, + {"R_igraph_vertex_disjoint_paths", (DL_FUNC) &R_igraph_vertex_disjoint_paths, 3}, + {"R_igraph_watts_strogatz_game", (DL_FUNC) &R_igraph_watts_strogatz_game, 6}, + {"R_igraph_weighted_adjacency", (DL_FUNC) &R_igraph_weighted_adjacency, 4}, + {"R_igraph_weighted_clique_number", (DL_FUNC) &R_igraph_weighted_clique_number, 2}, + {"R_igraph_weighted_cliques", (DL_FUNC) &R_igraph_weighted_cliques, 5}, + {"R_igraph_write_graph_dimacs", (DL_FUNC) &R_igraph_write_graph_dimacs, 5}, + {"R_igraph_write_graph_dot", (DL_FUNC) &R_igraph_write_graph_dot, 2}, + {"R_igraph_write_graph_edgelist", (DL_FUNC) &R_igraph_write_graph_edgelist, 2}, + {"R_igraph_write_graph_gml", (DL_FUNC) &R_igraph_write_graph_gml, 4}, + {"R_igraph_write_graph_graphml", (DL_FUNC) &R_igraph_write_graph_graphml, 3}, + {"R_igraph_write_graph_leda", (DL_FUNC) &R_igraph_write_graph_leda, 4}, + {"R_igraph_write_graph_lgl", (DL_FUNC) &R_igraph_write_graph_lgl, 5}, + {"R_igraph_write_graph_ncol", (DL_FUNC) &R_igraph_write_graph_ncol, 4}, + {"R_igraph_write_graph_pajek", (DL_FUNC) &R_igraph_write_graph_pajek, 2}, + + {NULL, NULL, 0} +}; diff --git a/src/rigraph/lazyeval.c b/src/rigraph/lazyeval.c new file mode 100644 index 0000000..dbc1f4c --- /dev/null +++ b/src/rigraph/lazyeval.c @@ -0,0 +1,172 @@ +#include +#include + +SEXP promise_as_lazy(SEXP promise, SEXP env, int follow_symbols) { + // recurse until we find the real promise, not a promise of a promise + // never go past the global environment + while(TYPEOF(promise) == PROMSXP && env != R_GlobalEnv) { + + env = PRENV(promise); + promise = PREXPR(promise); + + // If the promise is threaded through multiple functions, we'll + // get some symbols along the way. If the symbol is bound to a promise + // keep going on up + if (follow_symbols && TYPEOF(promise) == SYMSXP) { + SEXP obj = findVar(promise, env); + if (TYPEOF(obj) == PROMSXP) { + promise = obj; + } + } + } + + // Make named list for output + SEXP lazy = PROTECT(allocVector(VECSXP, 2)); + SET_VECTOR_ELT(lazy, 0, promise); + SET_VECTOR_ELT(lazy, 1, env); + + SEXP names = PROTECT(allocVector(STRSXP, 2)); + SET_STRING_ELT(names, 0, mkChar("expr")); + SET_STRING_ELT(names, 1, mkChar("env")); + + setAttrib(lazy, install("names"), names); + setAttrib(lazy, install("class"), PROTECT(mkString("lazy"))); + + UNPROTECT(3); + + return lazy; +} + +SEXP make_lazy(SEXP name, SEXP env, SEXP follow_symbols_) { + SEXP promise = PROTECT(findVar(name, env)); + int follow_symbols = asLogical(follow_symbols_); + SEXP ret = promise_as_lazy(promise, env, follow_symbols); + UNPROTECT(1); + return ret; +} + +SEXP make_lazy_dots(SEXP env, SEXP follow_symbols_) { + SEXP dots = PROTECT(findVar(install("..."), env)); + int follow_symbols = asLogical(follow_symbols_); + + // Figure out how many elements in dots + int n = 0; + for(SEXP nxt = dots; nxt != R_NilValue; nxt = CDR(nxt)) { + n++; + } + + // Allocate list to store results + SEXP lazy_dots = PROTECT(allocVector(VECSXP, n)); + SEXP names = PROTECT(allocVector(STRSXP, n)); + + // Iterate through all elements of dots, converting promises into lazy exprs + int i = 0; + SEXP nxt = dots; + while(nxt != R_NilValue) { + SEXP promise = CAR(nxt); + + SEXP lazy = promise_as_lazy(promise, env, follow_symbols); + SET_VECTOR_ELT(lazy_dots, i, lazy); + if (TAG(nxt) != R_NilValue) + SET_STRING_ELT(names, i, PRINTNAME(TAG(nxt))); + + nxt = CDR(nxt); + i++; + } + setAttrib(lazy_dots, install("names"), names); + setAttrib(lazy_dots, install("class"), PROTECT(mkString("lazy_dots"))); + + UNPROTECT(4); + + return lazy_dots; +} +#include +#include + +/* For now, replace with pure R alternative ------------------------------------ + +// This is a bit naughty, but there's no other way to create a promise +SEXP Rf_mkPROMISE(SEXP, SEXP); +SEXP Rf_installTrChar(SEXP); + +SEXP lazy_to_promise(SEXP x) { + // arg is a list of length 2 - LANGSXP/SYMSXP, followed by ENVSXP + return Rf_mkPROMISE(VECTOR_ELT(x, 0), VECTOR_ELT(x, 1)); +} + +SEXP eval_call_(SEXP fun, SEXP dots, SEXP env) { + if (TYPEOF(fun) != SYMSXP && TYPEOF(fun) != LANGSXP) { + error("fun must be a call or a symbol"); + } + if (TYPEOF(dots) != VECSXP) { + error("dots must be a list"); + } + if (!inherits(dots, "lazy_dots")) { + error("dots must be of class lazy_dots"); + } + if (TYPEOF(env) != ENVSXP) { + error("env must be an environment"); + } + + int n = length(dots); + if (n == 0) { + return LCONS(fun, R_NilValue); + } + + SEXP names = GET_NAMES(dots); + + SEXP args = R_NilValue; + for (int i = n - 1; i >= 0; --i) { + SEXP dot = VECTOR_ELT(dots, i); + SEXP prom = lazy_to_promise(dot); + args = PROTECT(CONS(prom, args)); + if (names != R_NilValue) { + SEXP name = STRING_ELT(names, i); + if (strlen(CHAR(name)) > 0) + SET_TAG(args, Rf_installTrChar(name)); + } + } + UNPROTECT(n); + + SEXP call = LCONS(fun, args); + + return eval(call, env); +} + +*/ +#include +#include + +/* Fails on Linux -------------------------------------------------------------- + +SEXP Rf_mkPROMISE(SEXP, SEXP); + +SEXP promise_(SEXP expr, SEXP env) { + if (TYPEOF(expr) != SYMSXP && TYPEOF(expr) != LANGSXP) { + error("expr must be a call or a symbol"); + } + if (TYPEOF(env) != ENVSXP) { + error("env must be an environment"); + } + + return Rf_mkPROMISE(expr, env); +} + +*/ + +SEXP promise_expr_(SEXP prom) { + if (TYPEOF(prom) != PROMSXP) { + error("prom must be a promise"); + } + + return PREXPR(prom); +} + +SEXP promise_env_(SEXP prom) { + if (TYPEOF(prom) != PROMSXP) { + error("prom must be a promise"); + } + + return PRENV(prom); +} + diff --git a/src/rigraph/rinterface.c b/src/rigraph/rinterface.c new file mode 100644 index 0000000..12c2ae1 --- /dev/null +++ b/src/rigraph/rinterface.c @@ -0,0 +1,16754 @@ +/* -*- mode: C -*- */ +/* + IGraph library R interface. + Copyright (C) 2005-2012 Gabor Csardi + 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.h" +#include "igraph_error.h" + +#include "config.h" + +#include +#include +#include +#include + +#include "rinterface.h" +#include "rrandom.h" + +#include "init.c" /* registration table */ + +#include + +void igraph_free(void *p); + +SEXP R_igraph_vector_to_SEXP(const igraph_vector_t *v); +SEXP R_igraph_vector_int_to_SEXP(const igraph_vector_int_t *v); +SEXP R_igraph_vector_int_to_SEXPp1(const igraph_vector_int_t *v); +SEXP R_igraph_vector_bool_to_SEXP(const igraph_vector_bool_t *v); +SEXP R_igraph_vector_long_to_SEXP(const igraph_vector_long_t *v); +SEXP R_igraph_vector_complex_to_SEXP(const igraph_vector_complex_t* v); +SEXP R_igraph_0orvector_to_SEXP(const igraph_vector_t *v); +SEXP R_igraph_0orvector_bool_to_SEXP(const igraph_vector_bool_t *v); +SEXP R_igraph_0orvector_long_to_SEXP(const igraph_vector_long_t *v); +SEXP R_igraph_0orvector_complex_to_SEXP(const igraph_vector_complex_t *v); +SEXP R_igraph_matrix_to_SEXP(const igraph_matrix_t *m); +SEXP R_igraph_matrix_complex_to_SEXP(const igraph_matrix_complex_t *m); +SEXP R_igraph_0ormatrix_complex_to_SEXP(const igraph_matrix_complex_t *m); +SEXP R_igraph_strvector_to_SEXP(const igraph_strvector_t *m); +SEXP R_igraph_to_SEXP(const igraph_t *graph); +SEXP R_igraph_vectorlist_to_SEXP(const igraph_vector_ptr_t *ptr); +SEXP R_igraph_vectorlist_int_to_SEXP(const igraph_vector_ptr_t *ptr); +void R_igraph_vectorlist_int_destroy(igraph_vector_ptr_t *ptr); +SEXP R_igraph_0orvectorlist_to_SEXP(const igraph_vector_ptr_t *ptr); +void R_igraph_vectorlist_destroy(igraph_vector_ptr_t *ptr); +SEXP R_igraph_matrixlist_to_SEXP(const igraph_vector_ptr_t *ptr); +void R_igraph_matrixlist_destroy(igraph_vector_ptr_t *ptr); +SEXP R_igraph_graphlist_to_SEXP(const igraph_vector_ptr_t *ptr); +void R_igraph_graphlist_destroy(igraph_vector_ptr_t *ptr); +SEXP R_igraph_hrg_to_SEXP(const igraph_hrg_t *hrg); +SEXP R_igraph_plfit_result_to_SEXP(const igraph_plfit_result_t *plfit); +SEXP R_igraph_sparsemat_to_SEXP(const igraph_sparsemat_t *sp); +SEXP R_igraph_0orsparsemat_to_SEXP(const igraph_sparsemat_t *sp); +SEXP R_igraph_maxflow_stats_to_SEXP(const igraph_maxflow_stats_t *st); +SEXP R_igraph_sirlist_to_SEXP(const igraph_vector_ptr_t *sl); +void R_igraph_sirlist_destroy(igraph_vector_ptr_t *sl); + +int R_igraph_SEXP_to_strvector(SEXP rval, igraph_strvector_t *sv); +int R_igraph_SEXP_to_strvector_copy(SEXP rval, igraph_strvector_t *sv); +int R_SEXP_to_vector(SEXP sv, igraph_vector_t *v); +int R_SEXP_to_vector_copy(SEXP sv, igraph_vector_t *v); +int R_SEXP_to_matrix(SEXP pakl, igraph_matrix_t *akl); +int R_SEXP_to_matrix_complex(SEXP pakl, igraph_matrix_complex_t *akl); +int R_SEXP_to_igraph_matrix_copy(SEXP pakl, igraph_matrix_t *akl); +int R_SEXP_to_igraph(SEXP graph, igraph_t *res); +int R_SEXP_to_igraph_copy(SEXP graph, igraph_t *res); +int R_SEXP_to_igraph_vs(SEXP rit, igraph_t *graph, igraph_vs_t *it); +int R_SEXP_to_igraph_es(SEXP rit, igraph_t *graph, igraph_es_t *it); +int R_SEXP_to_igraph_adjlist(SEXP vectorlist, igraph_adjlist_t *ptr); +int R_igraph_SEXP_to_0orvectorlist(SEXP vectorlist, + igraph_vector_ptr_t *ptr); +int R_igraph_SEXP_to_vectorlist(SEXP vectorlist, igraph_vector_ptr_t *ptr); +int R_igraph_SEXP_to_vectorlist_int(SEXP vectorlist, + igraph_vector_ptr_t *ptr); +int R_igraph_SEXP_to_matrixlist(SEXP matrixlist, igraph_vector_ptr_t *ptr); +int R_SEXP_to_vector_bool(SEXP sv, igraph_vector_bool_t *v); +int R_SEXP_to_vector_bool_copy(SEXP sv, igraph_vector_bool_t *v); +int R_SEXP_to_vector_int(SEXP sv, igraph_vector_int_t *v); +int R_SEXP_to_vector_long_copy(SEXP sv, igraph_vector_long_t *v); +int R_SEXP_to_hrg(SEXP shrg, igraph_hrg_t *hrg); +int R_SEXP_to_hrg_copy(SEXP shrg, igraph_hrg_t *hrg); +int R_SEXP_to_sparsemat(SEXP pakl, igraph_sparsemat_t *akl); + +SEXP R_igraph_i_lang7(SEXP s, SEXP t, SEXP u, SEXP v, SEXP w, SEXP x, SEXP y) +{ + PROTECT(s); + PROTECT(t); + PROTECT(u); + s = LCONS(s, LCONS(t, LCONS(u, list4(v, w, x, y)))); + UNPROTECT(3); + return s; +} + +/* get the list element named str, or return NULL */ +/* from the R Manual */ + +SEXP R_igraph_getListElement(SEXP list, const char *str) +{ + SEXP elmt = R_NilValue, names = getAttrib(list, R_NamesSymbol); + int i; + + for (i = 0; i < length(list); i++) + if(strcmp(CHAR(STRING_ELT(names, i)), str) == 0) { + elmt = VECTOR_ELT(list, i); + break; + } + return elmt; +} + +SEXP R_igraph_c2(SEXP x1, SEXP x2) { + SEXP cc = PROTECT(install("c")); + SEXP lc = PROTECT(lang3(cc, x1, x2)); + SEXP ret = EVAL(lc); + UNPROTECT(2); + return ret; +} + +/* evaluate an expression in a tryCatch() block to ensure that errors do not + * longjmp() back to the top level. Adapted from include/Rcpp/api/meat/Rcpp_eval.h + * in the Rcpp project */ + +typedef enum { + SAFEEVAL_OK = 0, + SAFEEVAL_ERROR = 1, + SAFEEVAL_INTERRUPTION = 2 +} R_igraph_safe_eval_result_t; + +R_igraph_safe_eval_result_t R_igraph_safe_eval_classify_result(SEXP result) { + if (Rf_inherits(result, "condition")) { + if (Rf_inherits(result, "error")) { + return SAFEEVAL_ERROR; + } else if (Rf_inherits(result, "interrupt")) { + return SAFEEVAL_INTERRUPTION; + } + } + + return SAFEEVAL_OK; +} + +SEXP R_igraph_safe_eval(SEXP expr_call, R_igraph_safe_eval_result_t* result) { + /* find `identity` function used to capture errors */ + SEXP identity = PROTECT(Rf_install("identity")); + SEXP identity_func = PROTECT(Rf_findFun(identity, R_BaseNamespace)); + if (identity_func == R_UnboundValue) { + Rf_error("Failed to find 'base::identity()'"); + } + + /* define the call -- enclose with `tryCatch` so we can record errors */ + SEXP try_catch = PROTECT(Rf_install("tryCatch")); + SEXP try_catch_call = PROTECT(Rf_lang4(try_catch, expr_call, identity_func, identity_func)); + + SET_TAG(CDDR(try_catch_call), Rf_install("error")); + SET_TAG(CDDR(CDR(try_catch_call)), Rf_install("interrupt")); + + /* execute the call */ + SEXP retval = PROTECT(Rf_eval(try_catch_call, R_GlobalEnv)); + + /* did we get an error or an interrupt? */ + if (result) { + *result = R_igraph_safe_eval_classify_result(retval); + } + + UNPROTECT(5); + + return retval; +} + +SEXP R_igraph_handle_safe_eval_result(SEXP result) { + switch (R_igraph_safe_eval_classify_result(result)) { + case SAFEEVAL_OK: + return result; + + case SAFEEVAL_ERROR: + /* extract the error message, call IGRAPH_FINALLY_FREE() and then throw + * the error. We cannot raise the error directly because that would + * longjmp() and could potentially overwrite stack-allocated data structures + * that are also in the "finally" stack */ + IGRAPH_FINALLY_FREE(); + + SEXP condition_message = PROTECT(Rf_install("conditionMessage")); + SEXP condition_message_call = PROTECT(Rf_lang2(condition_message, result)); + SEXP evaluated_condition_message = PROTECT(Rf_eval(condition_message_call, R_GlobalEnv)); + error(CHAR(STRING_ELT(evaluated_condition_message, 0))); + UNPROTECT(3); + return R_NilValue; + + case SAFEEVAL_INTERRUPTION: + IGRAPH_FINALLY_FREE(); + error("Interrupted by user"); + return R_NilValue; + + default: + error( + "Invalid object type returned from R_igraph_safe_eval(). This is a " + "bug; please report it to the developers." + ); + return R_NilValue; + } +} + +/****************************************************** + * Attributes * + *****************************************************/ + +SEXP R_igraph_get_attr_mode(SEXP graph, SEXP pwhich) { + int which=INTEGER(pwhich)[0]-1; + SEXP obj=VECTOR_ELT(VECTOR_ELT(graph, 8), which); + int i, len=GET_LENGTH(obj); + SEXP result; + + PROTECT(result=NEW_CHARACTER(len)); + for (i=0; iattr=result; + + /* Add graph attributes */ + attrno= attr==NULL ? 0 : igraph_vector_ptr_size(attr); + SET_VECTOR_ELT(result, 1, NEW_LIST(attrno)); + gal=VECTOR_ELT(result, 1); + PROTECT(names=NEW_CHARACTER(attrno)); px++; + for (i=0; iname)); + SET_VECTOR_ELT(gal, i, R_NilValue); + switch (rec->type) { + case IGRAPH_ATTRIBUTE_NUMERIC: + vec=(igraph_vector_t*) rec->value; + if (igraph_vector_size(vec) > 0) { + SET_VECTOR_ELT(gal, i, NEW_NUMERIC(1)); + REAL(VECTOR_ELT(gal, i))[0]=VECTOR(*vec)[0]; + } + break; + case IGRAPH_ATTRIBUTE_BOOLEAN: + log=(igraph_vector_bool_t*) rec->value; + if (igraph_vector_bool_size(log) > 0) { + SET_VECTOR_ELT(gal, i, NEW_LOGICAL(1)); + LOGICAL(VECTOR_ELT(gal, i))[0]=VECTOR(*log)[0]; + } + break; + case IGRAPH_ATTRIBUTE_STRING: + strvec=(igraph_strvector_t*) rec->value; + if (igraph_strvector_size(strvec) > 0) { + SET_VECTOR_ELT(gal, i, NEW_CHARACTER(1)); + SET_STRING_ELT(VECTOR_ELT(gal,i), 0, mkChar(STR(*strvec, 0))); + } + break; + case IGRAPH_ATTRIBUTE_R_OBJECT: + UNPROTECT(px); + IGRAPH_ERROR("R_objects not implemented yet", IGRAPH_UNIMPLEMENTED); + break; + case IGRAPH_ATTRIBUTE_DEFAULT: + case IGRAPH_ATTRIBUTE_PY_OBJECT: + default: + UNPROTECT(px); + IGRAPH_ERROR("Unknown attribute type, this should not happen", + IGRAPH_EINTERNAL); + break; + } + } + SET_NAMES(gal, names); + + UNPROTECT(px); + return 0; +} + +void R_igraph_attribute_destroy(igraph_t *graph) { + SEXP attr=graph->attr; + REAL(VECTOR_ELT(attr, 0))[1] -= 1; /* refcount for igraph_t */ + if (!R_igraph_attribute_protected && + REAL(VECTOR_ELT(attr, 0))[1]==0 && + REAL(VECTOR_ELT(attr, 0))[2]==1) { + R_ReleaseObject(attr); + } + graph->attr=0; +} + +/* If not copying all three attribute kinds are requested, then + we don't refcount, but really copy the requested ones, because + 1) we can only refcount all three at the same time, and + 2) the not-copied attributes will be set up by subsequent calls + to permute_vertices and/or permute/edges anyway. */ + +int R_igraph_attribute_copy(igraph_t *to, const igraph_t *from, + igraph_bool_t ga, igraph_bool_t va, igraph_bool_t ea) { + SEXP fromattr=from->attr; + if (ga && va && ea) { + to->attr=from->attr; + REAL(VECTOR_ELT(fromattr, 0))[1] += 1; /* refcount only */ + if (!R_igraph_attribute_protected && + REAL(VECTOR_ELT(fromattr, 0))[1] == 1) { + R_PreserveObject(to->attr); + } + } else { + R_igraph_attribute_init(to,0); /* Sets up many things */ + SEXP toattr=to->attr; + if (ga) { + SET_VECTOR_ELT(toattr, 1, duplicate(VECTOR_ELT(fromattr, 1))); + } + if (va) { + SET_VECTOR_ELT(toattr, 2, duplicate(VECTOR_ELT(fromattr, 2))); + } + if (ea) { + SET_VECTOR_ELT(toattr, 3, duplicate(VECTOR_ELT(fromattr, 3))); + } + } + return 0; +} + +SEXP R_igraph_attribute_add_vertices_append1(igraph_vector_ptr_t *nattr, + int j, int nv) { + + SEXP app = R_NilValue; + igraph_attribute_record_t *tmprec=VECTOR(*nattr)[j-1]; + long int len = 0; + + switch (tmprec->type) { + case IGRAPH_ATTRIBUTE_NUMERIC: + len = igraph_vector_size(tmprec->value); + break; + case IGRAPH_ATTRIBUTE_BOOLEAN: + len = igraph_vector_bool_size(tmprec->value); + break; + case IGRAPH_ATTRIBUTE_STRING: + len = igraph_strvector_size(tmprec->value); + break; + case IGRAPH_ATTRIBUTE_R_OBJECT: + igraph_error("R objects not implemented yet", __FILE__, __LINE__, + IGRAPH_UNIMPLEMENTED); + return R_NilValue; + break; + default: + igraph_error("Unknown attribute type, internal error", __FILE__, __LINE__, + IGRAPH_EINVAL); + return R_NilValue; + break; + } + + if (len != nv) { + igraph_error("Invalid attribute length", __FILE__, __LINE__, + IGRAPH_EINVAL); + return R_NilValue; + } + + switch (tmprec->type) { + case IGRAPH_ATTRIBUTE_NUMERIC: + PROTECT(app=NEW_NUMERIC(nv)); + igraph_vector_copy_to(tmprec->value, REAL(app)); + break; + case IGRAPH_ATTRIBUTE_BOOLEAN: + PROTECT(app=R_igraph_vector_bool_to_SEXP(tmprec->value)); + break; + default: /* IGRAPH_ATTRIBUTE_STRING */ + PROTECT(app=R_igraph_strvector_to_SEXP(tmprec->value)); + break; + } + + UNPROTECT(1); + return app; +} + +void R_igraph_attribute_add_vertices_append(SEXP val, long int nv, + igraph_vector_ptr_t *nattr) { + SEXP names; + long int valno, i, nattrno; + SEXP rep = R_NilValue; + int px = 0; + + valno=GET_LENGTH(val); + names=PROTECT(GET_NAMES(val)); px++; + if (nattr==NULL) { + nattrno=0; + } else { + nattrno=igraph_vector_ptr_size(nattr); + } + + for (i=0; iname); + } + if (l) { + /* This attribute is present in nattr */ + SEXP app = PROTECT(R_igraph_attribute_add_vertices_append1(nattr, j, nv)); + SEXP newva = PROTECT(R_igraph_c2(oldva, app)); + SET_VECTOR_ELT(val, i, newva); + UNPROTECT(2); + } else { + /* No such attribute, append NA's */ + if (isNull(rep)) { + SEXP l1 = PROTECT(install("rep")); px++; + SEXP l2 = PROTECT(ScalarLogical(NA_LOGICAL)); px++; + SEXP l3 = PROTECT(ScalarInteger((int) nv)); px++; + SEXP l4 = PROTECT(lang3(l1, l2, l3)); px++; + PROTECT(rep=EVAL(l4)); px++; + } + PROTECT(newva=R_igraph_c2(oldva, rep)); + SET_VECTOR_ELT(val, i, newva); + UNPROTECT(1); + } + } + + UNPROTECT(px); +} + +SEXP R_igraph_attribute_add_vertices_dup(SEXP attr) { + SEXP newattr=duplicate(attr); + int px = 0; + + if (R_igraph_attribute_protected) { + PROTECT(newattr); px++; + } else { + R_PreserveObject(newattr); + } + + REAL(VECTOR_ELT(attr, 0))[1] -= 1; + if (!R_igraph_attribute_protected && + REAL(VECTOR_ELT(attr, 0))[1] == 0) { + R_ReleaseObject(attr); + } + REAL(VECTOR_ELT(newattr, 0))[0] = 0; + REAL(VECTOR_ELT(newattr, 0))[1] = 1; + if (R_igraph_attribute_protected) { + long int pos, alen=LENGTH(VECTOR_ELT(attr, 0)); + if (alen == 4) { + pos=REAL(VECTOR_ELT(attr, 0))[3]; + SET_VECTOR_ELT(R_igraph_attribute_protected, pos, newattr); + } else { + SEXP tmp=PROTECT(NEW_NUMERIC(4)); px++; + REAL(tmp)[0] = REAL(VECTOR_ELT(attr, 0))[0]; + REAL(tmp)[1] = REAL(VECTOR_ELT(attr, 0))[1]; + REAL(tmp)[2] = REAL(VECTOR_ELT(attr, 0))[2]; + pos = REAL(tmp)[3] = R_igraph_attribute_protected_size; + R_igraph_attribute_protected_size += 1; + SET_VECTOR_ELT(newattr, 0, tmp); + } + SET_VECTOR_ELT(R_igraph_attribute_protected, pos, newattr); + } + + UNPROTECT(px); + return newattr; +} + +int R_igraph_attribute_add_vertices(igraph_t *graph, long int nv, + igraph_vector_ptr_t *nattr) { + SEXP attr=graph->attr; + SEXP val, rep=0, names, newnames; + igraph_vector_t news; + long int valno, i, origlen, nattrno, newattrs; + int px = 0; + + if (REAL(VECTOR_ELT(attr, 0))[0]+REAL(VECTOR_ELT(attr, 0))[1] > 1) { + SEXP newattr = PROTECT(R_igraph_attribute_add_vertices_dup(attr)); px++; + attr=graph->attr=newattr; + } + + val=VECTOR_ELT(attr, 2); + valno=GET_LENGTH(val); + names=PROTECT(GET_NAMES(val)); px++; + if (nattr==NULL) { + nattrno=0; + } else { + nattrno=igraph_vector_ptr_size(nattr); + } + origlen=igraph_vcount(graph)-nv; + + /* First add the new attributes, if any */ + newattrs=0; + if (igraph_vector_init(&news, 0)) error("Out of memory"); + IGRAPH_FINALLY(igraph_vector_destroy, &news); + for (i=0; iname; + long int j; + igraph_bool_t l=0; + for (j=0; !l && jname)); + } + PROTECT(newval=R_igraph_c2(val, app)); + PROTECT(newnames=R_igraph_c2(names, newnames)); + SET_NAMES(newval, newnames); + SET_VECTOR_ELT(attr, 2, newval); + val=VECTOR_ELT(attr, 2); + UNPROTECT(9); + } + igraph_vector_destroy(&news); + IGRAPH_FINALLY_CLEAN(1); /* news */ + + /* Now append the new values */ + R_igraph_attribute_add_vertices_append(val, nv, nattr); + + UNPROTECT(px); + + return 0; +} + +/* void R_igraph_attribute_delete_vertices(igraph_t *graph, */ +/* const igraph_vector_t *eidx, */ +/* const igraph_vector_t *vidx) { */ +/* SEXP attr=graph->attr; */ +/* SEXP eal, val; */ +/* long int valno, ealno, i; */ +/* if (REAL(VECTOR_ELT(attr, 0))[0]+REAL(VECTOR_ELT(attr, 0))[1] > 1) { */ +/* SEXP newattr; */ +/* PROTECT(newattr=duplicate(attr)); */ +/* REAL(VECTOR_ELT(attr, 0))[1] -= 1; */ +/* if (REAL(VECTOR_ELT(attr, 0))[1] == 0) { */ +/* R_ReleaseObject(attr); */ +/* } */ +/* REAL(VECTOR_ELT(newattr, 0))[0] = 0; */ +/* REAL(VECTOR_ELT(newattr, 0))[1] = 1; */ +/* attr=graph->attr=newattr; */ +/* } */ + +/* /\* Vertices *\/ */ +/* val=VECTOR_ELT(attr, 2); */ +/* valno=GET_LENGTH(val); */ +/* for (i=0; i 0) { */ +/* newlen++; */ +/* } */ +/* } */ +/* PROTECT(ss=NEW_NUMERIC(newlen)); */ +/* for (j=0; j0) { */ +/* REAL(ss)[(long int)VECTOR(*vidx)[j]-1]=j+1; */ +/* } */ +/* } */ +/* PROTECT(newva=EVAL(lang3(install("["), oldva, ss))); */ +/* SET_VECTOR_ELT(val, i, newva); */ +/* UNPROTECT(2); */ +/* } */ + +/* /\* Edges *\/ */ +/* eal=VECTOR_ELT(attr, 3); */ +/* ealno=GET_LENGTH(eal); */ +/* for (i=0; i 0) { */ +/* newlen++; */ +/* } */ +/* } */ +/* PROTECT(ss=NEW_NUMERIC(newlen)); */ +/* for (j=0; j0) { */ +/* REAL(ss)[(long int)VECTOR(*eidx)[j]-1]=j+1; */ +/* } */ +/* } */ +/* PROTECT(newea=EVAL(lang3(install("["), oldea, ss))); */ +/* SET_VECTOR_ELT(eal, i, newea); */ +/* UNPROTECT(2); */ +/* } */ +/* } */ + +int R_igraph_attribute_permute_vertices_same(const igraph_t *graph, + igraph_t *newgraph, + const igraph_vector_t *idx) { + SEXP attr=newgraph->attr; + SEXP val; + long int i, valno; + long int idxlen=igraph_vector_size(idx); + SEXP ss; + int px = 0; + + /* We copy if we need to */ + if (REAL(VECTOR_ELT(attr, 0))[0]+REAL(VECTOR_ELT(attr, 0))[1] > 1) { + SEXP newattr = duplicate(attr); + if (R_igraph_attribute_protected) { + PROTECT(newattr); px++; + } else { + R_PreserveObject(newattr); + } + REAL(VECTOR_ELT(attr, 0))[1] -= 1; + if (!R_igraph_attribute_protected && + REAL(VECTOR_ELT(attr, 0))[1] == 0) { + R_ReleaseObject(attr); + } + REAL(VECTOR_ELT(newattr, 0))[0] = 0; + REAL(VECTOR_ELT(newattr, 0))[1] = 1; + if (R_igraph_attribute_protected) { + long int pos, alen=LENGTH(VECTOR_ELT(attr, 0)); + if (alen == 4) { + pos=REAL(VECTOR_ELT(attr, 0))[3]; + SET_VECTOR_ELT(R_igraph_attribute_protected, pos, newattr); + } else { + SEXP tmp=PROTECT(NEW_NUMERIC(4)); px++; + REAL(tmp)[0] = REAL(VECTOR_ELT(attr, 0))[0]; + REAL(tmp)[1] = REAL(VECTOR_ELT(attr, 0))[1]; + REAL(tmp)[2] = REAL(VECTOR_ELT(attr, 0))[2]; + pos = REAL(tmp)[3] = R_igraph_attribute_protected_size; + R_igraph_attribute_protected_size += 1; + SET_VECTOR_ELT(newattr, 0, tmp); + } + SET_VECTOR_ELT(R_igraph_attribute_protected, pos, newattr); + } + attr=newgraph->attr=newattr; + } + + val=VECTOR_ELT(attr,2); + valno=GET_LENGTH(val); + + /* If we have no vertex attributes, then we don't need to do anything */ + if (valno==0) { UNPROTECT(px); return 0; } + + /* Convert idx to an R object, we will use this for indexing */ + PROTECT(ss=NEW_INTEGER(idxlen)); px++; + for (i=0; iattr; + SEXP toattr=newgraph->attr; + SEXP val, toval; + SEXP names; + long int i, valno; + long int idxlen=igraph_vector_size(idx); + SEXP ss; + int px = 0; + + val=VECTOR_ELT(attr,2); + valno=GET_LENGTH(val); + + /* If we have no vertex attributes, then we don't need to do anything */ + if (valno==0) { return 0; } + + /* Convert idx to an R object, we will use this for indexing */ + PROTECT(ss=NEW_INTEGER(idxlen)); px++; + for (i=0; itype) { + case IGRAPH_ATTRIBUTE_NUMERIC: + len = igraph_vector_size(tmprec->value); + break; + case IGRAPH_ATTRIBUTE_BOOLEAN: + len = igraph_vector_bool_size(tmprec->value); + break; + case IGRAPH_ATTRIBUTE_STRING: + len = igraph_strvector_size(tmprec->value); + break; + case IGRAPH_ATTRIBUTE_R_OBJECT: + igraph_error("R objects not implemented yet", __FILE__, __LINE__, + IGRAPH_UNIMPLEMENTED); + return R_NilValue; + break; + default: + igraph_error("Unknown attribute type, internal error", __FILE__, __LINE__, + IGRAPH_EINVAL); + return R_NilValue; + break; + } + + if (len != ne) { + igraph_error("Invalid attribute length", __FILE__, __LINE__, + IGRAPH_EINVAL); + return R_NilValue; + } + + switch (tmprec->type) { + case IGRAPH_ATTRIBUTE_NUMERIC: + PROTECT(app=NEW_NUMERIC(ne)); + igraph_vector_copy_to(tmprec->value, REAL(app)); + break; + case IGRAPH_ATTRIBUTE_BOOLEAN: + PROTECT(app=R_igraph_vector_bool_to_SEXP(tmprec->value)); + break; + default: /* IGRAPH_ATTRIBUTE_STRING */ + PROTECT(app=R_igraph_strvector_to_SEXP(tmprec->value)); + break; + } + + UNPROTECT(1); + return app; +} + +void R_igraph_attribute_add_edges_append(SEXP eal, + const igraph_vector_t *edges, + igraph_vector_ptr_t *nattr) { + SEXP names; + long int ealno, i; + long int ne=igraph_vector_size(edges)/2, nattrno; + SEXP rep = R_NilValue; + int px = 0; + + ealno=GET_LENGTH(eal); + names=PROTECT(GET_NAMES(eal)); px++; + if (nattr==NULL) { + nattrno=0; + } else { + nattrno=igraph_vector_ptr_size(nattr); + } + + for (i=0; iname); + } + if (l) { + /* This attribute is present in nattr */ + SEXP app = PROTECT(R_igraph_attribute_add_edges_append1(nattr, j, ne)); + SEXP newea = PROTECT(R_igraph_c2(oldea, app)); + SET_VECTOR_ELT(eal, i, newea); + UNPROTECT(2); + } else { + /* No such attribute, append NA's */ + if (isNull(rep)) { + SEXP l1 = PROTECT(install("rep")); px++; + SEXP l2 = PROTECT(ScalarLogical(NA_LOGICAL)); px++; + SEXP l3 = PROTECT(ScalarInteger((int) ne)); px++; + SEXP l4 = PROTECT(lang3(l1, l2, l3)); px++; + PROTECT(rep = EVAL(l4)); px++; + } + SEXP newea = PROTECT(R_igraph_c2(oldea, rep)); + SET_VECTOR_ELT(eal, i, newea); + UNPROTECT(1); + } + } + + UNPROTECT(px); +} + +int R_igraph_attribute_add_edges(igraph_t *graph, + const igraph_vector_t *edges, + igraph_vector_ptr_t *nattr) { + SEXP attr=graph->attr; + SEXP eal, names, newnames; + igraph_vector_t news; + long int ealno, i, origlen, nattrno, newattrs; + long int ne=igraph_vector_size(edges)/2; + int px = 0; + + if (igraph_vector_init(&news, 0)) error("Out of memory"); + IGRAPH_FINALLY(igraph_vector_destroy, &news); + + if (REAL(VECTOR_ELT(attr, 0))[0] + REAL(VECTOR_ELT(attr, 0))[1] > 1) { + SEXP newattr = PROTECT(R_igraph_attribute_add_edges_dup(attr)); px++; + attr=graph->attr=newattr; + } + + eal=VECTOR_ELT(attr, 3); + ealno=GET_LENGTH(eal); + names=PROTECT(GET_NAMES(eal)); px++; + if (nattr==NULL) { + nattrno=0; + } else { + nattrno=igraph_vector_ptr_size(nattr); + } + origlen=igraph_ecount(graph)-ne; + + /* First add the new attributes, if any */ + newattrs=0; + for (i=0; iname; + long int j; + igraph_bool_t l=0; + for (j=0; !l && jname)); + } + PROTECT(neweal=R_igraph_c2(eal, app)); + PROTECT(newnames=R_igraph_c2(names, newnames)); + SET_NAMES(neweal, newnames); + SET_VECTOR_ELT(attr, 3, neweal); + eal=VECTOR_ELT(attr, 3); + UNPROTECT(9); + } + igraph_vector_destroy(&news); + IGRAPH_FINALLY_CLEAN(1); + + /* Now append the new values */ + R_igraph_attribute_add_edges_append(eal, edges, nattr); + + UNPROTECT(px); + + return 0; +} + +/* void R_igraph_attribute_delete_edges(igraph_t *graph, */ +/* const igraph_vector_t *idx) { */ +/* SEXP attr=graph->attr; */ +/* SEXP eal; */ +/* long int ealno, i; */ +/* if (REAL(VECTOR_ELT(attr, 0))[0]+REAL(VECTOR_ELT(attr, 0))[1] > 1) { */ +/* SEXP newattr; */ +/* PROTECT(newattr=duplicate(attr)); */ +/* REAL(VECTOR_ELT(attr, 0))[1] -= 1; */ +/* if (REAL(VECTOR_ELT(attr, 0))[1] == 0) { */ +/* R_ReleaseObject(attr); */ +/* } */ +/* REAL(VECTOR_ELT(newattr, 0))[0] = 0; */ +/* REAL(VECTOR_ELT(newattr, 0))[1] = 1; */ +/* attr=graph->attr=newattr; */ +/* } */ + +/* eal=VECTOR_ELT(attr, 3); */ +/* ealno=GET_LENGTH(eal); */ +/* for (i=0; i 0) { */ +/* newlen++; */ +/* } */ +/* } */ +/* PROTECT(ss=NEW_NUMERIC(newlen)); */ +/* for (j=0; j 0) { */ +/* REAL(ss)[(long int)VECTOR(*idx)[j]-1] = j+1; */ +/* } */ +/* } */ +/* PROTECT(newea=EVAL(lang3(install("["), oldea, ss))); */ +/* SET_VECTOR_ELT(eal, i, newea); */ +/* UNPROTECT(2); */ +/* } */ +/* } */ + +int R_igraph_attribute_permute_edges_same(const igraph_t *graph, + igraph_t *newgraph, + const igraph_vector_t *idx) { + + SEXP attr=newgraph->attr; + SEXP eal; + long int i, ealno; + long int idxlen=igraph_vector_size(idx); + SEXP ss; + int px = 0; + + /* We copy if we need to */ + if (REAL(VECTOR_ELT(attr, 0))[0]+REAL(VECTOR_ELT(attr, 0))[1] > 1) { + SEXP newattr=duplicate(attr); + if (R_igraph_attribute_protected) { + PROTECT(newattr); px++; + } else { + R_PreserveObject(newattr); + } + REAL(VECTOR_ELT(attr, 0))[1] -= 1; + if (!R_igraph_attribute_protected && + REAL(VECTOR_ELT(attr, 0))[1] == 0) { + R_ReleaseObject(attr); + } + REAL(VECTOR_ELT(newattr, 0))[0] = 0; + REAL(VECTOR_ELT(newattr, 0))[1] = 1; + if (R_igraph_attribute_protected) { + long int pos, alen=LENGTH(VECTOR_ELT(attr, 0)); + if (alen == 4) { + pos=REAL(VECTOR_ELT(attr, 0))[3]; + SET_VECTOR_ELT(R_igraph_attribute_protected, pos, newattr); + } else { + SEXP tmp=PROTECT(NEW_NUMERIC(4)); px++; + REAL(tmp)[0] = REAL(VECTOR_ELT(attr, 0))[0]; + REAL(tmp)[1] = REAL(VECTOR_ELT(attr, 0))[1]; + REAL(tmp)[2] = REAL(VECTOR_ELT(attr, 0))[2]; + pos = REAL(tmp)[3] = R_igraph_attribute_protected_size; + R_igraph_attribute_protected_size += 1; + SET_VECTOR_ELT(newattr, 0, tmp); + } + SET_VECTOR_ELT(R_igraph_attribute_protected, pos, newattr); + } + attr=newgraph->attr=newattr; + } + + eal=VECTOR_ELT(attr,3); + ealno=GET_LENGTH(eal); + + /* If we have no edge attributes, then we don't need to do anything */ + if (ealno==0) { UNPROTECT(px); return 0; } + + /* Convert idx to an R object, we will use this for indexing */ + PROTECT(ss=NEW_INTEGER(idxlen)); px++; + for (i=0; iattr; + SEXP toattr=newgraph->attr; + SEXP eal, toeal; + SEXP names; + long int i, ealno; + long int idxlen=igraph_vector_size(idx); + SEXP ss; + + eal=VECTOR_ELT(attr,3); + ealno=GET_LENGTH(eal); + + /* If we have no vertex attributes, then we don't need to do anything */ + if (ealno==0) { return 0; } + + /* Convert idx to an R object, we will use this for indexing */ + PROTECT(ss=NEW_INTEGER(idxlen)); + for (i=0; iattr; + + for (i=0; i<3; i++) { + igraph_strvector_t *n=names[i]; + igraph_vector_t *t=types[i]; + SEXP al=VECTOR_ELT(attr, i+1); + + if (n) { /* return names */ + SEXP names = PROTECT(GET_NAMES(al)); + R_igraph_SEXP_to_strvector_copy(names, n); + UNPROTECT(1); + } + + if (t) { /* return types */ + igraph_vector_resize(t, GET_LENGTH(al)); + for (j=0; jattr, attrnum), name); + return res != R_NilValue; +} + +int R_igraph_attribute_gettype(const igraph_t *graph, + igraph_attribute_type_t *type, + igraph_attribute_elemtype_t elemtype, + const char *name) { + long int attrnum; + SEXP res; + + switch (elemtype) { + case IGRAPH_ATTRIBUTE_GRAPH: + attrnum=1; + break; + case IGRAPH_ATTRIBUTE_VERTEX: + attrnum=2; + break; + case IGRAPH_ATTRIBUTE_EDGE: + attrnum=3; + break; + default: + IGRAPH_ERROR("Unkwown attribute element type", IGRAPH_EINVAL); + break; + } + + res=R_igraph_getListElement(VECTOR_ELT(graph->attr, attrnum), name); + if (IS_NUMERIC(res) || IS_INTEGER(res)) { + *type=IGRAPH_ATTRIBUTE_NUMERIC; + } else if (IS_LOGICAL(res)) { + *type=IGRAPH_ATTRIBUTE_BOOLEAN; + } else if (IS_CHARACTER(res)) { + *type=IGRAPH_ATTRIBUTE_STRING; + } else { + *type=IGRAPH_ATTRIBUTE_R_OBJECT; + } + return 0; +} + +int R_igraph_attribute_get_numeric_graph_attr(const igraph_t *graph, + const char *name, + igraph_vector_t *value) { + SEXP gal=VECTOR_ELT(graph->attr, 1); + SEXP ga=R_igraph_getListElement(gal, name); + + if (ga == R_NilValue) { + IGRAPH_ERROR("No such attribute", IGRAPH_EINVAL); + } + if (!IS_NUMERIC(ga) && !IS_INTEGER(ga)) { + IGRAPH_ERROR("Attribute not numeric", IGRAPH_EINVAL); + } + + IGRAPH_CHECK(igraph_vector_resize(value, 1)); + if (IS_NUMERIC(ga)) { + VECTOR(*value)[0]=REAL(ga)[0]; + } else { /* INTEGER */ + VECTOR(*value)[0]=INTEGER(ga)[0]; + } + + return 0; +} + +int R_igraph_attribute_get_bool_graph_attr(const igraph_t *graph, + const char *name, + igraph_vector_bool_t *value) { + SEXP gal=VECTOR_ELT(graph->attr, 1); + SEXP ga=R_igraph_getListElement(gal, name); + + if (ga == R_NilValue) { + IGRAPH_ERROR("No such attribute", IGRAPH_EINVAL); + } + if (!IS_LOGICAL(ga)) { + IGRAPH_ERROR("Attribute not logical", IGRAPH_EINVAL); + } + + IGRAPH_CHECK(igraph_vector_bool_resize(value, 1)); + VECTOR(*value)[0]=LOGICAL(ga)[0]; + + return 0; +} + +int R_igraph_attribute_get_string_graph_attr(const igraph_t *graph, + const char *name, + igraph_strvector_t *value) { + /* TODO: serialization */ + SEXP gal=VECTOR_ELT(graph->attr, 1); + SEXP ga=R_igraph_getListElement(gal, name); + + if (ga == R_NilValue) { + IGRAPH_ERROR("No such attribute", IGRAPH_EINVAL); + } + if (!IS_CHARACTER(ga)) { + IGRAPH_ERROR("Attribute is not character", IGRAPH_EINVAL); + } + + IGRAPH_CHECK(igraph_strvector_resize(value, 1)); + IGRAPH_CHECK(igraph_strvector_set(value, 0, CHAR(STRING_ELT(ga, 0)))); + + return 0; +} + +int R_igraph_attribute_get_numeric_vertex_attr(const igraph_t *graph, + const char *name, + igraph_vs_t vs, + igraph_vector_t *value) { + /* TODO: serialization */ + SEXP val=VECTOR_ELT(graph->attr, 2); + SEXP va=R_igraph_getListElement(val, name); + igraph_vector_t newvalue; + + if (va == R_NilValue) { + IGRAPH_ERROR("No such attribute", IGRAPH_EINVAL); + } + if (!IS_NUMERIC(va) && !IS_INTEGER(va)) { + IGRAPH_ERROR("Attribute not numeric", IGRAPH_EINVAL); + } + + if (igraph_vs_is_all(&vs)) { + R_SEXP_to_vector_copy(AS_NUMERIC(va), &newvalue); + igraph_vector_destroy(value); + *value=newvalue; + } 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))); + if (IS_NUMERIC(va)) { + while (!IGRAPH_VIT_END(it)) { + long int v=IGRAPH_VIT_GET(it); + VECTOR(*value)[i]=REAL(va)[v]; + IGRAPH_VIT_NEXT(it); + i++; + } + } else if (IS_INTEGER(va)) { + while (!IGRAPH_VIT_END(it)) { + long int v=IGRAPH_VIT_GET(it); + VECTOR(*value)[i]=INTEGER(va)[v]; + IGRAPH_VIT_NEXT(it); + i++; + } + } + igraph_vit_destroy(&it); + IGRAPH_FINALLY_CLEAN(1); + } + + return 0; +} + +int R_igraph_attribute_get_bool_vertex_attr(const igraph_t *graph, + const char *name, + igraph_vs_t vs, + igraph_vector_bool_t *value) { + /* TODO: serialization */ + SEXP val=VECTOR_ELT(graph->attr, 2); + SEXP va=R_igraph_getListElement(val, name); + igraph_vector_bool_t newvalue; + + if (va == R_NilValue) { + IGRAPH_ERROR("No such attribute", IGRAPH_EINVAL); + } + if (!IS_LOGICAL(va)) { + IGRAPH_ERROR("Attribute not logical", IGRAPH_EINVAL); + } + + if (igraph_vs_is_all(&vs)) { + R_SEXP_to_vector_bool_copy(va, &newvalue); + igraph_vector_bool_destroy(value); + *value=newvalue; + } 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_bool_resize(value, IGRAPH_VIT_SIZE(it))); + while (!IGRAPH_VIT_END(it)) { + long int v=IGRAPH_VIT_GET(it); + VECTOR(*value)[i]=LOGICAL(va)[v]; + IGRAPH_VIT_NEXT(it); + i++; + } + igraph_vit_destroy(&it); + IGRAPH_FINALLY_CLEAN(1); + } + + return 0; +} + +int R_igraph_attribute_get_string_vertex_attr(const igraph_t *graph, + const char *name, + igraph_vs_t vs, + igraph_strvector_t *value) { + /* TODO: serialization */ + SEXP val, va; + + val=VECTOR_ELT(graph->attr, 2); + va=R_igraph_getListElement(val, name); + if (va == R_NilValue) { + IGRAPH_ERROR("No such attribute", IGRAPH_EINVAL); + } + if (!IS_CHARACTER(va)) { + IGRAPH_ERROR("Attribute is not character", IGRAPH_EINVAL); + } + + if (igraph_vs_is_all(&vs)) { + R_igraph_SEXP_to_strvector_copy(va, value); + } 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))); + while (!IGRAPH_VIT_END(it)) { + long int v=IGRAPH_VIT_GET(it); + const char *str=CHAR(STRING_ELT(va, v)); + IGRAPH_CHECK(igraph_strvector_set(value, i, str)); + IGRAPH_VIT_NEXT(it); + i++; + } + igraph_vit_destroy(&it); + IGRAPH_FINALLY_CLEAN(1); + } + + return 0; +} + +int R_igraph_attribute_get_numeric_edge_attr(const igraph_t *graph, + const char *name, + igraph_es_t es, + igraph_vector_t *value) { + /* TODO: serialization */ + SEXP eal=VECTOR_ELT(graph->attr, 3); + SEXP ea=R_igraph_getListElement(eal, name); + igraph_vector_t newvalue; + + if (ea == R_NilValue) { + IGRAPH_ERROR("No such attribute", IGRAPH_EINVAL); + } + if (!IS_NUMERIC(ea) && !IS_INTEGER(ea)) { + IGRAPH_ERROR("Attribute is not numeric", IGRAPH_EINVAL); + } + + if (igraph_es_is_all(&es)) { + R_SEXP_to_vector_copy(AS_NUMERIC(ea), &newvalue); + igraph_vector_destroy(value); + *value=newvalue; + } 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))); + if (IS_NUMERIC(ea)) { + while (!IGRAPH_EIT_END(it)) { + long int e=IGRAPH_EIT_GET(it); + VECTOR(*value)[i]=REAL(ea)[e]; + IGRAPH_EIT_NEXT(it); + i++; + } + } else { /* INTEGER */ + while (!IGRAPH_EIT_END(it)) { + long int e=IGRAPH_EIT_GET(it); + VECTOR(*value)[i]=INTEGER(ea)[e]; + IGRAPH_EIT_NEXT(it); + i++; + } + } + igraph_eit_destroy(&it); + IGRAPH_FINALLY_CLEAN(1); + } + + return 0; +} + +int R_igraph_attribute_get_bool_edge_attr(const igraph_t *graph, + const char *name, + igraph_es_t es, + igraph_vector_bool_t *value) { + /* TODO: serialization */ + SEXP eal=VECTOR_ELT(graph->attr, 3); + SEXP ea=R_igraph_getListElement(eal, name); + igraph_vector_bool_t newvalue; + + if (ea == R_NilValue) { + IGRAPH_ERROR("No such attribute", IGRAPH_EINVAL); + } + if (!IS_LOGICAL(ea)) { + IGRAPH_ERROR("Attribute not logical", IGRAPH_EINVAL); + } + + if (igraph_es_is_all(&es)) { + R_SEXP_to_vector_bool_copy(ea, &newvalue); + igraph_vector_bool_destroy(value); + *value=newvalue; + } 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))); + while (!IGRAPH_EIT_END(it)) { + long int e=IGRAPH_EIT_GET(it); + VECTOR(*value)[i]=LOGICAL(ea)[e]; + IGRAPH_EIT_NEXT(it); + i++; + } + igraph_eit_destroy(&it); + IGRAPH_FINALLY_CLEAN(1); + } + + return 0; +} + +int R_igraph_attribute_get_string_edge_attr(const igraph_t *graph, + const char *name, + igraph_es_t es, + igraph_strvector_t *value) { + /* TODO: serialization */ + SEXP eal=VECTOR_ELT(graph->attr, 3); + SEXP ea=R_igraph_getListElement(eal, name); + + if (ea == R_NilValue) { + IGRAPH_ERROR("No such attribute", IGRAPH_EINVAL); + } + if (!IS_CHARACTER(ea)) { + IGRAPH_ERROR("Attribute is not character", IGRAPH_EINVAL); + } + + if (igraph_es_is_all(&es)) { + R_igraph_SEXP_to_strvector_copy(ea, value); + } 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))); + while (!IGRAPH_EIT_END(it)) { + long int e=IGRAPH_EIT_GET(it); + const char *str=CHAR(STRING_ELT(ea, e)); + IGRAPH_CHECK(igraph_strvector_set(value, i, str)); + IGRAPH_EIT_NEXT(it); + i++; + } + igraph_eit_destroy(&it); + IGRAPH_FINALLY_CLEAN(1); + } + + return 0; +} + +SEXP R_igraph_ac_sum_numeric(SEXP attr, + const igraph_vector_ptr_t *merges) { + SEXP res; + SEXP attr2; + long int i, len=igraph_vector_ptr_size(merges); + + PROTECT(attr2=AS_NUMERIC(attr)); + PROTECT(res=NEW_NUMERIC(len)); + + for (i=0; i 0 ? REAL(attr2)[(long) VECTOR(*v)[0] ] : NA_REAL; + for (j=1; j 0 ? REAL(attr2)[(long) VECTOR(*v)[0] ] : NA_REAL; + for (j=1; j m) { + m=val; + } + } + REAL(res)[i] = m; + } + + UNPROTECT(2); + return res; +} + +SEXP R_igraph_ac_random_numeric(SEXP attr, + const igraph_vector_ptr_t *merges) { + SEXP res; + SEXP attr2; + long int i, len=igraph_vector_ptr_size(merges); + + PROTECT(attr2=AS_NUMERIC(attr)); + PROTECT(res=NEW_NUMERIC(len)); + + RNG_BEGIN(); + + for (i=0; i0 ? 0.0 : NA_REAL; + for (j=0; j0) { s=s/n; } + REAL(res)[i] = s; + } + + UNPROTECT(2); + return res; +} + +SEXP R_igraph_ac_median_numeric(SEXP attr, + const igraph_vector_ptr_t *merges) { + SEXP res; + SEXP attr2; + long int i, len=igraph_vector_ptr_size(merges); + + PROTECT(attr2=AS_NUMERIC(attr)); + PROTECT(res=NEW_NUMERIC(len)); + + for (i=0; iattr; + SEXP toattr=newgraph->attr; + SEXP val=VECTOR_ELT(attr, 2); + long int i, j, valno=GET_LENGTH(val); + SEXP names, newnames; + SEXP res; + int keepno=0; + int *TODO; + igraph_function_pointer_t *funcs; + int px = 0; + + /* Create the TODO list first */ + PROTECT(names=GET_NAMES(val)); px++; + TODO=igraph_Calloc(valno, int); + if (!TODO) { + UNPROTECT(px); + IGRAPH_ERROR("Cannot combine edge attributes", + IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, TODO); + funcs=igraph_Calloc(valno, igraph_function_pointer_t); + if (!funcs) { + UNPROTECT(px); + IGRAPH_ERROR("Cannot combine edge attributes", + IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, funcs); + for (i=0; iattr; + SEXP toattr=newgraph->attr; + SEXP eal=VECTOR_ELT(attr, 3); + long int i, j, ealno=GET_LENGTH(eal); + SEXP names, newnames; + SEXP res; + int keepno=0; + int *TODO; + igraph_function_pointer_t *funcs; + int px = 0; + + /* Create the TODO list first */ + PROTECT(names=GET_NAMES(eal)); px++; + TODO=igraph_Calloc(ealno, int); + if (!TODO) { + UNPROTECT(px); + IGRAPH_ERROR("Cannot combine edge attributes", + IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, TODO); + funcs=igraph_Calloc(ealno, igraph_function_pointer_t); + if (!funcs) { + UNPROTECT(px); + IGRAPH_ERROR("Cannot combine edge attributes", + IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, funcs); + for (i=0; idata, REAL(result)); + PROTECT(dim=NEW_INTEGER(3)); + INTEGER(dim)[0]=(int) igraph_array3_n(a, 1); + INTEGER(dim)[1]=(int) igraph_array3_n(a, 2); + INTEGER(dim)[2]=(int) igraph_array3_n(a, 3); + SET_DIM(result, dim); + + UNPROTECT(2); + return result; +} + +SEXP R_igraph_0orarray3_to_SEXP(const igraph_array3_t *a) { + SEXP result; + if (a) { + PROTECT(result=R_igraph_array3_to_SEXP(a)); + } else { + PROTECT(result=R_NilValue); + } + UNPROTECT(1); + return result; +} + +SEXP R_igraph_strvector_to_SEXP(const igraph_strvector_t *m) { + SEXP result; + long int i; + char *str; + long int len; + + len=igraph_strvector_size(m); + PROTECT(result=NEW_CHARACTER(len)); + for (i=0; idirected; + memcpy(REAL(VECTOR_ELT(result, 2)), graph->from.stor_begin, + sizeof(igraph_real_t)*(size_t) no_of_edges); + memcpy(REAL(VECTOR_ELT(result, 3)), graph->to.stor_begin, + sizeof(igraph_real_t)*(size_t) no_of_edges); + memcpy(REAL(VECTOR_ELT(result, 4)), graph->oi.stor_begin, + sizeof(igraph_real_t)*(size_t) no_of_edges); + memcpy(REAL(VECTOR_ELT(result, 5)), graph->ii.stor_begin, + sizeof(igraph_real_t)*(size_t) no_of_edges); + memcpy(REAL(VECTOR_ELT(result, 6)), graph->os.stor_begin, + sizeof(igraph_real_t)*(size_t) (no_of_nodes+1)); + memcpy(REAL(VECTOR_ELT(result, 7)), graph->is.stor_begin, + sizeof(igraph_real_t)*(size_t) (no_of_nodes+1)); + + SET_CLASS(result, ScalarString(CREATE_STRING_VECTOR("igraph"))); + + /* Attributes */ + SET_VECTOR_ELT(result, 8, graph->attr); + REAL(VECTOR_ELT(graph->attr, 0))[0] += 1; + + /* Environment for vertex/edge seqs */ + SET_VECTOR_ELT(result, 9, R_NilValue); + R_igraph_add_env(result); + + UNPROTECT(1); + return result; +} + +SEXP R_igraph_vectorlist_to_SEXP(const igraph_vector_ptr_t *ptr) { + SEXP result; + long int i, n=igraph_vector_ptr_size(ptr); + + PROTECT(result=NEW_LIST(n)); + for (i=0; ileft)); + SET_VECTOR_ELT(result, 1, R_igraph_vector_to_SEXP(&hrg->right)); + SET_VECTOR_ELT(result, 2, R_igraph_vector_to_SEXP(&hrg->prob)); + SET_VECTOR_ELT(result, 3, R_igraph_vector_to_SEXP(&hrg->edges)); + SET_VECTOR_ELT(result, 4, R_igraph_vector_to_SEXP(&hrg->vertices)); + + PROTECT(names=NEW_CHARACTER(5)); + SET_STRING_ELT(names, 0, mkChar("left")); + SET_STRING_ELT(names, 1, mkChar("right")); + SET_STRING_ELT(names, 2, mkChar("prob")); + SET_STRING_ELT(names, 3, mkChar("edges")); + SET_STRING_ELT(names, 4, mkChar("vertices")); + SET_NAMES(result, names); + + UNPROTECT(2); + return result; +} + +int R_SEXP_to_hrg(SEXP shrg, igraph_hrg_t *hrg) { + R_SEXP_to_vector(VECTOR_ELT(shrg, 0), &hrg->left); + R_SEXP_to_vector(VECTOR_ELT(shrg, 1), &hrg->right); + R_SEXP_to_vector(VECTOR_ELT(shrg, 2), &hrg->prob); + R_SEXP_to_vector(VECTOR_ELT(shrg, 3), &hrg->edges); + R_SEXP_to_vector(VECTOR_ELT(shrg, 4), &hrg->vertices); + return 0; +} + +int R_SEXP_to_hrg_copy(SEXP shrg, igraph_hrg_t *hrg) { + R_SEXP_to_vector_copy(VECTOR_ELT(shrg, 0), &hrg->left); + R_SEXP_to_vector_copy(VECTOR_ELT(shrg, 1), &hrg->right); + R_SEXP_to_vector_copy(VECTOR_ELT(shrg, 2), &hrg->prob); + R_SEXP_to_vector_copy(VECTOR_ELT(shrg, 3), &hrg->edges); + R_SEXP_to_vector_copy(VECTOR_ELT(shrg, 4), &hrg->vertices); + return 0; +} + +SEXP R_igraph_plfit_result_to_SEXP(const igraph_plfit_result_t *plfit) { + SEXP result, names; + + PROTECT(result=NEW_LIST(6)); + SET_VECTOR_ELT(result, 0, ScalarLogical(plfit->continuous)); + SET_VECTOR_ELT(result, 1, ScalarReal(plfit->alpha)); + SET_VECTOR_ELT(result, 2, ScalarReal(plfit->xmin)); + SET_VECTOR_ELT(result, 3, ScalarReal(plfit->L)); + SET_VECTOR_ELT(result, 4, ScalarReal(plfit->D)); + SET_VECTOR_ELT(result, 5, ScalarReal(plfit->p)); + + PROTECT(names=NEW_CHARACTER(6)); + SET_STRING_ELT(names, 0, mkChar("continuous")); + SET_STRING_ELT(names, 1, mkChar("alpha")); + SET_STRING_ELT(names, 2, mkChar("xmin")); + SET_STRING_ELT(names, 3, mkChar("logLik")); + SET_STRING_ELT(names, 4, mkChar("KS.stat")); + SET_STRING_ELT(names, 5, mkChar("KS.p")); + SET_NAMES(result, names); + + UNPROTECT(2); + return result; +} + +SEXP R_igraph_maxflow_stats_to_SEXP(const igraph_maxflow_stats_t *st) { + SEXP result, names; + + PROTECT(result=NEW_LIST(5)); + SET_VECTOR_ELT(result, 0, ScalarInteger(st->nopush)); + SET_VECTOR_ELT(result, 1, ScalarInteger(st->norelabel)); + SET_VECTOR_ELT(result, 2, ScalarInteger(st->nogap)); + SET_VECTOR_ELT(result, 3, ScalarInteger(st->nogapnodes)); + SET_VECTOR_ELT(result, 4, ScalarInteger(st->nobfs)); + + PROTECT(names=NEW_CHARACTER(5)); + SET_STRING_ELT(names, 0, mkChar("nopush")); + SET_STRING_ELT(names, 1, mkChar("norelabel")); + SET_STRING_ELT(names, 2, mkChar("nogap")); + SET_STRING_ELT(names, 3, mkChar("nogapnodes")); + SET_STRING_ELT(names, 4, mkChar("nobfs")); + SET_NAMES(result, names); + + UNPROTECT(2); + return result; +} + +SEXP R_igraph_sirlist_to_SEXP(const igraph_vector_ptr_t *sl) { + SEXP result, names; + int i, n=igraph_vector_ptr_size(sl); + PROTECT(result=NEW_LIST(n)); + + PROTECT(names=NEW_CHARACTER(4)); + SET_STRING_ELT(names, 0, mkChar("times")); + SET_STRING_ELT(names, 1, mkChar("NS")); + SET_STRING_ELT(names, 2, mkChar("NI")); + SET_STRING_ELT(names, 3, mkChar("NR")); + + for (i=0; itimes)); + SET_VECTOR_ELT(tmp, 1, R_igraph_vector_int_to_SEXP(&sir->no_s)); + SET_VECTOR_ELT(tmp, 2, R_igraph_vector_int_to_SEXP(&sir->no_i)); + SET_VECTOR_ELT(tmp, 3, R_igraph_vector_int_to_SEXP(&sir->no_r)); + SET_VECTOR_ELT(result, i, tmp); + SET_NAMES(tmp, names); + UNPROTECT(1); + } + + UNPROTECT(2); + return result; +} + +void R_igraph_sirlist_destroy(igraph_vector_ptr_t *sl) { + int i, n=igraph_vector_ptr_size(sl); + for (i=0; itimes); + igraph_vector_int_destroy(&sir->no_s); + igraph_vector_int_destroy(&sir->no_i); + igraph_vector_int_destroy(&sir->no_r); + igraph_free(sir); + } + igraph_vector_ptr_destroy(sl); +} + +int R_SEXP_to_sparsemat(SEXP pakl, igraph_sparsemat_t *akl) { + SEXP Dim=GET_SLOT(pakl, install("Dim")); + SEXP i=GET_SLOT(pakl, install("i")); + SEXP p=GET_SLOT(pakl, install("p")); + SEXP x=GET_SLOT(pakl, install("x")); + + igraph_sparsemat_view(akl, /*nzmax=*/ GET_LENGTH(x), + /*m=*/ INTEGER(Dim)[0], /*n=*/ INTEGER(Dim)[1], + /*p=*/ INTEGER(p), /*i=*/ INTEGER(i), + /*x=*/ REAL(x), /*nz=*/ -1); + + return 0; +} + +SEXP R_igraph_sparsemat_to_SEXP_triplet(const igraph_sparsemat_t *sp) { + SEXP res, names; + int nz=igraph_sparsemat_nonzero_storage(sp); + + PROTECT(res=NEW_LIST(5)); + SET_VECTOR_ELT(res, 0, ScalarString(CREATE_STRING_VECTOR("triplet"))); + SET_VECTOR_ELT(res, 1, NEW_INTEGER(2)); + INTEGER(VECTOR_ELT(res, 1))[0] = (int) igraph_sparsemat_nrow(sp); + INTEGER(VECTOR_ELT(res, 1))[1] = (int) igraph_sparsemat_ncol(sp); + SET_VECTOR_ELT(res, 2, NEW_INTEGER(nz)); + SET_VECTOR_ELT(res, 3, NEW_INTEGER(nz)); + SET_VECTOR_ELT(res, 4, NEW_NUMERIC(nz)); + if (nz > 0) { + igraph_vector_int_t i, j; + igraph_vector_t x; + igraph_vector_int_view(&i, INTEGER(VECTOR_ELT(res, 2)), nz); + igraph_vector_int_view(&j, INTEGER(VECTOR_ELT(res, 3)), nz); + igraph_vector_view(&x, REAL(VECTOR_ELT(res, 4)), nz); + igraph_sparsemat_getelements(sp, &j, &i, &x); + } + + PROTECT(names=NEW_CHARACTER(5)); + SET_STRING_ELT(names, 0, mkChar("type")); + SET_STRING_ELT(names, 1, mkChar("dim")); + SET_STRING_ELT(names, 2, mkChar("p")); + SET_STRING_ELT(names, 3, mkChar("i")); + SET_STRING_ELT(names, 4, mkChar("x")); + SET_NAMES(res, names); + SET_CLASS(res, ScalarString(CREATE_STRING_VECTOR("igraph.tmp.sparse"))); + + UNPROTECT(2); + return res; +} + +SEXP R_igraph_sparsemat_to_SEXP_cc(const igraph_sparsemat_t *sp) { + SEXP res, names; + int nz=igraph_sparsemat_nonzero_storage(sp); + int m=(int) igraph_sparsemat_nrow(sp); + int n=(int) igraph_sparsemat_ncol(sp); + + PROTECT(res=NEW_LIST(5)); + SET_VECTOR_ELT(res, 0, ScalarString(CREATE_STRING_VECTOR("cc"))); + SET_VECTOR_ELT(res, 1, NEW_INTEGER(2)); + INTEGER(VECTOR_ELT(res, 1))[0] = m; + INTEGER(VECTOR_ELT(res, 1))[1] = n; + SET_VECTOR_ELT(res, 2, NEW_INTEGER(n+1)); + SET_VECTOR_ELT(res, 3, NEW_INTEGER(nz)); + SET_VECTOR_ELT(res, 4, NEW_NUMERIC(nz)); + if (nz > 0) { + igraph_vector_int_t i, p; + igraph_vector_t x; + igraph_vector_int_view(&p, INTEGER(VECTOR_ELT(res, 2)), n+1); + igraph_vector_int_view(&i, INTEGER(VECTOR_ELT(res, 3)), nz); + igraph_vector_view(&x, REAL(VECTOR_ELT(res, 4)), nz); + igraph_sparsemat_getelements_sorted(sp, &i, &p, &x); + } + + PROTECT(names=NEW_CHARACTER(5)); + SET_STRING_ELT(names, 0, mkChar("type")); + SET_STRING_ELT(names, 1, mkChar("dim")); + SET_STRING_ELT(names, 2, mkChar("p")); + SET_STRING_ELT(names, 3, mkChar("i")); + SET_STRING_ELT(names, 4, mkChar("x")); + SET_NAMES(res, names); + SET_CLASS(res, ScalarString(CREATE_STRING_VECTOR("igraph.tmp.sparse"))); + + UNPROTECT(2); + return res; +} + +SEXP R_igraph_sparsemat_to_SEXP(const igraph_sparsemat_t *sp) { + if (igraph_sparsemat_is_triplet(sp)) { + return R_igraph_sparsemat_to_SEXP_triplet(sp); + } else { + return R_igraph_sparsemat_to_SEXP_cc(sp); + } +} + +SEXP R_igraph_0orsparsemat_to_SEXP(const igraph_sparsemat_t *sp) { + if (!sp) { + return R_NilValue; + } else { + return R_igraph_sparsemat_to_SEXP(sp); + } +} + +int R_SEXP_to_igraph_adjlist(SEXP vectorlist, igraph_adjlist_t *ptr) { + int length=GET_LENGTH(vectorlist); + int i; + + ptr->length=length; + ptr->adjs = (igraph_vector_int_t*) R_alloc((size_t) length, + sizeof(igraph_vector_int_t)); + for (i=0; iadjs[i], INTEGER(vec), GET_LENGTH(vec)); + } + return 0; +} + +int R_igraph_SEXP_to_0orvectorlist(SEXP vectorlist, + igraph_vector_ptr_t *ptr) { + if (!isNull(vectorlist)) { + return R_igraph_SEXP_to_vectorlist(vectorlist, ptr); + } + return 0; +} + +int R_igraph_SEXP_to_vectorlist(SEXP vectorlist, igraph_vector_ptr_t *ptr) { + int length=GET_LENGTH(vectorlist); + int i; + igraph_vector_t *vecs; + igraph_vector_t **vecsptr; + + vecs = (igraph_vector_t *) R_alloc((size_t) length, sizeof(igraph_vector_t)); + vecsptr = (igraph_vector_t **) R_alloc((size_t) length, + sizeof(igraph_vector_t*)); + igraph_vector_ptr_view(ptr, (void**) vecsptr, length); + for (i=0; ilen=GET_LENGTH(rval); + sv->data=(char**) R_alloc((size_t) (sv->len), sizeof(char*)); + for (i=0; ilen; i++) { + sv->data[i]=(char*) CHAR(STRING_ELT(rval, i)); + } + + return 0; +} + +int R_igraph_SEXP_to_strvector_copy(SEXP rval, igraph_strvector_t *sv) { + long int i; + igraph_strvector_init(sv, GET_LENGTH(rval)); + for (i=0; ilen; i++) { + igraph_strvector_set(sv, i, CHAR(STRING_ELT(rval, i))); + } + + return 0; +} + +int R_SEXP_to_vector(SEXP sv, igraph_vector_t *v) { + v->stor_begin=REAL(sv); + v->stor_end=v->stor_begin+GET_LENGTH(sv); + v->end=v->stor_end; + return 0; +} + +int R_SEXP_to_vector_copy(SEXP sv, igraph_vector_t *v) { + return igraph_vector_init_copy(v, REAL(sv), GET_LENGTH(sv)); +} + +int R_SEXP_to_vector_bool(SEXP sv, igraph_vector_bool_t *v) { + v->stor_begin=LOGICAL(sv); + v->stor_end=v->stor_begin+GET_LENGTH(sv); + v->end=v->stor_end; + return 0; +} + +int R_SEXP_to_vector_bool_copy(SEXP sv, igraph_vector_bool_t *v) { + long int i, n=GET_LENGTH(sv); + int *svv=LOGICAL(sv); + igraph_vector_bool_init(v, n); + for (i=0; istor_begin=(int*) INTEGER(sv); + v->stor_end=v->stor_begin+GET_LENGTH(sv); + v->end=v->stor_end; + return 0; +} + +int R_SEXP_to_vector_long_copy(SEXP sv, igraph_vector_long_t *v) { + long int i, n=GET_LENGTH(sv); + double *svv=REAL(sv); + igraph_vector_long_init(v, n); + for (i=0; idata); + akl->nrow=INTEGER(GET_DIM(pakl))[0]; + akl->ncol=INTEGER(GET_DIM(pakl))[1]; + + return 0; +} + +int R_SEXP_to_igraph_matrix_copy(SEXP pakl, igraph_matrix_t *akl) { + igraph_vector_init_copy(&akl->data, REAL(pakl), GET_LENGTH(pakl)); + akl->nrow=INTEGER(GET_DIM(pakl))[0]; + akl->ncol=INTEGER(GET_DIM(pakl))[1]; + + return 0; +} + +int R_SEXP_to_vector_complex(SEXP pv, igraph_vector_complex_t *v) { + v->stor_begin=(igraph_complex_t*) COMPLEX(pv); + v->stor_end=v->stor_begin+GET_LENGTH(pv); + v->end=v->stor_end; + return 0; +} + +int R_SEXP_to_vector_complex_copy(SEXP pv, igraph_vector_complex_t *v) { + igraph_vector_complex_init_copy(v, (igraph_complex_t*) COMPLEX(pv), + GET_LENGTH(pv)); + return 0; +} + +int R_SEXP_to_matrix_complex(SEXP pakl, igraph_matrix_complex_t *akl) { + R_SEXP_to_vector_complex(pakl, &akl->data); + akl->nrow=INTEGER(GET_DIM(pakl))[0]; + akl->ncol=INTEGER(GET_DIM(pakl))[1]; + return 0; +} + +int R_SEXP_to_matrix_complex_copy(SEXP pakl, igraph_matrix_complex_t *akl) { + igraph_vector_complex_init_copy(&akl->data, + (igraph_complex_t*) COMPLEX(pakl), + GET_LENGTH(pakl)); + akl->nrow=INTEGER(GET_DIM(pakl))[0]; + akl->ncol=INTEGER(GET_DIM(pakl))[1]; + return 0; +} + +int R_igraph_SEXP_to_array3(SEXP rval, igraph_array3_t *a) { + R_SEXP_to_vector(rval, &a->data); + a->n1=INTEGER(GET_DIM(rval))[0]; + a->n2=INTEGER(GET_DIM(rval))[1]; + a->n3=INTEGER(GET_DIM(rval))[2]; + a->n1n2=(a->n1) * (a->n2); + + return 0; +} + +int R_igraph_SEXP_to_array3_copy(SEXP rval, igraph_array3_t *a) { + igraph_vector_init_copy(&a->data, REAL(rval), GET_LENGTH(rval)); + a->n1=INTEGER(GET_DIM(rval))[0]; + a->n2=INTEGER(GET_DIM(rval))[1]; + a->n3=INTEGER(GET_DIM(rval))[2]; + a->n1n2=(a->n1) * (a->n2); + + return 0; +} + +int R_SEXP_to_igraph(SEXP graph, igraph_t *res) { + + res->n=(igraph_integer_t) REAL(VECTOR_ELT(graph, 0))[0]; + res->directed=LOGICAL(VECTOR_ELT(graph, 1))[0]; + R_SEXP_to_vector(VECTOR_ELT(graph, 2), &res->from); + R_SEXP_to_vector(VECTOR_ELT(graph, 3), &res->to); + R_SEXP_to_vector(VECTOR_ELT(graph, 4), &res->oi); + R_SEXP_to_vector(VECTOR_ELT(graph, 5), &res->ii); + R_SEXP_to_vector(VECTOR_ELT(graph, 6), &res->os); + R_SEXP_to_vector(VECTOR_ELT(graph, 7), &res->is); + + /* attributes */ + REAL(VECTOR_ELT(VECTOR_ELT(graph, 8), 0))[0] = 1; /* R objects refcount */ + REAL(VECTOR_ELT(VECTOR_ELT(graph, 8), 0))[1] = 0; /* igraph_t objects */ + res->attr=VECTOR_ELT(graph, 8); + + return 0; +} + +int R_SEXP_to_igraph_copy(SEXP graph, igraph_t *res) { + + res->n=(igraph_integer_t) REAL(VECTOR_ELT(graph, 0))[0]; + res->directed=LOGICAL(VECTOR_ELT(graph, 1))[0]; + igraph_vector_init_copy(&res->from, REAL(VECTOR_ELT(graph, 2)), + GET_LENGTH(VECTOR_ELT(graph, 2))); + igraph_vector_init_copy(&res->to, REAL(VECTOR_ELT(graph, 3)), + GET_LENGTH(VECTOR_ELT(graph, 3))); + igraph_vector_init_copy(&res->oi, REAL(VECTOR_ELT(graph, 4)), + GET_LENGTH(VECTOR_ELT(graph, 4))); + igraph_vector_init_copy(&res->ii, REAL(VECTOR_ELT(graph, 5)), + GET_LENGTH(VECTOR_ELT(graph, 5))); + igraph_vector_init_copy(&res->os, REAL(VECTOR_ELT(graph, 6)), + GET_LENGTH(VECTOR_ELT(graph, 6))); + igraph_vector_init_copy(&res->is, REAL(VECTOR_ELT(graph, 7)), + GET_LENGTH(VECTOR_ELT(graph, 7))); + + /* attributes */ + REAL(VECTOR_ELT(VECTOR_ELT(graph, 8), 0))[0] = 1; /* R objects */ + REAL(VECTOR_ELT(VECTOR_ELT(graph, 8), 0))[1] = 1; /* igraph_t objects */ + R_PreserveObject(res->attr=VECTOR_ELT(graph, 8)); + + return 0; +} + +/* + * We have only vector type + */ + +int R_SEXP_to_igraph_vs(SEXP rit, igraph_t *graph, igraph_vs_t *it) { + + igraph_vector_t *tmpv=(igraph_vector_t*)R_alloc(1,sizeof(igraph_vector_t)); + igraph_vs_vector(it, igraph_vector_view(tmpv, REAL(rit), + GET_LENGTH(rit))); + return 0; +} + +/* + * We have only vector type + */ + +int R_SEXP_to_igraph_es(SEXP rit, igraph_t *graph, igraph_es_t *it) { + + igraph_vector_t *tmpv=(igraph_vector_t*)R_alloc(1,sizeof(igraph_vector_t)); + igraph_es_vector(it, igraph_vector_view(tmpv, REAL(rit), + GET_LENGTH(rit))); + return 0; +} + +int R_SEXP_to_igraph_layout_drl_options(SEXP in, igraph_layout_drl_options_t *opt) { + opt->edge_cut = REAL(AS_NUMERIC(R_igraph_getListElement(in, "edge.cut")))[0]; + opt->init_iterations = (igraph_integer_t) REAL(AS_NUMERIC(R_igraph_getListElement(in, "init.iterations")))[0]; + opt->init_temperature = REAL(AS_NUMERIC(R_igraph_getListElement(in, "init.temperature")))[0]; + opt->init_attraction = REAL(AS_NUMERIC(R_igraph_getListElement(in, "init.attraction")))[0]; + opt->init_damping_mult = REAL(AS_NUMERIC(R_igraph_getListElement(in, "init.damping.mult")))[0]; + opt->liquid_iterations = (igraph_integer_t) REAL(AS_NUMERIC(R_igraph_getListElement(in, "liquid.iterations")))[0]; + opt->liquid_temperature = REAL(AS_NUMERIC(R_igraph_getListElement(in, "liquid.temperature")))[0]; + opt->liquid_attraction = REAL(AS_NUMERIC(R_igraph_getListElement(in, "liquid.attraction")))[0]; + opt->liquid_damping_mult = REAL(AS_NUMERIC(R_igraph_getListElement(in, "liquid.damping.mult")))[0]; + opt->expansion_iterations = (igraph_integer_t) REAL(AS_NUMERIC(R_igraph_getListElement(in, "expansion.iterations")))[0]; + opt->expansion_temperature = REAL(AS_NUMERIC(R_igraph_getListElement(in, "expansion.temperature")))[0]; + opt->expansion_attraction = REAL(AS_NUMERIC(R_igraph_getListElement(in, "expansion.attraction")))[0]; + opt->expansion_damping_mult = REAL(AS_NUMERIC(R_igraph_getListElement(in, "expansion.damping.mult")))[0]; + opt->cooldown_iterations = (igraph_integer_t) REAL(AS_NUMERIC(R_igraph_getListElement(in, "cooldown.iterations")))[0]; + opt->cooldown_temperature = REAL(AS_NUMERIC(R_igraph_getListElement(in, "cooldown.temperature")))[0]; + opt->cooldown_attraction = REAL(AS_NUMERIC(R_igraph_getListElement(in, "cooldown.attraction")))[0]; + opt->cooldown_damping_mult = REAL(AS_NUMERIC(R_igraph_getListElement(in, "cooldown.damping.mult")))[0]; + opt->crunch_iterations = (igraph_integer_t) REAL(AS_NUMERIC(R_igraph_getListElement(in, "crunch.iterations")))[0]; + opt->crunch_temperature = REAL(AS_NUMERIC(R_igraph_getListElement(in, "crunch.temperature")))[0]; + opt->crunch_attraction = REAL(AS_NUMERIC(R_igraph_getListElement(in, "crunch.attraction")))[0]; + opt->crunch_damping_mult = REAL(AS_NUMERIC(R_igraph_getListElement(in, "crunch.damping.mult")))[0]; + opt->simmer_iterations = (igraph_integer_t) REAL(AS_NUMERIC(R_igraph_getListElement(in, "simmer.iterations")))[0]; + opt->simmer_temperature = REAL(AS_NUMERIC(R_igraph_getListElement(in, "simmer.temperature")))[0]; + opt->simmer_attraction = REAL(AS_NUMERIC(R_igraph_getListElement(in, "simmer.attraction")))[0]; + opt->simmer_damping_mult = REAL(AS_NUMERIC(R_igraph_getListElement(in, "simmer.damping.mult")))[0]; + + return 0; +} + +int R_SEXP_to_igraph_arpack_options(SEXP in, igraph_arpack_options_t *opt) { + const char *tmpstr; + igraph_arpack_options_init(opt); + opt -> bmat[0] = CHAR(STRING_ELT(AS_CHARACTER + (R_igraph_getListElement(in, "bmat")), 0))[0]; + opt -> n = INTEGER(AS_INTEGER(R_igraph_getListElement(in, "n")))[0]; + tmpstr=CHAR(STRING_ELT(AS_CHARACTER(R_igraph_getListElement(in, "which")), 0)); + opt -> which[0]=tmpstr[0]; opt -> which[1]=tmpstr[1]; + opt -> nev = INTEGER(AS_INTEGER(R_igraph_getListElement(in, "nev")))[0]; + opt -> tol = REAL(AS_NUMERIC(R_igraph_getListElement(in, "tol")))[0]; + opt -> ncv = INTEGER(AS_INTEGER(R_igraph_getListElement(in, "ncv")))[0]; + opt -> ldv = INTEGER(AS_INTEGER(R_igraph_getListElement(in, "ldv")))[0]; + opt -> ishift = INTEGER(AS_INTEGER(R_igraph_getListElement(in, "ishift")))[0]; + opt -> mxiter = INTEGER(AS_INTEGER(R_igraph_getListElement(in, "maxiter")))[0]; + opt -> nb = INTEGER(AS_INTEGER(R_igraph_getListElement(in, "nb")))[0]; + opt -> mode = INTEGER(AS_INTEGER(R_igraph_getListElement(in, "mode")))[0]; + opt -> start = INTEGER(AS_INTEGER(R_igraph_getListElement(in, "start")))[0]; + opt -> lworkl = 0; + opt -> sigma = REAL(AS_NUMERIC(R_igraph_getListElement(in, "sigma")))[0]; + opt -> sigmai = REAL(AS_NUMERIC(R_igraph_getListElement(in, "sigmai")))[0]; + opt -> info = opt -> start; + + opt->iparam[0]=opt->ishift; + opt->iparam[2]=opt->mxiter; + opt->iparam[3]=opt->nb; + opt->iparam[6]=opt->mode; + + return 0; +} + +SEXP R_igraph_arpack_options_to_SEXP(const igraph_arpack_options_t *opt) { + SEXP result, names; + char bmat[2], which[3]; + + bmat[0]=opt->bmat[0]; bmat[1]='\0'; + which[0]=opt->which[0]; which[1]=opt->which[1]; which[2]='\0'; + + PROTECT(result = NEW_LIST(20)); + SET_VECTOR_ELT(result, 0, ScalarString(CREATE_STRING_VECTOR(bmat))); + SET_VECTOR_ELT(result, 1, NEW_INTEGER(1)); INTEGER(VECTOR_ELT(result, 1))[0]=opt->n; + SET_VECTOR_ELT(result, 2, ScalarString(CREATE_STRING_VECTOR(which))); + SET_VECTOR_ELT(result, 3, NEW_INTEGER(1)); INTEGER(VECTOR_ELT(result, 3))[0]=opt->nev; + SET_VECTOR_ELT(result, 4, NEW_NUMERIC(1)); REAL(VECTOR_ELT(result, 4))[0]=opt->tol; + SET_VECTOR_ELT(result, 5, NEW_INTEGER(1)); INTEGER(VECTOR_ELT(result, 5))[0]=opt->ncv; + SET_VECTOR_ELT(result, 6, NEW_INTEGER(1)); INTEGER(VECTOR_ELT(result, 6))[0]=opt->ldv; + SET_VECTOR_ELT(result, 7, NEW_INTEGER(1)); INTEGER(VECTOR_ELT(result, 7))[0]=opt->ishift; + SET_VECTOR_ELT(result, 8, NEW_INTEGER(1)); INTEGER(VECTOR_ELT(result, 8))[0]=opt->mxiter; + SET_VECTOR_ELT(result, 9, NEW_INTEGER(1)); INTEGER(VECTOR_ELT(result, 9))[0]=opt->nb; + SET_VECTOR_ELT(result, 10, NEW_INTEGER(1)); INTEGER(VECTOR_ELT(result, 10))[0]=opt->mode; + SET_VECTOR_ELT(result, 11, NEW_INTEGER(1)); INTEGER(VECTOR_ELT(result, 11))[0]=opt->start; + SET_VECTOR_ELT(result, 12, NEW_NUMERIC(1)); REAL(VECTOR_ELT(result, 12))[0]=opt->sigma; + SET_VECTOR_ELT(result, 13, NEW_NUMERIC(1)); REAL(VECTOR_ELT(result, 13))[0]=opt->sigmai; + + SET_VECTOR_ELT(result, 14, NEW_INTEGER(1)); INTEGER(VECTOR_ELT(result, 14))[0]=opt->info; + SET_VECTOR_ELT(result, 15, NEW_INTEGER(1)); INTEGER(VECTOR_ELT(result, 15))[0]=opt->iparam[2];/* mxiter */ + SET_VECTOR_ELT(result, 16, NEW_INTEGER(1)); INTEGER(VECTOR_ELT(result, 16))[0]=opt->iparam[4];/* nconv */ + SET_VECTOR_ELT(result, 17, NEW_INTEGER(1)); INTEGER(VECTOR_ELT(result, 17))[0]=opt->iparam[8];/* numop */ + SET_VECTOR_ELT(result, 18, NEW_INTEGER(1)); INTEGER(VECTOR_ELT(result, 18))[0]=opt->iparam[9];/* numopb */ + SET_VECTOR_ELT(result, 19, NEW_INTEGER(1)); INTEGER(VECTOR_ELT(result, 19))[0]=opt->iparam[10];/* numreo */ + + PROTECT(names=NEW_CHARACTER(20)); + SET_STRING_ELT(names, 0, mkChar("bmat")); + SET_STRING_ELT(names, 1, mkChar("n")); + SET_STRING_ELT(names, 2, mkChar("which")); + SET_STRING_ELT(names, 3, mkChar("nev")); + SET_STRING_ELT(names, 4, mkChar("tol")); + SET_STRING_ELT(names, 5, mkChar("ncv")); + SET_STRING_ELT(names, 6, mkChar("ldv")); + SET_STRING_ELT(names, 7, mkChar("ishift")); + SET_STRING_ELT(names, 8, mkChar("maxiter")); + SET_STRING_ELT(names, 9, mkChar("nb")); + SET_STRING_ELT(names, 10, mkChar("mode")); + SET_STRING_ELT(names, 11, mkChar("start")); + SET_STRING_ELT(names, 12, mkChar("sigma")); + SET_STRING_ELT(names, 13, mkChar("sigmai")); + SET_STRING_ELT(names, 14, mkChar("info")); + SET_STRING_ELT(names, 15, mkChar("iter")); + SET_STRING_ELT(names, 16, mkChar("nconv")); + SET_STRING_ELT(names, 17, mkChar("numop")); + SET_STRING_ELT(names, 18, mkChar("numopb")); + SET_STRING_ELT(names, 19, mkChar("numreo")); + SET_NAMES(result, names); + + UNPROTECT(2); + return result; +} + +int R_SEXP_to_igraph_eigen_which(SEXP in, igraph_eigen_which_t *out) { + SEXP pos=PROTECT(AS_CHARACTER(R_igraph_getListElement(in, "pos"))); + SEXP balance=PROTECT(AS_CHARACTER(R_igraph_getListElement + (in, "balance"))); + + if (!strcasecmp(CHAR(STRING_ELT(pos, 0)), "lm")) { + out->pos=IGRAPH_EIGEN_LM; + } else if (!strcasecmp(CHAR(STRING_ELT(pos, 0)), "sm")) { + out->pos=IGRAPH_EIGEN_SM; + } else if (!strcasecmp(CHAR(STRING_ELT(pos, 0)), "la")) { + out->pos=IGRAPH_EIGEN_LA; + } else if (!strcasecmp(CHAR(STRING_ELT(pos, 0)), "sa")) { + out->pos=IGRAPH_EIGEN_SA; + } else if (!strcasecmp(CHAR(STRING_ELT(pos, 0)), "be")) { + out->pos=IGRAPH_EIGEN_BE; + } else if (!strcasecmp(CHAR(STRING_ELT(pos, 0)), "lr")) { + out->pos=IGRAPH_EIGEN_LR; + } else if (!strcasecmp(CHAR(STRING_ELT(pos, 0)), "sr")) { + out->pos=IGRAPH_EIGEN_SR; + } else if (!strcasecmp(CHAR(STRING_ELT(pos, 0)), "li")) { + out->pos=IGRAPH_EIGEN_LI; + } else if (!strcasecmp(CHAR(STRING_ELT(pos, 0)), "si")) { + out->pos=IGRAPH_EIGEN_SI; + } else if (!strcasecmp(CHAR(STRING_ELT(pos, 0)), "all")) { + out->pos=IGRAPH_EIGEN_ALL; + } else if (!strcasecmp(CHAR(STRING_ELT(pos, 0)), "interval")) { + out->pos=IGRAPH_EIGEN_INTERVAL; + } else if (!strcasecmp(CHAR(STRING_ELT(pos, 0)), "select")) { + out->pos=IGRAPH_EIGEN_SELECT; + } else { + UNPROTECT(2); + IGRAPH_ERROR("Unknown eigenvalue position specification", + IGRAPH_EINVAL); + } + + out->howmany=INTEGER(AS_INTEGER(R_igraph_getListElement + (in, "howmany")))[0]; + out->il=INTEGER(AS_INTEGER(R_igraph_getListElement(in, "il")))[0]; + out->iu=INTEGER(AS_INTEGER(R_igraph_getListElement(in, "iu")))[0]; + out->vl=REAL(AS_NUMERIC(R_igraph_getListElement(in, "vl")))[0]; + out->vu=REAL(AS_NUMERIC(R_igraph_getListElement(in, "vu")))[0]; + out->vestimate=INTEGER(AS_INTEGER(R_igraph_getListElement + (in, "vestimate")))[0]; + + if (!strcasecmp(CHAR(STRING_ELT(balance, 0)), "none")) { + out->balance=IGRAPH_LAPACK_DGEEVX_BALANCE_NONE; + } else if (!strcasecmp(CHAR(STRING_ELT(balance, 0)), "perm")) { + out->balance=IGRAPH_LAPACK_DGEEVX_BALANCE_PERM; + } else if (!strcasecmp(CHAR(STRING_ELT(balance, 0)), "scale")) { + out->balance=IGRAPH_LAPACK_DGEEVX_BALANCE_SCALE; + } else if (!strcasecmp(CHAR(STRING_ELT(balance, 0)), "both")) { + out->balance=IGRAPH_LAPACK_DGEEVX_BALANCE_BOTH; + } else { + UNPROTECT(2); + IGRAPH_ERROR("Unknown balance specification", IGRAPH_EINVAL); + } + + UNPROTECT(2); + return 0; +} + +SEXP R_igraph_bliss_info_to_SEXP(const igraph_bliss_info_t *info) { + SEXP result, names; + + PROTECT(result=NEW_LIST(6)); + SET_VECTOR_ELT(result, 0, NEW_NUMERIC(1)); REAL(VECTOR_ELT(result, 0))[0]=info->nof_nodes; + SET_VECTOR_ELT(result, 1, NEW_NUMERIC(1)); REAL(VECTOR_ELT(result, 1))[0]=info->nof_leaf_nodes; + SET_VECTOR_ELT(result, 2, NEW_NUMERIC(1)); REAL(VECTOR_ELT(result, 2))[0]=info->nof_bad_nodes; + SET_VECTOR_ELT(result, 3, NEW_NUMERIC(1)); REAL(VECTOR_ELT(result, 3))[0]=info->nof_canupdates; + SET_VECTOR_ELT(result, 4, NEW_NUMERIC(1)); REAL(VECTOR_ELT(result, 4))[0]=info->max_level; + if (info->group_size) { + SET_VECTOR_ELT(result, 5, NEW_CHARACTER(1)); + SET_STRING_ELT(VECTOR_ELT(result, 5), 0, mkChar(info->group_size)); + } else { + SET_VECTOR_ELT(result, 5, R_NilValue); + } + + PROTECT(names=NEW_CHARACTER(6)); + SET_STRING_ELT(names, 0, mkChar("nof_nodes")); + SET_STRING_ELT(names, 1, mkChar("nof_leaf_nodes")); + SET_STRING_ELT(names, 2, mkChar("nof_bad_nodes")); + SET_STRING_ELT(names, 3, mkChar("nof_canupdates")); + SET_STRING_ELT(names, 4, mkChar("max_level")); + SET_STRING_ELT(names, 5, mkChar("group_size")); + SET_NAMES(result, names); + + UNPROTECT(2); + return result; +} + +/*******************************************************************/ + +SEXP R_igraph_mybracket(SEXP graph, SEXP pidx) { + int idx=INTEGER(pidx)[0]-1; + return duplicate(VECTOR_ELT(graph, idx)); +} + +SEXP R_igraph_mybracket2(SEXP graph, SEXP pidx1, SEXP pidx2) { + int idx1=INTEGER(pidx1)[0]-1; + int idx2=INTEGER(pidx2)[0]-1; + return duplicate(VECTOR_ELT(VECTOR_ELT(graph, idx1), idx2)); +} + +SEXP R_igraph_mybracket2_names(SEXP graph, SEXP pidx1, SEXP pidx2) { + SEXP result; + int idx1=INTEGER(pidx1)[0]-1; + int idx2=INTEGER(pidx2)[0]-1; + result=duplicate(GET_NAMES(VECTOR_ELT(VECTOR_ELT(graph, idx1), idx2))); + return result; +} + +SEXP R_igraph_mybracket2_copy(SEXP graph, SEXP pidx1, SEXP pidx2) { + int idx1=INTEGER(pidx1)[0]-1; + int idx2=INTEGER(pidx2)[0]-1; + return duplicate(VECTOR_ELT(VECTOR_ELT(graph, idx1), idx2)); +} + +SEXP R_igraph_mybracket2_set(SEXP graph, SEXP pidx1, SEXP pidx2, + SEXP value) { + SEXP newgraph; + int idx1=INTEGER(pidx1)[0]-1; + int idx2=INTEGER(pidx2)[0]-1; + PROTECT(newgraph=duplicate(graph)); + SET_VECTOR_ELT(VECTOR_ELT(newgraph, idx1), idx2, value); + UNPROTECT(1); + return newgraph; +} + +SEXP R_igraph_mybracket3_set(SEXP graph, SEXP pidx1, SEXP pidx2, + SEXP pname, SEXP value) { + SEXP newgraph; + int idx1=INTEGER(pidx1)[0]-1; + int idx2=INTEGER(pidx2)[0]-1; + const char *name=CHAR(STRING_ELT(pname, 0)); + SEXP attrs, names; + int i, n; + + PROTECT(newgraph=duplicate(graph)); + attrs=VECTOR_ELT(VECTOR_ELT(newgraph, idx1), idx2); + names=PROTECT(getAttrib(attrs, R_NamesSymbol)); + n=length(attrs); + + for (i=0; i 0 && igraph_vector_min(&w) < 0; + } + igraph_matrix_init(&res, 0, 0); + switch (algo) { + case 0: /* automatic */ + if (negw && mode==IGRAPH_OUT && GET_LENGTH(pvids)>100) { + igraph_shortest_paths_johnson(&g, &res, vs, to, pw); + } else if (negw) { + igraph_shortest_paths_bellman_ford(&g, &res, vs, to, pw, + (igraph_neimode_t) mode); + } else { + /* This one chooses 'unweighted' if there are no weights */ + igraph_shortest_paths_dijkstra(&g, &res, vs, to, pw, + (igraph_neimode_t) mode); + } + break; + case 1: /* unweighted */ + igraph_shortest_paths(&g, &res, vs, to, (igraph_neimode_t) mode); + break; + case 2: /* dijkstra */ + igraph_shortest_paths_dijkstra(&g, &res, vs, to, pw, + (igraph_neimode_t) mode); + break; + case 3: /* bellman-ford */ + igraph_shortest_paths_bellman_ford(&g, &res, vs, to, pw, + (igraph_neimode_t) mode); + break; + case 4: /* johnson */ + igraph_shortest_paths_johnson(&g, &res, vs, to, pw); + break; + } + PROTECT(result=R_igraph_matrix_to_SEXP(&res)); + igraph_matrix_destroy(&res); + igraph_vs_destroy(&vs); + + UNPROTECT(1); + return result; +} + +SEXP R_igraph_lattice(SEXP pdimvector, SEXP pnei, SEXP pdirected, + SEXP pmutual, SEXP pcircular) { + + igraph_t g; + igraph_vector_t dimvector; + igraph_integer_t nei=(igraph_integer_t) REAL(pnei)[0]; + igraph_bool_t directed=LOGICAL(pdirected)[0]; + igraph_bool_t mutual=LOGICAL(pmutual)[0]; + igraph_bool_t circular=LOGICAL(pcircular)[0]; + SEXP result; + + R_SEXP_to_vector(pdimvector, &dimvector); + + igraph_lattice(&g, &dimvector, nei, directed, mutual, circular); + PROTECT(result=R_igraph_to_SEXP(&g)); + igraph_destroy(&g); + + UNPROTECT(1); + return result; +} + +SEXP R_igraph_barabasi_game(SEXP pn, SEXP ppower, SEXP pm, SEXP poutseq, + SEXP poutpref, SEXP pA, SEXP pdirected, + SEXP palgo, SEXP pstart) { + + igraph_t g; + igraph_integer_t n=(igraph_integer_t) REAL(pn)[0]; + igraph_real_t power=REAL(ppower)[0]; + igraph_integer_t m=isNull(pm) ? 0 : (igraph_integer_t) REAL(pm)[0]; + igraph_vector_t outseq, *myoutseq=0; + igraph_bool_t outpref=LOGICAL(poutpref)[0]; + igraph_real_t A=REAL(pA)[0]; + igraph_bool_t directed=LOGICAL(pdirected)[0]; + igraph_barabasi_algorithm_t algo = (igraph_barabasi_algorithm_t) Rf_asInteger(palgo); + igraph_t start, *ppstart=0; + SEXP result; + + if (!isNull(poutseq)) { + R_SEXP_to_vector(poutseq, &outseq); + myoutseq=&outseq; + } + + if (!isNull(pstart)) { + R_SEXP_to_igraph(pstart, &start); + ppstart=&start; + } + + igraph_barabasi_game(&g, n, power, m, myoutseq, outpref, + A, directed, algo, ppstart); + PROTECT(result=R_igraph_to_SEXP(&g)); + igraph_destroy(&g); + + UNPROTECT(1); + return result; +} + +SEXP R_igraph_recent_degree_game(SEXP pn, SEXP ppower, SEXP pwindow, + SEXP pm, SEXP poutseq, SEXP poutpref, + SEXP pzero_appeal, + SEXP pdirected) { + igraph_t g; + igraph_integer_t n=(igraph_integer_t) REAL(pn)[0]; + igraph_real_t power=REAL(ppower)[0]; + igraph_integer_t window=(igraph_integer_t) REAL(pwindow)[0]; + igraph_integer_t m=(igraph_integer_t) REAL(pm)[0]; + igraph_vector_t outseq; + igraph_bool_t outpref=LOGICAL(poutpref)[0]; + igraph_bool_t directed=LOGICAL(pdirected)[0]; + igraph_real_t zero_appeal=REAL(pzero_appeal)[0]; + SEXP result; + + R_SEXP_to_vector(poutseq, &outseq); + + igraph_recent_degree_game(&g, n, power, window, m, &outseq, outpref, + zero_appeal, directed); + PROTECT(result=R_igraph_to_SEXP(&g)); + igraph_destroy(&g); + + UNPROTECT(1); + return result; +} + +SEXP R_igraph_layout_fruchterman_reingold(SEXP graph, SEXP coords, + SEXP niter, SEXP start_temp, + SEXP weights, + SEXP minx, SEXP maxx, + SEXP miny, SEXP maxy, SEXP grid) { + /* Declarations */ + igraph_t c_graph; + igraph_matrix_t c_coords; + igraph_integer_t c_niter; + igraph_real_t c_start_temp; + igraph_vector_t c_weights; + igraph_vector_t c_minx; + igraph_vector_t c_maxx; + igraph_vector_t c_miny; + igraph_vector_t c_maxy; + igraph_layout_grid_t c_grid=INTEGER(grid)[0]; + + SEXP result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (!isNull(coords)) { + if (0 != R_SEXP_to_igraph_matrix_copy(coords, &c_coords)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + } else { + igraph_matrix_init(&c_coords, 0, 0); + } + IGRAPH_FINALLY(igraph_matrix_destroy, &c_coords); + c_niter=INTEGER(niter)[0]; + c_start_temp=REAL(start_temp)[0]; + if (!isNull(weights)) { R_SEXP_to_vector(weights, &c_weights); } + if (!isNull(minx)) { R_SEXP_to_vector(minx, &c_minx); } + if (!isNull(maxx)) { R_SEXP_to_vector(maxx, &c_maxx); } + if (!isNull(miny)) { R_SEXP_to_vector(miny, &c_miny); } + if (!isNull(maxy)) { R_SEXP_to_vector(maxy, &c_maxy); } + /* Call igraph */ + igraph_layout_fruchterman_reingold(&c_graph, &c_coords, + !isNull(coords), + c_niter, c_start_temp, c_grid, + (isNull(weights) ? 0 : &c_weights), + (isNull(minx) ? 0 : &c_minx), + (isNull(maxx) ? 0 : &c_maxx), + (isNull(miny) ? 0 : &c_miny), + (isNull(maxy) ? 0 : &c_maxy)); + + /* Convert output */ + PROTECT(coords=R_igraph_matrix_to_SEXP(&c_coords)); + igraph_matrix_destroy(&c_coords); + IGRAPH_FINALLY_CLEAN(1); + result=coords; + + UNPROTECT(1); + return(result); +} + +SEXP R_igraph_layout_fruchterman_reingold_3d(SEXP graph, SEXP coords, + SEXP niter, SEXP start_temp, + SEXP weights, + SEXP minx, SEXP maxx, + SEXP miny, SEXP maxy, + SEXP minz, SEXP maxz) { + /* Declarations */ + igraph_t c_graph; + igraph_matrix_t c_coords; + igraph_integer_t c_niter; + igraph_real_t c_start_temp; + igraph_vector_t c_weights; + igraph_vector_t c_minx; + igraph_vector_t c_maxx; + igraph_vector_t c_miny; + igraph_vector_t c_maxy; + igraph_vector_t c_minz; + igraph_vector_t c_maxz; + + SEXP result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (!isNull(coords)) { + if (0 != R_SEXP_to_igraph_matrix_copy(coords, &c_coords)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + } else { + igraph_matrix_init(&c_coords, 0, 0); + } + IGRAPH_FINALLY(igraph_matrix_destroy, &c_coords); + c_niter=INTEGER(niter)[0]; + c_start_temp=REAL(start_temp)[0]; + if (!isNull(weights)) { R_SEXP_to_vector(weights, &c_weights); } + if (!isNull(minx)) { R_SEXP_to_vector(minx, &c_minx); } + if (!isNull(maxx)) { R_SEXP_to_vector(maxx, &c_maxx); } + if (!isNull(miny)) { R_SEXP_to_vector(miny, &c_miny); } + if (!isNull(maxy)) { R_SEXP_to_vector(maxy, &c_maxy); } + if (!isNull(minz)) { R_SEXP_to_vector(minz, &c_minz); } + if (!isNull(maxz)) { R_SEXP_to_vector(maxz, &c_maxz); } + /* Call igraph */ + igraph_layout_fruchterman_reingold_3d(&c_graph, &c_coords, !isNull(coords), + c_niter, c_start_temp, + (isNull(weights) ? 0 : &c_weights), + (isNull(minx) ? 0 : &c_minx), + (isNull(maxx) ? 0 : &c_maxx), + (isNull(miny) ? 0 : &c_miny), + (isNull(maxy) ? 0 : &c_maxy), + (isNull(minz) ? 0 : &c_minz), + (isNull(maxz) ? 0 : &c_maxz)); + + /* Convert output */ + PROTECT(coords=R_igraph_matrix_to_SEXP(&c_coords)); + igraph_matrix_destroy(&c_coords); + IGRAPH_FINALLY_CLEAN(1); + result=coords; + + UNPROTECT(1); + return(result); +} + +SEXP R_igraph_layout_kamada_kawai(SEXP graph, SEXP coords, SEXP maxiter, + SEXP epsilon, SEXP kkconst, SEXP weights, + SEXP minx, SEXP maxx, + SEXP miny, SEXP maxy) { + + /* Declarations */ + igraph_t c_graph; + igraph_matrix_t c_coords; + igraph_integer_t c_maxiter; + igraph_real_t c_epsilon; + igraph_real_t c_kkconst; + igraph_vector_t c_weights; + igraph_vector_t c_minx; + igraph_vector_t c_maxx; + igraph_vector_t c_miny; + igraph_vector_t c_maxy; + + SEXP result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (!isNull(coords)) { + if (0 != R_SEXP_to_igraph_matrix_copy(coords, &c_coords)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + } else { + igraph_matrix_init(&c_coords, 0, 0); + } + IGRAPH_FINALLY(igraph_matrix_destroy, &c_coords); + c_maxiter=INTEGER(maxiter)[0]; + c_epsilon=REAL(epsilon)[0]; + c_kkconst=REAL(kkconst)[0]; + if (!isNull(weights)) { R_SEXP_to_vector(weights, &c_weights); } + if (!isNull(minx)) { R_SEXP_to_vector(minx, &c_minx); } + if (!isNull(maxx)) { R_SEXP_to_vector(maxx, &c_maxx); } + if (!isNull(miny)) { R_SEXP_to_vector(miny, &c_miny); } + if (!isNull(maxy)) { R_SEXP_to_vector(maxy, &c_maxy); } + /* Call igraph */ + igraph_layout_kamada_kawai(&c_graph, &c_coords, !isNull(coords), + c_maxiter, c_epsilon, c_kkconst, + (isNull(weights) ? 0 : &c_weights), + (isNull(minx) ? 0 : &c_minx), + (isNull(maxx) ? 0 : &c_maxx), + (isNull(miny) ? 0 : &c_miny), + (isNull(maxy) ? 0 : &c_maxy)); + + /* Convert output */ + PROTECT(coords=R_igraph_matrix_to_SEXP(&c_coords)); + igraph_matrix_destroy(&c_coords); + IGRAPH_FINALLY_CLEAN(1); + result=coords; + + UNPROTECT(1); + return(result); +} + +SEXP R_igraph_layout_kamada_kawai_3d(SEXP graph, SEXP coords, SEXP maxiter, + SEXP epsilon, SEXP kkconst, + SEXP weights, + SEXP minx, SEXP maxx, + SEXP miny, SEXP maxy, + SEXP minz, SEXP maxz) { + + /* Declarations */ + igraph_t c_graph; + igraph_matrix_t c_coords; + igraph_integer_t c_maxiter; + igraph_real_t c_epsilon; + igraph_real_t c_kkconst; + igraph_vector_t c_weights; + igraph_vector_t c_minx; + igraph_vector_t c_maxx; + igraph_vector_t c_miny; + igraph_vector_t c_maxy; + igraph_vector_t c_minz; + igraph_vector_t c_maxz; + + SEXP result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (!isNull(coords)) { + if (0 != R_SEXP_to_igraph_matrix_copy(coords, &c_coords)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + } else { + igraph_matrix_init(&c_coords, 0, 0); + } + IGRAPH_FINALLY(igraph_matrix_destroy, &c_coords); + c_maxiter=INTEGER(maxiter)[0]; + c_epsilon=REAL(epsilon)[0]; + c_kkconst=REAL(kkconst)[0]; + if (!isNull(weights)) { R_SEXP_to_vector(weights, &c_weights); } + if (!isNull(minx)) { R_SEXP_to_vector(minx, &c_minx); } + if (!isNull(maxx)) { R_SEXP_to_vector(maxx, &c_maxx); } + if (!isNull(miny)) { R_SEXP_to_vector(miny, &c_miny); } + if (!isNull(maxy)) { R_SEXP_to_vector(maxy, &c_maxy); } + if (!isNull(minz)) { R_SEXP_to_vector(minz, &c_minz); } + if (!isNull(maxz)) { R_SEXP_to_vector(maxz, &c_maxz); } + /* Call igraph */ + igraph_layout_kamada_kawai_3d(&c_graph, &c_coords, !isNull(coords), + c_maxiter, c_epsilon, c_kkconst, + (isNull(weights) ? 0 : &c_weights), + (isNull(minx) ? 0 : &c_minx), + (isNull(maxx) ? 0 : &c_maxx), + (isNull(miny) ? 0 : &c_miny), + (isNull(maxy) ? 0 : &c_maxy), + (isNull(minz) ? 0 : &c_minz), + (isNull(maxz) ? 0 : &c_maxz)); + + /* Convert output */ + PROTECT(coords=R_igraph_matrix_to_SEXP(&c_coords)); + igraph_matrix_destroy(&c_coords); + IGRAPH_FINALLY_CLEAN(1); + result=coords; + + UNPROTECT(1); + return(result); +} + +SEXP R_igraph_layout_graphopt(SEXP graph, SEXP pniter, SEXP pcharge, + SEXP pmass, SEXP pspring_length, + SEXP pspring_constant, SEXP pmax_sa_movement, + SEXP start) { + igraph_t g; + igraph_integer_t niter=(igraph_integer_t) REAL(pniter)[0]; + igraph_real_t charge=REAL(pcharge)[0]; + igraph_real_t mass=REAL(pmass)[0]; + igraph_real_t spring_length=REAL(pspring_length)[0]; + igraph_real_t spring_constant=REAL(pspring_constant)[0]; + igraph_real_t max_sa_movement=REAL(pmax_sa_movement)[0]; + igraph_matrix_t res; + SEXP result; + + R_SEXP_to_igraph(graph, &g); + if (isNull(start)) { + igraph_matrix_init(&res, 0, 0); + } else { + R_SEXP_to_igraph_matrix_copy(start, &res); + } + igraph_layout_graphopt(&g, &res, niter, charge, mass, spring_length, + spring_constant, max_sa_movement, !isNull(start)); + PROTECT(result=R_igraph_matrix_to_SEXP(&res)); + igraph_matrix_destroy(&res); + + UNPROTECT(1); + return result; +} + +SEXP R_igraph_layout_lgl(SEXP graph, SEXP pmaxiter, SEXP pmaxdelta, + SEXP parea, SEXP pcoolexp, SEXP prepulserad, + SEXP pcellsize, SEXP proot) { + + igraph_t g; + igraph_matrix_t res; + igraph_integer_t maxiter=(igraph_integer_t) REAL(pmaxiter)[0]; + igraph_real_t maxdelta=REAL(pmaxdelta)[0]; + igraph_real_t area=REAL(parea)[0]; + igraph_real_t coolexp=REAL(pcoolexp)[0]; + igraph_real_t repulserad=REAL(prepulserad)[0]; + igraph_real_t cellsize=REAL(pcellsize)[0]; + igraph_integer_t root=(igraph_integer_t) REAL(proot)[0]; + SEXP result; + + R_SEXP_to_igraph(graph, &g); + igraph_matrix_init(&res, 0, 0); + igraph_layout_lgl(&g, &res, maxiter, maxdelta, area, coolexp, repulserad, + cellsize, root); + PROTECT(result=R_igraph_matrix_to_SEXP(&res)); + igraph_matrix_destroy(&res); + + UNPROTECT(1); + return result; +} + +SEXP R_igraph_minimum_spanning_tree_unweighted(SEXP graph) { + + igraph_t g; + igraph_t mst; + SEXP result; + + R_SEXP_to_igraph(graph, &g); + igraph_minimum_spanning_tree_unweighted(&g, &mst); + PROTECT(result=R_igraph_to_SEXP(&mst)); + igraph_destroy(&mst); + + UNPROTECT(1); + return result; +} + +SEXP R_igraph_minimum_spanning_tree_prim(SEXP graph, SEXP pweights) { + + igraph_t g; + igraph_t mst; + igraph_vector_t weights; + SEXP result; + + R_SEXP_to_vector(pweights, &weights); + + R_SEXP_to_igraph(graph, &g); + igraph_minimum_spanning_tree_prim(&g, &mst, &weights); + PROTECT(result=R_igraph_to_SEXP(&mst)); + igraph_destroy(&mst); + + UNPROTECT(1); + return result; +} + +SEXP R_igraph_get_shortest_paths(SEXP graph, SEXP pfrom, SEXP pto, + SEXP pmode, SEXP pno, SEXP weights, + SEXP output, SEXP ppred, SEXP pinbound, + SEXP palgo) { + + igraph_t g; + igraph_integer_t from=(igraph_integer_t) REAL(pfrom)[0]; + igraph_vs_t to; + igraph_integer_t mode=(igraph_integer_t) REAL(pmode)[0]; + igraph_vector_t *vects, *evects; + long int i; + igraph_vector_ptr_t ptrvec, ptrevec; + igraph_vector_t w, *pw=&w; + igraph_bool_t negw=0; + SEXP result, result1, result2, names; + igraph_bool_t verts=REAL(output)[0]==0 || REAL(output)[0]==2; + igraph_bool_t edges=REAL(output)[0]==1 || REAL(output)[0]==2; + igraph_bool_t pred=LOGICAL(ppred)[0]; + igraph_bool_t inbound=LOGICAL(pinbound)[0]; + long int algo=(long int) REAL(palgo)[0]; + igraph_vector_long_t predvec, inboundvec; + + long int no=(long int) REAL(pno)[0]; + + R_SEXP_to_igraph(graph, &g); + R_SEXP_to_igraph_vs(pto, &g, &to); + + if (verts) { + igraph_vector_ptr_init(&ptrvec, no); + vects=(igraph_vector_t*) R_alloc((size_t) GET_LENGTH(pto), + sizeof(igraph_vector_t)); + for (i=0; i 0 && igraph_vector_min(&w) < 0; + } + + if (pred) { igraph_vector_long_init(&predvec, no); } + if (inbound) { igraph_vector_long_init(&inboundvec, no); } + + switch (algo) { + case 0: /* automatic */ + if (negw) { + igraph_get_shortest_paths_bellman_ford(&g, + verts ? &ptrvec : 0, + edges ? &ptrevec : 0, + from, to, pw, (igraph_neimode_t) mode, + pred ? &predvec : 0, + inbound ? &inboundvec : 0); + } else { + /* This one chooses 'unweighted' if there are no weights */ + igraph_get_shortest_paths_dijkstra(&g, + verts ? &ptrvec : 0, + edges ? &ptrevec : 0, + from, to, pw, (igraph_neimode_t) mode, + pred ? &predvec : 0, + inbound ? &inboundvec : 0); + } + break; + case 1: /* unweighted */ + igraph_get_shortest_paths(&g, + verts ? &ptrvec : 0, + edges ? &ptrevec : 0, + from, to, (igraph_neimode_t) mode, + pred ? &predvec : 0, + inbound ? &inboundvec : 0); + break; + case 2: /* dijkstra */ + igraph_get_shortest_paths_dijkstra(&g, + verts ? &ptrvec : 0, + edges ? &ptrevec : 0, + from, to, pw, (igraph_neimode_t) mode, + pred ? &predvec : 0, + inbound ? &inboundvec : 0); + break; + case 3: /* bellman-ford */ + igraph_get_shortest_paths_bellman_ford(&g, + verts ? &ptrvec : 0, + edges ? &ptrevec : 0, + from, to, pw, (igraph_neimode_t) mode, + pred ? &predvec : 0, + inbound ? &inboundvec : 0); + break; + } + + PROTECT(result=NEW_LIST(4)); + if (verts) { + SET_VECTOR_ELT(result, 0, NEW_LIST(no)); + result1=VECTOR_ELT(result, 0); + for (i=0; i0) { + R_igraph_SEXP_to_strvector(ppredef, &predef); + predefptr=&predef; + } + igraph_read_graph_ncol(&g, file, predefptr, names, weights, directed); + fclose(file); + PROTECT(result=R_igraph_to_SEXP(&g)); + igraph_destroy(&g); + + UNPROTECT(1); + return result; +} + +SEXP R_igraph_write_graph_ncol(SEXP graph, SEXP file, SEXP pnames, + SEXP pweights) { + igraph_t g; + FILE *stream; +#if HAVE_OPEN_MEMSTREAM == 1 + char *bp; + size_t size; +#endif + const char *names, *weights; + SEXP result; + + if (isNull(pnames)) { + names=0; + } else { + names=CHAR(STRING_ELT(pnames, 0)); + } + if (isNull(pweights)) { + weights=0; + } else { + weights=CHAR(STRING_ELT(pweights, 0)); + } + + R_SEXP_to_igraph(graph, &g); +#if HAVE_OPEN_MEMSTREAM == 1 + stream=open_memstream(&bp, &size); +#else + stream=fopen(CHAR(STRING_ELT(file,0)), "w"); +#endif + if (stream==0) { igraph_error("Cannot write .ncol file", __FILE__, __LINE__, + IGRAPH_EFILE); } + igraph_write_graph_ncol(&g, stream, names, weights); + fclose(stream); +#if HAVE_OPEN_MEMSTREAM == 1 + PROTECT(result=allocVector(RAWSXP, size)); + memcpy(RAW(result), bp, sizeof(char)*size); + free(bp); +#else + PROTECT(result=NEW_NUMERIC(0)); +#endif + + UNPROTECT(1); + return result; +} + +SEXP R_igraph_read_graph_lgl(SEXP pvfile, SEXP pnames, SEXP pweights, SEXP pdirected) { + igraph_t g; + igraph_bool_t names=LOGICAL(pnames)[0]; + igraph_add_weights_t weights=REAL(pweights)[0]; + igraph_bool_t directed=LOGICAL(pdirected)[0]; + FILE *file; + SEXP result; + +#if HAVE_FMEMOPEN == 1 + file=fmemopen(RAW(pvfile), GET_LENGTH(pvfile), "r"); +#else + file=fopen(CHAR(STRING_ELT(pvfile, 0)), "r"); +#endif + if (file==0) { igraph_error("Cannot read edgelist", __FILE__, __LINE__, + IGRAPH_EFILE); } + igraph_read_graph_lgl(&g, file, names, weights, directed); + fclose(file); + PROTECT(result=R_igraph_to_SEXP(&g)); + igraph_destroy(&g); + + UNPROTECT(1); + return result; +} + +SEXP R_igraph_write_graph_lgl(SEXP graph, SEXP file, SEXP pnames, + SEXP pweights, SEXP pisolates) { + igraph_t g; + FILE *stream; +#if HAVE_OPEN_MEMSTREAM == 1 + char *bp; + size_t size; +#endif + const char *names, *weights; + igraph_bool_t isolates=LOGICAL(pisolates)[0]; + SEXP result; + + if (isNull(pnames)) { + names=0; + } else { + names=CHAR(STRING_ELT(pnames, 0)); + } + if (isNull(pweights)) { + weights=0; + } else { + weights=CHAR(STRING_ELT(pweights, 0)); + } + + R_SEXP_to_igraph(graph, &g); +#if HAVE_OPEN_MEMSTREAM == 1 + stream=open_memstream(&bp, &size); +#else + stream=fopen(CHAR(STRING_ELT(file, 0)), "w"); +#endif + igraph_write_graph_lgl(&g, stream, names, weights, isolates); + fclose(stream); +#if HAVE_OPEN_MEMSTREAM == 1 + PROTECT(result=allocVector(RAWSXP, size)); + memcpy(RAW(result), bp, sizeof(char)*size); + free(bp); +#else + PROTECT(result=NEW_NUMERIC(0)); +#endif + + UNPROTECT(1); + return result; +} + +SEXP R_igraph_read_graph_pajek(SEXP pvfile) { + igraph_t g; + FILE *file; + SEXP result; + +#if HAVE_FMEMOPEN == 1 + file=fmemopen(RAW(pvfile), GET_LENGTH(pvfile), "r"); +#else + file=fopen(CHAR(STRING_ELT(pvfile, 0)), "r"); +#endif + if (file==0) { igraph_error("Cannot read Pajek file", __FILE__, __LINE__, + IGRAPH_EFILE); } + igraph_read_graph_pajek(&g, file); + fclose(file); + PROTECT(result=R_igraph_to_SEXP(&g)); + igraph_destroy(&g); + + UNPROTECT(1); + return result; +} + +SEXP R_igraph_decompose(SEXP graph, SEXP pmode, SEXP pmaxcompno, + SEXP pminelements) { + + igraph_t g; + igraph_connectedness_t mode = (igraph_connectedness_t) Rf_asInteger(pmode); + igraph_integer_t maxcompno=(igraph_integer_t) REAL(pmaxcompno)[0]; + igraph_integer_t minelements=(igraph_integer_t) REAL(pminelements)[0]; + igraph_vector_ptr_t comps; + SEXP result; + long int i; + + R_PreserveObject(R_igraph_attribute_protected=NEW_LIST(100)); + R_igraph_attribute_protected_size=0; + IGRAPH_FINALLY(R_igraph_attribute_protected_destroy, 0); + + R_SEXP_to_igraph(graph, &g); + igraph_vector_ptr_init(&comps, 0); + IGRAPH_FINALLY(igraph_vector_ptr_destroy, &comps); + igraph_decompose(&g, &comps, mode, maxcompno, minelements); + PROTECT(result=NEW_LIST(igraph_vector_ptr_size(&comps))); + for (i=0; ifun, s_from, data->extra)); + PROTECT(s_to = eval(R_fcall, data->rho)); + memcpy(to, REAL(s_to), sizeof(igraph_real_t) * (size_t) n); + + UNPROTECT(3); + return 0; +} + +SEXP R_igraph_arpack(SEXP function, SEXP extra, SEXP options, SEXP rho, + SEXP sym) { + + igraph_vector_t values; + igraph_matrix_t vectors, values2; + R_igraph_i_arpack_data_t data; + igraph_arpack_options_t c_options; + SEXP result, names; + + if (0 != igraph_matrix_init(&vectors, 0, 0)) { + igraph_error("Cannot run ARPACK", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_matrix_destroy, &vectors); + if (LOGICAL(sym)[0]) { + if (0 != igraph_vector_init(&values, 0)) { + igraph_error("Cannot run ARPACK", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &values); + } else { + if (0 != igraph_matrix_init(&values2, 0, 0)) { + igraph_error("Cannot run ARPACK", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_matrix_destroy, &values2); + } + + data.fun=function; + data.extra=extra; + data.rho=rho; + + R_SEXP_to_igraph_arpack_options(options, &c_options); + if (LOGICAL(sym)[0]) { + if (0 != igraph_arpack_rssolve(R_igraph_i_arpack_callback, &data, + &c_options, 0, &values, &vectors)) { + igraph_error("ARPACK failed", __FILE__, __LINE__, IGRAPH_FAILURE); + } + } else { + if (0 != igraph_arpack_rnsolve(R_igraph_i_arpack_callback, &data, + &c_options, 0, &values2, &vectors)) { + igraph_error("ARPACK failed", __FILE__, __LINE__, IGRAPH_FAILURE); + } + } + + PROTECT(result=NEW_LIST(3)); + if (LOGICAL(sym)[0]) { + SET_VECTOR_ELT(result, 0, R_igraph_vector_to_SEXP(&values)); + igraph_vector_destroy(&values); IGRAPH_FINALLY_CLEAN(1); + } else { + SET_VECTOR_ELT(result, 0, R_igraph_matrix_to_SEXP(&values2)); + igraph_matrix_destroy(&values2); IGRAPH_FINALLY_CLEAN(1); + } + SET_VECTOR_ELT(result, 1, R_igraph_matrix_to_SEXP(&vectors)); + igraph_matrix_destroy(&vectors); IGRAPH_FINALLY_CLEAN(1); + SET_VECTOR_ELT(result, 2, R_igraph_arpack_options_to_SEXP(&c_options)); + PROTECT(names=NEW_CHARACTER(3)); + SET_STRING_ELT(names, 0, mkChar("values")); + SET_STRING_ELT(names, 1, mkChar("vectors")); + SET_STRING_ELT(names, 2, mkChar("options")); + SET_NAMES(result, names); + + UNPROTECT(2); + return result; +} + +SEXP R_igraph_is_chordal(SEXP graph, SEXP alpha, SEXP alpham1, + SEXP pfillin, SEXP pnewgraph) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_t c_alpha; + igraph_vector_t c_alpham1; + igraph_bool_t c_chordal; + igraph_vector_t c_fillin; + igraph_t c_newgraph; + SEXP chordal; + SEXP fillin; + SEXP newgraph; + SEXP result, names; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (!isNull(alpha)) { R_SEXP_to_vector(alpha, &c_alpha); } + if (!isNull(alpham1)) { R_SEXP_to_vector(alpham1, &c_alpham1); } + if (LOGICAL(pfillin)[0]) { + if (0 != igraph_vector_init(&c_fillin, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_fillin); + } + igraph_is_chordal(&c_graph, (isNull(alpha) ? 0 : &c_alpha), + (isNull(alpham1) ? 0 : &c_alpham1), &c_chordal, + (LOGICAL(pfillin)[0] ? &c_fillin : 0), + (LOGICAL(pnewgraph)[0] ? &c_newgraph : 0)); + + /* Convert output */ + PROTECT(result=NEW_LIST(3)); + PROTECT(names=NEW_CHARACTER(3)); + PROTECT(chordal=NEW_LOGICAL(1)); + LOGICAL(chordal)[0]=c_chordal; + if (LOGICAL(pfillin)[0]) { + PROTECT(fillin=R_igraph_vector_to_SEXP(&c_fillin)); + igraph_vector_destroy(&c_fillin); + IGRAPH_FINALLY_CLEAN(1); + } else { + PROTECT(fillin=R_NilValue); + } + if (LOGICAL(pnewgraph)[0]) { + IGRAPH_FINALLY(igraph_destroy, &c_newgraph); + PROTECT(newgraph=R_igraph_to_SEXP(&c_newgraph)); + igraph_destroy(&c_newgraph); + IGRAPH_FINALLY_CLEAN(1); + } else { + PROTECT(newgraph=R_NilValue); + } + SET_VECTOR_ELT(result, 0, chordal); + SET_VECTOR_ELT(result, 1, fillin); + SET_VECTOR_ELT(result, 2, newgraph); + SET_STRING_ELT(names, 0, CREATE_STRING_VECTOR("chordal")); + SET_STRING_ELT(names, 1, CREATE_STRING_VECTOR("fillin")); + SET_STRING_ELT(names, 2, CREATE_STRING_VECTOR("newgraph")); + SET_NAMES(result, names); + UNPROTECT(4); + + UNPROTECT(1); + return(result); +} + +typedef struct { + SEXP graph, fun, extra, rho; +} R_igraph_i_bfs_data_t; + +igraph_bool_t R_igraph_bfshandler(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) { + + R_igraph_i_bfs_data_t *data=extra; + SEXP args, R_fcall, result, names; + igraph_bool_t cres; + + PROTECT(args=NEW_NUMERIC(5)); + PROTECT(names=NEW_CHARACTER(5)); + + SET_STRING_ELT(names, 0, mkChar("vid")); + SET_STRING_ELT(names, 1, mkChar("pred")); + SET_STRING_ELT(names, 2, mkChar("succ")); + SET_STRING_ELT(names, 3, mkChar("rank")); + SET_STRING_ELT(names, 4, mkChar("dist")); + REAL(args)[0]=vid + 1; + REAL(args)[1]=pred + 1; + REAL(args)[2]=succ + 1; + REAL(args)[3]=rank + 1; + REAL(args)[4]=dist; + SET_NAMES(args, names); + + PROTECT(R_fcall = lang4(data->fun, data->graph, args, data->extra)); + PROTECT(result = eval(R_fcall, data->rho)); + + cres=Rf_asLogical(result); + + UNPROTECT(4); + return cres; +} + +SEXP R_igraph_bfs(SEXP graph, SEXP proot, SEXP proots, SEXP pneimode, + SEXP punreachable, SEXP prestricted, + SEXP porder, SEXP prank, SEXP pfather, + SEXP ppred, SEXP psucc, SEXP pdist, + SEXP pcallback, SEXP pextra, SEXP prho) { + + igraph_t g; + SEXP result, names; + + igraph_integer_t root=(igraph_integer_t) REAL(proot)[0]; + igraph_vector_t roots; + igraph_bool_t unreachable=LOGICAL(punreachable)[0]; + igraph_vector_t restricted; + igraph_integer_t neimode=(igraph_integer_t) REAL(pneimode)[0]; + igraph_vector_t order, rank, father, pred, succ, dist; + igraph_vector_t *p_order=0, *p_rank=0, *p_father=0, *p_pred=0, + *p_succ=0, *p_dist=0; + igraph_bfshandler_t *callback=0; + R_igraph_i_bfs_data_t cb_data, *p_cb_data=0; + + R_SEXP_to_igraph(graph, &g); + if (!isNull(proots)) { + R_SEXP_to_vector(proots, &roots); + } + if (!isNull(prestricted)) { + R_SEXP_to_vector(prestricted, &restricted); + } + + if (LOGICAL(porder)[0]) { + igraph_vector_init(&order, 0); p_order=ℴ + } + if (LOGICAL(prank)[0]) { + igraph_vector_init(&rank, 0); p_rank=&rank; + } + if (LOGICAL(pfather)[0]) { + igraph_vector_init(&father, 0); p_father=&father; + } + if (LOGICAL(ppred)[0]) { + igraph_vector_init(&pred, 0); p_pred=&pred; + } + if (LOGICAL(psucc)[0]) { + igraph_vector_init(&succ, 0); p_succ=≻ + } + if (LOGICAL(pdist)[0]) { + igraph_vector_init(&dist, 0); p_dist=&dist; + } + + if (!isNull(pcallback)) { + cb_data.graph=graph; + cb_data.fun=pcallback; + cb_data.extra=pextra; + cb_data.rho=prho; + callback=R_igraph_bfshandler; + p_cb_data = &cb_data; + } + + igraph_bfs(&g, root, isNull(proots) ? 0 : &roots, (igraph_neimode_t) neimode, + unreachable, isNull(prestricted) ? 0 : &restricted, + p_order, p_rank, p_father, p_pred, p_succ, p_dist, + (igraph_bfshandler_t*) callback, p_cb_data); + + PROTECT(result=NEW_LIST(8)); + PROTECT(names=NEW_CHARACTER(8)); + + SET_STRING_ELT(names, 0, mkChar("root")); + SET_VECTOR_ELT(result, 0, NEW_NUMERIC(1)); + REAL(VECTOR_ELT(result, 0))[0] = root+1; + + SET_STRING_ELT(names, 1, mkChar("mode")); + SET_VECTOR_ELT(result, 1, NEW_CHARACTER(1)); + if (neimode==1) { + SET_STRING_ELT(VECTOR_ELT(result, 1), 0, mkChar("out")); + } else if (neimode==2) { + SET_STRING_ELT(VECTOR_ELT(result, 1), 0, mkChar("in")); + } else { + SET_STRING_ELT(VECTOR_ELT(result, 1), 0, mkChar("all")); + } + + SET_STRING_ELT(names, 2, mkChar("order")); + SET_VECTOR_ELT(result, 2, R_igraph_0orvector_to_SEXP_d(p_order)); + SET_STRING_ELT(names, 3, mkChar("rank")); + SET_VECTOR_ELT(result, 3, R_igraph_0orvector_to_SEXP_d(p_rank)); + SET_STRING_ELT(names, 4, mkChar("father")); + SET_VECTOR_ELT(result, 4, R_igraph_0orvector_to_SEXP_d(p_father)); + SET_STRING_ELT(names, 5, mkChar("pred")); + SET_VECTOR_ELT(result, 5, R_igraph_0orvector_to_SEXP_d(p_pred)); + SET_STRING_ELT(names, 6, mkChar("succ")); + SET_VECTOR_ELT(result, 6, R_igraph_0orvector_to_SEXP_d(p_succ)); + SET_STRING_ELT(names, 7, mkChar("dist")); + SET_VECTOR_ELT(result, 7, R_igraph_0orvector_to_SEXP_d(p_dist)); + + SET_NAMES(result, names); + + UNPROTECT(2); + return result; +} + +typedef struct { + SEXP graph, fun_in, fun_out, extra, rho; +} R_igraph_i_dfs_data_t; + +igraph_bool_t R_igraph_dfshandler(const igraph_t *graph, + igraph_integer_t vid, + igraph_integer_t dist, + void *extra, + int which) { + + R_igraph_i_dfs_data_t *data=extra; + SEXP args, R_fcall, result, names; + igraph_bool_t cres; + + PROTECT(args=NEW_NUMERIC(2)); + PROTECT(names=NEW_CHARACTER(2)); + + SET_STRING_ELT(names, 0, mkChar("vid")); + SET_STRING_ELT(names, 1, mkChar("dist")); + REAL(args)[0]=vid + 1; + REAL(args)[1]=dist; + SET_NAMES(args, names); + + PROTECT(R_fcall = lang4(which==0 ? data->fun_in : data->fun_out, + data->graph, args, data->extra)); + PROTECT(result = eval(R_fcall, data->rho)); + + cres=Rf_asLogical(result); + + UNPROTECT(4); + return cres; +} + +igraph_bool_t R_igraph_dfshandler_in(const igraph_t *graph, + igraph_integer_t vid, + igraph_integer_t dist, + void *extra) { + + return R_igraph_dfshandler(graph, vid, dist, extra, 0); +} + + +igraph_bool_t R_igraph_dfshandler_out(const igraph_t *graph, + igraph_integer_t vid, + igraph_integer_t dist, + void *extra) { + + return R_igraph_dfshandler(graph, vid, dist, extra, 1); +} + +SEXP R_igraph_dfs(SEXP graph, SEXP proot, SEXP pneimode, SEXP punreachable, + SEXP porder, SEXP porder_out, SEXP pfather, SEXP pdist, + SEXP pin_callback, SEXP pout_callback, + SEXP pextra, SEXP prho) { + + igraph_t g; + SEXP result, names; + + igraph_integer_t root=(igraph_integer_t) REAL(proot)[0]; + igraph_integer_t neimode=(igraph_integer_t) REAL(pneimode)[0]; + igraph_bool_t unreachable=LOGICAL(punreachable)[0]; + igraph_vector_t order, order_out, father, dist; + igraph_vector_t *p_order=0, *p_order_out=0, *p_father=0, *p_dist=0; + igraph_dfshandler_t *in_callback=0, *out_callback=0; + R_igraph_i_dfs_data_t cb_data, *p_cb_data=0; + + R_SEXP_to_igraph(graph, &g); + + if (LOGICAL(porder)[0]) { + igraph_vector_init(&order, 0); p_order=ℴ + } + if (LOGICAL(porder_out)[0]) { + igraph_vector_init(&order_out, 0); p_order_out=&order_out; + } + if (LOGICAL(pfather)[0]) { + igraph_vector_init(&father, 0); p_father=&father; + } + if (LOGICAL(pdist)[0]) { + igraph_vector_init(&dist, 0); p_dist=&dist; + } + + if (!isNull(pin_callback) || !isNull(pout_callback)) { + cb_data.graph=graph; + cb_data.fun_in=pin_callback; + cb_data.fun_out=pout_callback; + cb_data.extra=pextra; + cb_data.rho=prho; + p_cb_data = &cb_data; + } + if (!isNull(pin_callback)) { + in_callback=R_igraph_dfshandler_in; + } + if (!isNull(pout_callback)) { + out_callback=R_igraph_dfshandler_out; + } + + igraph_dfs(&g, root, (igraph_neimode_t) neimode, unreachable, + p_order, p_order_out, p_father, p_dist, + (igraph_dfshandler_t*) in_callback, + (igraph_dfshandler_t*) out_callback, p_cb_data); + + PROTECT(result=NEW_LIST(6)); + PROTECT(names=NEW_CHARACTER(6)); + + SET_STRING_ELT(names, 0, mkChar("root")); + SET_VECTOR_ELT(result, 0, NEW_NUMERIC(1)); + REAL(VECTOR_ELT(result, 0))[0] = root; + + SET_STRING_ELT(names, 1, mkChar("mode")); + SET_VECTOR_ELT(result, 1, NEW_CHARACTER(1)); + if (neimode==1) { + SET_STRING_ELT(VECTOR_ELT(result, 1), 0, mkChar("out")); + } else if (neimode==2) { + SET_STRING_ELT(VECTOR_ELT(result, 1), 0, mkChar("in")); + } else { + SET_STRING_ELT(VECTOR_ELT(result, 1), 0, mkChar("all")); + } + + SET_STRING_ELT(names, 2, mkChar("order")); + SET_VECTOR_ELT(result, 2, R_igraph_0orvector_to_SEXP_d(p_order)); + SET_STRING_ELT(names, 3, mkChar("order.out")); + SET_VECTOR_ELT(result, 3, R_igraph_0orvector_to_SEXP_d(p_order_out)); + SET_STRING_ELT(names, 4, mkChar("father")); + SET_VECTOR_ELT(result, 4, R_igraph_0orvector_to_SEXP_d(p_father)); + SET_STRING_ELT(names, 5, mkChar("dist")); + SET_VECTOR_ELT(result, 5, R_igraph_0orvector_to_SEXP_d(p_dist)); + + SET_NAMES(result, names); + + UNPROTECT(2); + return result; +} + + +SEXP R_igraph_cohesive_blocks(SEXP graph) { + igraph_vector_ptr_t c_blocks; + igraph_vector_t c_cohesion; + igraph_vector_t c_parent; + igraph_t c_blockTree; + igraph_t c_graph; + SEXP blocks; + SEXP cohesion; + SEXP parent; + SEXP blockTree; + SEXP result; + SEXP names; + + R_SEXP_to_igraph(graph, &c_graph); + if (0 != igraph_vector_ptr_init(&c_blocks, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(R_igraph_vectorlist_destroy, &c_blocks); + if (0 != igraph_vector_init(&c_cohesion, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_cohesion); + if (0 != igraph_vector_init(&c_parent, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_parent); + + igraph_cohesive_blocks(&c_graph, &c_blocks, &c_cohesion, &c_parent, &c_blockTree); + + PROTECT(result=NEW_LIST(4)); + PROTECT(names=NEW_CHARACTER(4)); + PROTECT(blocks=R_igraph_vectorlist_to_SEXP_p1(&c_blocks)); + R_igraph_vectorlist_destroy(&c_blocks); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(cohesion=R_igraph_vector_to_SEXP(&c_cohesion)); + igraph_vector_destroy(&c_cohesion); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(parent=R_igraph_vector_to_SEXPp1(&c_parent)); + igraph_vector_destroy(&c_parent); + IGRAPH_FINALLY_CLEAN(1); + IGRAPH_FINALLY(igraph_destroy, &c_blockTree); + PROTECT(blockTree=R_igraph_to_SEXP(&c_blockTree)); + igraph_destroy(&c_blockTree); + IGRAPH_FINALLY_CLEAN(1); + SET_VECTOR_ELT(result, 0, blocks); + SET_VECTOR_ELT(result, 1, cohesion); + SET_VECTOR_ELT(result, 2, parent); + SET_VECTOR_ELT(result, 3, blockTree); + SET_STRING_ELT(names, 0, CREATE_STRING_VECTOR("blocks")); + SET_STRING_ELT(names, 1, CREATE_STRING_VECTOR("cohesion")); + SET_STRING_ELT(names, 2, CREATE_STRING_VECTOR("parent")); + SET_STRING_ELT(names, 3, CREATE_STRING_VECTOR("blockTree")); + SET_NAMES(result, names); + + UNPROTECT(6); + return result; +} + +typedef struct { + igraph_arpack_function_t *fun; +} R_igraph_i_function_container_t; + +SEXP R_igraph_i_levc_arp(SEXP extP, SEXP extE, SEXP pv) { + R_igraph_i_function_container_t *cont = R_ExternalPtrAddr(extP); + igraph_arpack_function_t *fun= cont->fun; + void *extra=R_ExternalPtrAddr(extE); + SEXP res; + + PROTECT(res=NEW_NUMERIC(GET_LENGTH(pv))); + fun(REAL(res), REAL(pv), GET_LENGTH(pv), extra); + + UNPROTECT(1); + return res; +} + +typedef struct R_igraph_i_levc_data_t { + SEXP fun; + SEXP extra; + SEXP rho; + SEXP rho2; +} R_igraph_i_levc_data_t; + +int R_igraph_i_levc_callback(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) { + + SEXP s_memb, s_comm, s_evalue, s_evector, s_multip; + SEXP R_fcall, R_multip_call; + SEXP res, l1, l2, l3; + int result; + R_igraph_i_levc_data_t *data=extra; + R_igraph_i_function_container_t cont = { arpack_multiplier }; + + PROTECT(s_memb=R_igraph_vector_to_SEXP(membership)); + PROTECT(s_comm=NEW_NUMERIC(1)); REAL(s_comm)[0]=comm; + PROTECT(s_evalue=NEW_NUMERIC(1)); REAL(s_evalue)[0]=eigenvalue; + PROTECT(s_evector=R_igraph_vector_to_SEXP(eigenvector)); + PROTECT(l1 = install("igraph.i.levc.arp")); + PROTECT(l2 = R_MakeExternalPtr((void*) &cont, R_NilValue, R_NilValue)); + PROTECT(l3 = R_MakeExternalPtr(arpack_extra, R_NilValue, R_NilValue)); + PROTECT(R_multip_call = lang3(l1, l2, l3)); + PROTECT(s_multip = eval(R_multip_call, data->rho2)); + + PROTECT(R_fcall = R_igraph_i_lang7(data->fun, s_memb, s_comm, + s_evalue, s_evector, s_multip, + data->extra)); + PROTECT(res = eval(R_fcall, data->rho)); + + result=(int) REAL(AS_NUMERIC(res))[0]; + + UNPROTECT(11); + return result; +} + +SEXP R_igraph_community_leading_eigenvector(SEXP graph, SEXP steps, + SEXP weights, + SEXP options, SEXP pstart, + SEXP callback, + SEXP callback_extra, + SEXP callback_env, + SEXP callback_env2) { + /* Declarations */ + igraph_t c_graph; + igraph_matrix_t c_merges; + igraph_vector_t c_membership; + igraph_integer_t c_steps; + igraph_vector_t v_weights, *pweights=0; + igraph_bool_t c_start=!isNull(pstart); + igraph_arpack_options_t c_options; + igraph_real_t c_modularity; + igraph_vector_t c_eigenvalues; + igraph_vector_ptr_t c_eigenvectors; + igraph_vector_t c_history; + SEXP merges; + SEXP membership; + SEXP modularity; + SEXP eigenvalues; + SEXP eigenvectors; + SEXP history; + SEXP result, names; + + R_igraph_i_levc_data_t callback_data = { callback, callback_extra, + callback_env, callback_env2 }; + + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (!isNull(weights)) { + pweights=&v_weights; R_SEXP_to_vector(weights, &v_weights); + } + if (0 != igraph_matrix_init(&c_merges, 0, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_matrix_destroy, &c_merges); + if (c_start) { + R_SEXP_to_vector_copy(pstart, &c_membership); + } else { + if (0 != igraph_vector_init(&c_membership, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_membership); + c_steps=INTEGER(steps)[0]; + R_SEXP_to_igraph_arpack_options(options, &c_options); + if (0 != igraph_vector_init(&c_eigenvalues, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + if (0 != igraph_vector_ptr_init(&c_eigenvectors, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + if (0 != igraph_vector_init(&c_history, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + /* Call igraph */ + igraph_community_leading_eigenvector(&c_graph, pweights, + &c_merges, + &c_membership, c_steps, + &c_options, &c_modularity, + c_start, &c_eigenvalues, + &c_eigenvectors, + &c_history, + isNull(callback) ? 0 : + R_igraph_i_levc_callback, + &callback_data); + + /* Convert output */ + PROTECT(result=NEW_LIST(7)); + PROTECT(names=NEW_CHARACTER(7)); + PROTECT(merges=R_igraph_matrix_to_SEXP(&c_merges)); + igraph_matrix_destroy(&c_merges); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(membership=R_igraph_vector_to_SEXP(&c_membership)); + igraph_vector_destroy(&c_membership); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(options=R_igraph_arpack_options_to_SEXP(&c_options)); + PROTECT(modularity=NEW_NUMERIC(1)); + REAL(modularity)[0]=c_modularity; + PROTECT(eigenvalues=R_igraph_vector_to_SEXP(&c_eigenvalues)); + igraph_vector_destroy(&c_eigenvalues); + PROTECT(eigenvectors=R_igraph_vectorlist_to_SEXP(&c_eigenvectors)); + R_igraph_vectorlist_destroy(&c_eigenvectors); + PROTECT(history=R_igraph_vector_to_SEXP(&c_history)); + igraph_vector_destroy(&c_history); + SET_VECTOR_ELT(result, 0, merges); + SET_VECTOR_ELT(result, 1, membership); + SET_VECTOR_ELT(result, 2, options); + SET_VECTOR_ELT(result, 3, modularity); + SET_VECTOR_ELT(result, 4, eigenvalues); + SET_VECTOR_ELT(result, 5, eigenvectors); + SET_VECTOR_ELT(result, 6, history); + SET_STRING_ELT(names, 0, CREATE_STRING_VECTOR("merges")); + SET_STRING_ELT(names, 1, CREATE_STRING_VECTOR("membership")); + SET_STRING_ELT(names, 2, CREATE_STRING_VECTOR("options")); + SET_STRING_ELT(names, 3, CREATE_STRING_VECTOR("modularity")); + SET_STRING_ELT(names, 4, CREATE_STRING_VECTOR("eigenvalues")); + SET_STRING_ELT(names, 5, CREATE_STRING_VECTOR("eigenvectors")); + SET_STRING_ELT(names, 6, CREATE_STRING_VECTOR("history")); + SET_NAMES(result, names); + + UNPROTECT(8); + + UNPROTECT(1); + return(result); +} + +SEXP R_igraph_get_eids(SEXP graph, SEXP pvp, SEXP pdirected, + SEXP perror, SEXP pmulti) { + igraph_t g; + igraph_vector_t vp; + igraph_vector_t res; + igraph_bool_t directed=LOGICAL(pdirected)[0]; + igraph_bool_t error=LOGICAL(perror)[0]; + igraph_bool_t multi=LOGICAL(pmulti)[0]; + SEXP result; + + R_SEXP_to_igraph(graph, &g); + R_SEXP_to_vector(pvp, &vp); + igraph_vector_init(&res, 0); + + if (multi) { + igraph_get_eids_multi(&g, &res, /*pairs=*/ &vp, /*path=*/ 0, directed, + error); + } else { + igraph_get_eids(&g, &res, /*pairs=*/ &vp, /*path=*/ 0, directed, error); + } + + PROTECT(result=R_igraph_vector_to_SEXP(&res)); + igraph_vector_destroy(&res); + + UNPROTECT(1); + return result; +} + +SEXP R_igraph_scg_semiprojectors(SEXP groups, SEXP matrix_type, SEXP p, + SEXP norm, SEXP psparse) { + /* Declarations */ + igraph_vector_t c_groups; + igraph_integer_t c_matrix_type; + igraph_matrix_t c_L; + igraph_matrix_t c_R; + igraph_sparsemat_t c_Lsparse; + igraph_sparsemat_t c_Rsparse; + igraph_vector_t c_p; + igraph_integer_t c_norm; + SEXP L; + SEXP R; + SEXP Lsparse; + SEXP Rsparse; + igraph_bool_t sparse=LOGICAL(psparse)[0]; + SEXP result, names; + /* Convert input */ + R_SEXP_to_vector(groups, &c_groups); + c_matrix_type=(igraph_integer_t) REAL(matrix_type)[0]; + if (!sparse) { + if (0 != igraph_matrix_init(&c_L, 0, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_matrix_destroy, &c_L); + if (0 != igraph_matrix_init(&c_R, 0, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_matrix_destroy, &c_R); + } else { + /* Nothing to do, because igraph_scg_semiprojectors + expect uninitialized sparse matrices */ + } + if (!isNull(p)) { R_SEXP_to_vector(p, &c_p); } + c_norm=(igraph_integer_t) REAL(norm)[0]; + /* Call igraph */ + igraph_scg_semiprojectors(&c_groups, (igraph_scg_matrix_t) + c_matrix_type, + (sparse ? 0 : &c_L), (sparse ? 0 : &c_R), + (sparse ? &c_Lsparse : 0), + (sparse ? &c_Rsparse : 0), + (isNull(p) ? 0 : &c_p), + (igraph_scg_norm_t) c_norm); + + /* Convert output */ + PROTECT(result=NEW_LIST(2)); + PROTECT(names=NEW_CHARACTER(2)); + if (!sparse) { + PROTECT(L=R_igraph_0ormatrix_to_SEXP(&c_L)); + igraph_matrix_destroy(&c_L); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(R=R_igraph_0ormatrix_to_SEXP(&c_R)); + igraph_matrix_destroy(&c_R); + IGRAPH_FINALLY_CLEAN(1); + SET_VECTOR_ELT(result, 0, L); + SET_VECTOR_ELT(result, 1, R); + } else { + PROTECT(Lsparse=R_igraph_0orsparsemat_to_SEXP(&c_Lsparse)); + igraph_sparsemat_destroy(&c_Lsparse); + PROTECT(Rsparse=R_igraph_0orsparsemat_to_SEXP(&c_Rsparse)); + igraph_sparsemat_destroy(&c_Rsparse); + SET_VECTOR_ELT(result, 0, Lsparse); + SET_VECTOR_ELT(result, 1, Rsparse); + } + SET_STRING_ELT(names, 0, CREATE_STRING_VECTOR("L")); + SET_STRING_ELT(names, 1, CREATE_STRING_VECTOR("R")); + SET_NAMES(result, names); + UNPROTECT(3); + + UNPROTECT(1); + return(result); +} + +SEXP R_igraph_laplacian(SEXP graph, SEXP normalized, SEXP weights, + SEXP psparse) { + /* Declarations */ + igraph_t c_graph; + igraph_matrix_t c_res; + igraph_sparsemat_t c_sparseres; + igraph_bool_t c_normalized; + igraph_vector_t c_weights; + igraph_bool_t c_sparse=LOGICAL(psparse)[0]; + SEXP result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (!c_sparse) { + if (0 != igraph_matrix_init(&c_res, 0, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_matrix_destroy, &c_res); + } + if (c_sparse) { + if (0 != igraph_sparsemat_init(&c_sparseres, 0, 0, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_sparsemat_destroy, &c_sparseres); + } + c_normalized=LOGICAL(normalized)[0]; + if (!isNull(weights)) { R_SEXP_to_vector(weights, &c_weights); } + /* Call igraph */ + igraph_laplacian(&c_graph, (c_sparse ? 0 : &c_res), + (c_sparse ? &c_sparseres : 0), + c_normalized, (isNull(weights) ? 0 : &c_weights)); + + /* Convert output */ + if (!c_sparse) { + PROTECT(result=R_igraph_matrix_to_SEXP(&c_res)); + igraph_matrix_destroy(&c_res); + IGRAPH_FINALLY_CLEAN(1); + } else { + PROTECT(result=R_igraph_sparsemat_to_SEXP(&c_sparseres)); + igraph_sparsemat_destroy(&c_sparseres); + IGRAPH_FINALLY_CLEAN(1); + } + + UNPROTECT(1); + return(result); +} + +SEXP R_igraph_scg_adjacency(SEXP graph, SEXP matrix, SEXP sparsmat, SEXP ev, + SEXP intervals_vector, + SEXP algorithm, SEXP evec, + SEXP groups, SEXP use_arpack, SEXP maxiter, + SEXP sparse, SEXP output, SEXP semproj, + SEXP epairs) { + + /* Declarations */ + igraph_t c_graph; + igraph_matrix_t c_matrix; + igraph_sparsemat_t c_sparsmat; + igraph_vector_t c_ev; + igraph_vector_t c_intervals_vector; + igraph_integer_t c_algorithm=(igraph_integer_t) REAL(algorithm)[0]; + igraph_vector_t c_eval; + igraph_matrix_t c_evec; + igraph_vector_t c_groups; + igraph_bool_t c_use_arpack=LOGICAL(use_arpack)[0]; + igraph_integer_t c_maxiter=INTEGER(maxiter)[0]; + igraph_bool_t c_sparse=LOGICAL(sparse)[0]; + igraph_real_t c_output=REAL(output)[0]; + igraph_bool_t c_semproj=LOGICAL(semproj)[0]; + igraph_bool_t c_epairs=LOGICAL(epairs)[0]; + igraph_t c_scg_graph; + igraph_matrix_t c_scg_matrix; + igraph_sparsemat_t c_scg_sparsemat; + igraph_matrix_t c_L; + igraph_matrix_t c_R; + igraph_sparsemat_t c_Lsparse; + igraph_sparsemat_t c_Rsparse; + SEXP scg_graph; + SEXP scg_matrix; + SEXP scg_sparsemat; + SEXP L; + SEXP R; + SEXP Lsparse; + SEXP Rsparse; + SEXP result, names; + SEXP eval; + /* What to return */ + igraph_bool_t do_scg_graph= + (!isNull(graph) && c_output==1 /*default*/) || c_output==3 /*graph*/; + igraph_bool_t do_scg_matrix=!c_sparse && + ((isNull(graph) && c_output==1 /*default*/) || c_output==2 /*matrix*/); + igraph_bool_t do_scg_sparsemat=c_sparse && + ((isNull(graph) && c_output==1 /*default*/) || c_output==2 /*matrix*/); + igraph_bool_t do_L=c_semproj && !c_sparse; + igraph_bool_t do_R=c_semproj && !c_sparse; + igraph_bool_t do_Lsparse=c_semproj && c_sparse; + igraph_bool_t do_Rsparse=c_semproj && c_sparse; + igraph_bool_t do_eval=c_epairs; + igraph_bool_t do_evec=c_epairs; + + /* Convert input */ + if (!isNull(graph)) { R_SEXP_to_igraph(graph, &c_graph); } + if (!isNull(matrix)) { R_SEXP_to_matrix(matrix, &c_matrix); } + if (!isNull(sparsmat)) { R_SEXP_to_sparsemat(sparsmat, &c_sparsmat); } + + R_SEXP_to_vector(ev, &c_ev); + R_SEXP_to_vector(intervals_vector, &c_intervals_vector); + + if (do_eval) { igraph_vector_init(&c_eval, 0); } + if (!isNull(evec)) { + R_SEXP_to_igraph_matrix_copy(evec, &c_evec); + } else if (do_evec) { + igraph_matrix_init(&c_evec, 0, 0); + } + if (!isNull(groups)) { + R_SEXP_to_vector_copy(groups, &c_groups); + } else { + igraph_vector_init(&c_groups, 0); + } + + if (do_scg_matrix) { igraph_matrix_init(&c_scg_matrix, 0, 0); } + if (do_L) { igraph_matrix_init(&c_L, 0, 0); } + if (do_R) { igraph_matrix_init(&c_R, 0, 0); } + + if (do_scg_sparsemat) { igraph_sparsemat_init(&c_scg_sparsemat, 0, 0, 0); } + + /* Call igraph */ + igraph_scg_adjacency((isNull(graph) ? 0 : &c_graph), + (isNull(matrix) ? 0 : &c_matrix), + (isNull(sparsmat) ? 0 : &c_sparsmat), &c_ev, + /*intervals=*/ 0, &c_intervals_vector, + (igraph_scg_algorithm_t) c_algorithm, + (do_eval ? &c_eval : 0), + (!isNull(evec) || do_evec ? &c_evec : 0), + &c_groups, c_use_arpack, c_maxiter, + (do_scg_graph ? &c_scg_graph : 0), + (do_scg_matrix ? &c_scg_matrix : 0), + (do_scg_sparsemat ? &c_scg_sparsemat : 0), + (do_L ? &c_L : 0), (do_R ? &c_R : 0), + (do_Lsparse ? &c_Lsparse : 0), + (do_Rsparse ? &c_Rsparse : 0)); + + if (!isNull(sparsmat)) { igraph_free(c_sparsmat.cs); } + + /* Convert output */ + PROTECT(result=NEW_LIST(6)); + PROTECT(names=NEW_CHARACTER(6)); + + if (do_eval) { + eval=R_igraph_vector_to_SEXP(&c_eval); + igraph_vector_destroy(&c_eval); + } else { + eval=R_NilValue; + } + PROTECT(eval); + + if (do_evec) { + evec=R_igraph_matrix_to_SEXP(&c_evec); + igraph_matrix_destroy(&c_evec); + } else { + evec=R_NilValue; + } + PROTECT(evec); + + PROTECT(groups=R_igraph_vector_to_SEXPp1(&c_groups)); + igraph_vector_destroy(&c_groups); + + if (do_scg_graph) { + PROTECT(scg_graph=R_igraph_to_SEXP(&c_scg_graph)); + igraph_destroy(&c_scg_graph); + UNPROTECT(1); + } else { + scg_graph=R_NilValue; + } + PROTECT(scg_graph); + + if (do_scg_matrix) { + scg_matrix=R_igraph_matrix_to_SEXP(&c_scg_matrix); + igraph_matrix_destroy(&c_scg_matrix); + } else { + scg_matrix=R_NilValue; + } + PROTECT(scg_matrix); + + if (do_scg_sparsemat) { + scg_sparsemat=R_igraph_sparsemat_to_SEXP(&c_scg_sparsemat); + igraph_sparsemat_destroy(&c_scg_sparsemat); + } else { + scg_sparsemat=R_NilValue; + } + PROTECT(scg_sparsemat); + + if (do_L) { + L=R_igraph_matrix_to_SEXP(&c_L); + igraph_matrix_destroy(&c_L); + } else { + L=R_NilValue; + } + PROTECT(L); + + if (do_R) { + R=R_igraph_matrix_to_SEXP(&c_R); + igraph_matrix_destroy(&c_R); + } else { + R=R_NilValue; + } + PROTECT(R); + + if (do_Lsparse) { + Lsparse=R_igraph_sparsemat_to_SEXP(&c_Lsparse); + igraph_sparsemat_destroy(&c_Lsparse); + } else { + Lsparse=R_NilValue; + } + PROTECT(Lsparse); + + if (do_Rsparse) { + Rsparse=R_igraph_sparsemat_to_SEXP(&c_Rsparse); + igraph_sparsemat_destroy(&c_Rsparse); + } else { + Rsparse=R_NilValue; + } + PROTECT(Rsparse); + + if (do_scg_graph) { SET_VECTOR_ELT(result, 0, scg_graph); } + if (do_scg_matrix) { SET_VECTOR_ELT(result, 0, scg_matrix); } + if (do_scg_sparsemat) { SET_VECTOR_ELT(result, 0, scg_sparsemat); } + SET_VECTOR_ELT(result, 1, groups); + if (do_L) { SET_VECTOR_ELT(result, 2, L); } + if (do_Lsparse) { SET_VECTOR_ELT(result, 2, Lsparse); } + if (do_R) { SET_VECTOR_ELT(result, 3, R); } + if (do_Rsparse) { SET_VECTOR_ELT(result, 3, Rsparse); } + SET_VECTOR_ELT(result, 4, eval); + SET_VECTOR_ELT(result, 5, evec); + SET_STRING_ELT(names, 0, CREATE_STRING_VECTOR("Xt")); + SET_STRING_ELT(names, 1, CREATE_STRING_VECTOR("groups")); + SET_STRING_ELT(names, 2, CREATE_STRING_VECTOR("L")); + SET_STRING_ELT(names, 3, CREATE_STRING_VECTOR("R")); + SET_STRING_ELT(names, 4, CREATE_STRING_VECTOR("values")); + SET_STRING_ELT(names, 5, CREATE_STRING_VECTOR("vectors")); + SET_NAMES(result, names); + + UNPROTECT(12); + return(result); +} + +SEXP R_igraph_scg_stochastic(SEXP graph, SEXP matrix, SEXP sparsmat, SEXP ev, + SEXP intervals_vector, SEXP algorithm, SEXP norm, + SEXP evec, SEXP groups, SEXP p, SEXP use_arpack, + SEXP maxiter, SEXP sparse, SEXP output, + SEXP semproj, SEXP epairs, SEXP stat_prob) { + + /* Declarations */ + igraph_t c_graph; + igraph_matrix_t c_matrix; + igraph_sparsemat_t c_sparsmat; + igraph_vector_t c_ev; + igraph_vector_t c_intervals_vector; + igraph_integer_t c_algorithm=(igraph_integer_t) REAL(algorithm)[0]; + igraph_integer_t c_norm=(igraph_integer_t) REAL(norm)[0]; + igraph_vector_complex_t c_eval; + igraph_matrix_complex_t c_evec; + igraph_vector_t c_groups; + igraph_vector_t c_p; + igraph_bool_t c_use_arpack=LOGICAL(use_arpack)[0]; + igraph_integer_t c_maxiter=INTEGER(maxiter)[0]; + igraph_bool_t c_sparse=LOGICAL(sparse)[0]; + igraph_real_t c_output=REAL(output)[0]; + igraph_bool_t c_semproj=LOGICAL(semproj)[0]; + igraph_bool_t c_epairs=LOGICAL(epairs)[0]; + igraph_bool_t c_stat_prob=LOGICAL(stat_prob)[0]; + igraph_t c_scg_graph; + igraph_matrix_t c_scg_matrix; + igraph_sparsemat_t c_scg_sparsemat; + igraph_matrix_t c_L; + igraph_matrix_t c_R; + igraph_sparsemat_t c_Lsparse; + igraph_sparsemat_t c_Rsparse; + SEXP scg_graph; + SEXP scg_matrix; + SEXP scg_sparsemat; + SEXP L; + SEXP R; + SEXP Lsparse; + SEXP Rsparse; + SEXP result, names; + SEXP eval; + /* What to return */ + igraph_bool_t do_scg_graph= + (!isNull(graph) && c_output==1 /*default*/) || c_output==3 /*graph*/; + igraph_bool_t do_scg_matrix=!c_sparse && + ((isNull(graph) && c_output==1 /*default*/) || c_output==2 /*matrix*/); + igraph_bool_t do_scg_sparsemat=c_sparse && + ((isNull(graph) && c_output==1 /*default*/) || c_output==2 /*matrix*/); + igraph_bool_t do_L=c_semproj && !c_sparse; + igraph_bool_t do_R=c_semproj && !c_sparse; + igraph_bool_t do_Lsparse=c_semproj && c_sparse; + igraph_bool_t do_Rsparse=c_semproj && c_sparse; + igraph_bool_t do_eval=c_epairs; + igraph_bool_t do_evec=c_epairs; + igraph_bool_t do_p=c_stat_prob; + + /* Convert input */ + if (!isNull(graph)) { R_SEXP_to_igraph(graph, &c_graph); } + if (!isNull(matrix)) { R_SEXP_to_matrix(matrix, &c_matrix); } + if (!isNull(sparsmat)) { R_SEXP_to_sparsemat(sparsmat, &c_sparsmat); } + + R_SEXP_to_vector(ev, &c_ev); + R_SEXP_to_vector(intervals_vector, &c_intervals_vector); + if (do_eval) { igraph_vector_complex_init(&c_eval, 0); } + if (!isNull(evec)) { + R_SEXP_to_matrix_complex_copy(evec, &c_evec); + } else if (do_evec) { + igraph_matrix_complex_init(&c_evec, 0, 0); + } + if (!isNull(groups)) { + R_SEXP_to_vector_copy(groups, &c_groups); + } else { + igraph_vector_init(&c_groups, 0); + } + + if (!isNull(p)) { + R_SEXP_to_vector_copy(p, &c_p); + } else if (do_p) { + igraph_vector_init(&c_p, 0); + } + + if (do_scg_matrix) { igraph_matrix_init(&c_scg_matrix, 0, 0); } + if (do_L) { igraph_matrix_init(&c_L, 0, 0); } + if (do_R) { igraph_matrix_init(&c_R, 0, 0); } + + /* Call igraph */ + igraph_scg_stochastic((isNull(graph) ? 0 : &c_graph), + (isNull(matrix) ? 0 : &c_matrix), + (isNull(sparsmat) ? 0 : &c_sparsmat), &c_ev, + /*intervals=*/ 0, &c_intervals_vector, + (igraph_scg_algorithm_t) c_algorithm, + (igraph_scg_norm_t) c_norm, + (do_eval ? &c_eval : 0), + (!isNull(evec) || do_evec ? &c_evec : 0), + &c_groups, (!isNull(p) || do_p ? &c_p : 0), + c_use_arpack, c_maxiter, + (do_scg_graph ? &c_scg_graph : 0), + (do_scg_matrix ? &c_scg_matrix : 0), + (do_scg_sparsemat ? &c_scg_sparsemat : 0), + (do_L ? &c_L : 0), (do_R ? &c_R : 0), + (do_Lsparse ? &c_Lsparse : 0), + (do_Rsparse ? &c_Rsparse : 0)); + + if (!isNull(sparsmat)) { igraph_free(c_sparsmat.cs); } + + /* Convert output */ + PROTECT(result=NEW_LIST(7)); + PROTECT(names=NEW_CHARACTER(7)); + + if (do_eval) { + PROTECT(eval=R_igraph_vector_complex_to_SEXP(&c_eval)); + igraph_vector_complex_destroy(&c_eval); + } else { + PROTECT(eval=R_NilValue); + } + + if (do_evec) { + PROTECT(evec=R_igraph_matrix_complex_to_SEXP(&c_evec)); + igraph_matrix_complex_destroy(&c_evec); + } else { + PROTECT(evec=R_NilValue); + } + + if (do_p) { + PROTECT(p=R_igraph_vector_to_SEXP(&c_p)); + igraph_vector_destroy(&c_p); + } else { + PROTECT(p=R_NilValue); + } + + PROTECT(groups=R_igraph_vector_to_SEXPp1(&c_groups)); + igraph_vector_destroy(&c_groups); + + if (do_scg_graph) { + PROTECT(scg_graph=R_igraph_to_SEXP(&c_scg_graph)); + igraph_destroy(&c_scg_graph); + } else { + PROTECT(scg_graph=R_NilValue); + } + if (do_scg_matrix) { + PROTECT(scg_matrix=R_igraph_matrix_to_SEXP(&c_scg_matrix)); + igraph_matrix_destroy(&c_scg_matrix); + } else { + PROTECT(scg_matrix=R_NilValue); + } + if (do_scg_sparsemat) { + PROTECT(scg_sparsemat=R_igraph_sparsemat_to_SEXP(&c_scg_sparsemat)); + igraph_sparsemat_destroy(&c_scg_sparsemat); + } else { + PROTECT(scg_sparsemat=R_NilValue); + } + if (do_L) { + PROTECT(L=R_igraph_matrix_to_SEXP(&c_L)); + igraph_matrix_destroy(&c_L); + } else { + PROTECT(L=R_NilValue); + } + if (do_R) { + PROTECT(R=R_igraph_matrix_to_SEXP(&c_R)); + igraph_matrix_destroy(&c_R); + } else { + PROTECT(R=R_NilValue); + } + if (do_Lsparse) { + PROTECT(Lsparse=R_igraph_sparsemat_to_SEXP(&c_Lsparse)); + igraph_sparsemat_destroy(&c_Lsparse); + } else { + PROTECT(Lsparse=R_NilValue); + } + if (do_Rsparse) { + PROTECT(Rsparse=R_igraph_sparsemat_to_SEXP(&c_Rsparse)); + igraph_sparsemat_destroy(&c_Rsparse); + } else { + PROTECT(Rsparse=R_NilValue); + } + + if (do_scg_graph) { SET_VECTOR_ELT(result, 0, scg_graph); } + if (do_scg_matrix) { SET_VECTOR_ELT(result, 0, scg_matrix); } + if (do_scg_sparsemat) { SET_VECTOR_ELT(result, 0, scg_sparsemat); } + SET_VECTOR_ELT(result, 1, groups); + if (do_L) { SET_VECTOR_ELT(result, 2, L); } + if (do_Lsparse) { SET_VECTOR_ELT(result, 2, Lsparse); } + if (do_R) { SET_VECTOR_ELT(result, 3, R); } + if (do_Rsparse) { SET_VECTOR_ELT(result, 3, Rsparse); } + SET_VECTOR_ELT(result, 4, eval); + SET_VECTOR_ELT(result, 5, evec); + if (do_p) { SET_VECTOR_ELT(result, 6, p); } + SET_STRING_ELT(names, 0, CREATE_STRING_VECTOR("Xt")); + SET_STRING_ELT(names, 1, CREATE_STRING_VECTOR("groups")); + SET_STRING_ELT(names, 2, CREATE_STRING_VECTOR("L")); + SET_STRING_ELT(names, 3, CREATE_STRING_VECTOR("R")); + SET_STRING_ELT(names, 4, CREATE_STRING_VECTOR("values")); + SET_STRING_ELT(names, 5, CREATE_STRING_VECTOR("vectors")); + SET_STRING_ELT(names, 6, CREATE_STRING_VECTOR("p")); + SET_NAMES(result, names); + UNPROTECT(12); + + UNPROTECT(1); + return(result); +} + +SEXP R_igraph_scg_laplacian(SEXP graph, SEXP matrix, SEXP sparsmat, SEXP ev, + SEXP intervals_vector, SEXP algorithm, SEXP norm, + SEXP direction, SEXP evec, SEXP groups, + SEXP use_arpack, SEXP maxiter, SEXP sparse, + SEXP output, SEXP semproj, SEXP epairs) { + + /* Declarations */ + igraph_t c_graph; + igraph_matrix_t c_matrix; + igraph_sparsemat_t c_sparsmat; + igraph_vector_t c_ev; + igraph_vector_t c_intervals_vector; + igraph_integer_t c_algorithm=(igraph_integer_t) REAL(algorithm)[0]; + igraph_integer_t c_norm=(igraph_integer_t) REAL(norm)[0]; + igraph_integer_t c_direction=(igraph_integer_t) REAL(direction)[0]; + igraph_vector_complex_t c_eval; + igraph_matrix_complex_t c_evec; + igraph_vector_t c_groups; + igraph_bool_t c_use_arpack=LOGICAL(use_arpack)[0]; + igraph_integer_t c_maxiter=INTEGER(maxiter)[0]; + igraph_bool_t c_sparse=LOGICAL(sparse)[0]; + igraph_real_t c_output=REAL(output)[0]; + igraph_bool_t c_semproj=LOGICAL(semproj)[0]; + igraph_bool_t c_epairs=LOGICAL(epairs)[0]; + igraph_t c_scg_graph; + igraph_matrix_t c_scg_matrix; + igraph_sparsemat_t c_scg_sparsemat; + igraph_matrix_t c_L; + igraph_matrix_t c_R; + igraph_sparsemat_t c_Lsparse; + igraph_sparsemat_t c_Rsparse; + SEXP eval; + SEXP scg_graph; + SEXP scg_matrix; + SEXP scg_sparsemat; + SEXP L; + SEXP R; + SEXP Lsparse; + SEXP Rsparse; + SEXP result, names; + /* What to return */ + igraph_bool_t do_scg_graph= + (!isNull(graph) && c_output==1 /*default*/) || c_output==3 /*graph*/; + igraph_bool_t do_scg_matrix=!c_sparse && + ((isNull(graph) && c_output==1 /*default*/) || c_output==2 /*matrix*/); + igraph_bool_t do_scg_sparsemat=c_sparse && + ((isNull(graph) && c_output==1 /*default*/) || c_output==2 /*matrix*/); + igraph_bool_t do_L=c_semproj && !c_sparse; + igraph_bool_t do_R=c_semproj && !c_sparse; + igraph_bool_t do_Lsparse=c_semproj && c_sparse; + igraph_bool_t do_Rsparse=c_semproj && c_sparse; + igraph_bool_t do_eval=c_epairs; + igraph_bool_t do_evec=c_epairs; + + /* Convert input */ + if (!isNull(graph)) { R_SEXP_to_igraph(graph, &c_graph); } + if (!isNull(matrix)) { R_SEXP_to_matrix(matrix, &c_matrix); } + if (!isNull(sparsmat)) { R_SEXP_to_sparsemat(sparsmat, &c_sparsmat); } + + R_SEXP_to_vector(ev, &c_ev); + R_SEXP_to_vector(intervals_vector, &c_intervals_vector); + + if (do_eval) { igraph_vector_complex_init(&c_eval, 0); } + if (!isNull(evec)) { + R_SEXP_to_matrix_complex_copy(evec, &c_evec); + } else if (do_evec) { + igraph_matrix_complex_init(&c_evec, 0, 0); + } + if (!isNull(groups)) { + R_SEXP_to_vector_copy(groups, &c_groups); + } else { + igraph_vector_init(&c_groups, 0); + } + + if (do_scg_matrix) { igraph_matrix_init(&c_scg_matrix, 0, 0); } + if (do_L) { igraph_matrix_init(&c_L, 0, 0); } + if (do_R) { igraph_matrix_init(&c_R, 0, 0); } + + /* Call igraph */ + igraph_scg_laplacian((isNull(graph) ? 0 : &c_graph), + (isNull(matrix) ? 0 : &c_matrix), + (isNull(sparsmat) ? 0 : &c_sparsmat), &c_ev, + /*intervals=*/ 0, &c_intervals_vector, + (igraph_scg_algorithm_t) c_algorithm, + (igraph_scg_norm_t) c_norm, + (igraph_scg_direction_t) c_direction, + (do_eval ? &c_eval : 0), + (!isNull(evec) || do_evec ? &c_evec : 0), + &c_groups, c_use_arpack, c_maxiter, + (do_scg_graph ? &c_scg_graph : 0), + (do_scg_matrix ? &c_scg_matrix : 0), + (do_scg_sparsemat ? &c_scg_sparsemat : 0), + (do_L ? &c_L : 0), (do_R ? &c_R : 0), + (do_Lsparse ? &c_Lsparse : 0), + (do_Rsparse ? &c_Rsparse : 0)); + + if (!isNull(sparsmat)) { igraph_free(c_sparsmat.cs); } + + /* Convert output */ + PROTECT(result=NEW_LIST(6)); + PROTECT(names=NEW_CHARACTER(6)); + + if (do_eval) { + PROTECT(eval=R_igraph_vector_complex_to_SEXP(&c_eval)); + igraph_vector_complex_destroy(&c_eval); + } else { + PROTECT(eval=R_NilValue); + } + + if (do_evec) { + PROTECT(evec=R_igraph_matrix_complex_to_SEXP(&c_evec)); + igraph_matrix_complex_destroy(&c_evec); + } else { + PROTECT(evec=R_NilValue); + } + + PROTECT(groups=R_igraph_vector_to_SEXPp1(&c_groups)); + igraph_vector_destroy(&c_groups); + + if (do_scg_graph) { + PROTECT(scg_graph=R_igraph_to_SEXP(&c_scg_graph)); + igraph_destroy(&c_scg_graph); + } else { + PROTECT(scg_graph=R_NilValue); + } + if (do_scg_matrix) { + PROTECT(scg_matrix=R_igraph_matrix_to_SEXP(&c_scg_matrix)); + igraph_matrix_destroy(&c_scg_matrix); + } else { + PROTECT(scg_matrix=R_NilValue); + } + if (do_scg_sparsemat) { + PROTECT(scg_sparsemat=R_igraph_sparsemat_to_SEXP(&c_scg_sparsemat)); + igraph_sparsemat_destroy(&c_scg_sparsemat); + } else { + PROTECT(scg_sparsemat=R_NilValue); + } + if (do_L) { + PROTECT(L=R_igraph_matrix_to_SEXP(&c_L)); + igraph_matrix_destroy(&c_L); + } else { + PROTECT(L=R_NilValue); + } + if (do_R) { + PROTECT(R=R_igraph_matrix_to_SEXP(&c_R)); + igraph_matrix_destroy(&c_R); + } else { + PROTECT(R=R_NilValue); + } + if (do_Lsparse) { + PROTECT(Lsparse=R_igraph_sparsemat_to_SEXP(&c_Lsparse)); + igraph_sparsemat_destroy(&c_Lsparse); + } else { + PROTECT(Lsparse=R_NilValue); + } + if (do_Rsparse) { + PROTECT(Rsparse=R_igraph_sparsemat_to_SEXP(&c_Rsparse)); + igraph_sparsemat_destroy(&c_Rsparse); + } else { + PROTECT(Rsparse=R_NilValue); + } + + if (do_scg_graph) { SET_VECTOR_ELT(result, 0, scg_graph); } + if (do_scg_matrix) { SET_VECTOR_ELT(result, 0, scg_matrix); } + if (do_scg_sparsemat) { SET_VECTOR_ELT(result, 0, scg_sparsemat); } + SET_VECTOR_ELT(result, 1, groups); + if (do_L) { SET_VECTOR_ELT(result, 2, L); } + if (do_Lsparse) { SET_VECTOR_ELT(result, 2, Lsparse); } + if (do_R) { SET_VECTOR_ELT(result, 3, R); } + if (do_Rsparse) { SET_VECTOR_ELT(result, 3, Rsparse); } + SET_VECTOR_ELT(result, 4, eval); + SET_VECTOR_ELT(result, 5, evec); + SET_STRING_ELT(names, 0, CREATE_STRING_VECTOR("Xt")); + SET_STRING_ELT(names, 1, CREATE_STRING_VECTOR("groups")); + SET_STRING_ELT(names, 2, CREATE_STRING_VECTOR("L")); + SET_STRING_ELT(names, 3, CREATE_STRING_VECTOR("R")); + SET_STRING_ELT(names, 4, CREATE_STRING_VECTOR("values")); + SET_STRING_ELT(names, 5, CREATE_STRING_VECTOR("vectors")); + SET_NAMES(result, names); + UNPROTECT(11); + + UNPROTECT(1); + return(result); +} + +SEXP R_igraph_subisomorphic_lad(SEXP pattern, SEXP target, SEXP domains, + SEXP induced, SEXP time_limit, + SEXP pqmap, SEXP pqall_maps) { + /* Declarations */ + igraph_t c_pattern; + igraph_t c_target; + igraph_vector_ptr_t c_domains; + igraph_bool_t c_iso; + igraph_vector_t c_map; + igraph_vector_ptr_t c_maps; + igraph_bool_t c_induced; + int c_time_limit; + igraph_bool_t c_qmap; + igraph_bool_t c_qall_maps; + SEXP iso; + SEXP map; + SEXP maps; + + SEXP result, names; + /* Convert input */ + R_SEXP_to_igraph(pattern, &c_pattern); + R_SEXP_to_igraph(target, &c_target); + + R_igraph_SEXP_to_0orvectorlist(domains, &c_domains); + + c_qmap=LOGICAL(pqmap)[0]; + c_qall_maps=LOGICAL(pqall_maps)[0]; + if (c_qmap) { + if (0 != igraph_vector_init(&c_map, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_map); + map=R_GlobalEnv; /* hack to have a non-NULL value */ + } else { + map=R_NilValue; + } + + if (c_qall_maps) { + if (0 != igraph_vector_ptr_init(&c_maps, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(R_igraph_vectorlist_destroy, &c_maps); + maps=R_GlobalEnv; /* hack to have a non-NULL value */ + } else { + maps=R_NilValue; + } + + c_induced=LOGICAL(induced)[0]; + c_time_limit=INTEGER(time_limit)[0]; + /* Call igraph */ + igraph_subisomorphic_lad(&c_pattern, &c_target, + (isNull(domains) ? 0 : &c_domains), &c_iso, + (isNull(map) ? 0 : &c_map), + (isNull(maps) ? 0 : &c_maps), + c_induced, c_time_limit); + + /* Convert output */ + PROTECT(result=NEW_LIST(3)); + PROTECT(names=NEW_CHARACTER(3)); + PROTECT(iso=NEW_LOGICAL(1)); + LOGICAL(iso)[0]=c_iso; + if (!isNull(map)) { + PROTECT(map=R_igraph_0orvector_to_SEXP(&c_map)); + igraph_vector_destroy(&c_map); + IGRAPH_FINALLY_CLEAN(1); + } else { + PROTECT(map=R_NilValue); + } + if (!isNull(maps)) { + PROTECT(maps=R_igraph_0orvectorlist_to_SEXP(&c_maps)); + R_igraph_vectorlist_destroy(&c_maps); + IGRAPH_FINALLY_CLEAN(1); + } else { + PROTECT(maps=R_NilValue); + } + SET_VECTOR_ELT(result, 0, iso); + SET_VECTOR_ELT(result, 1, map); + SET_VECTOR_ELT(result, 2, maps); + SET_STRING_ELT(names, 0, CREATE_STRING_VECTOR("iso")); + SET_STRING_ELT(names, 1, CREATE_STRING_VECTOR("map")); + SET_STRING_ELT(names, 2, CREATE_STRING_VECTOR("maps")); + SET_NAMES(result, names); + UNPROTECT(4); + + UNPROTECT(1); + return(result); +} + +/*-------------------------------------------/ + / igraph_graphlets / + /-------------------------------------------*/ +SEXP R_igraph_graphlets(SEXP graph, SEXP weights, SEXP niter) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_t c_weights; + igraph_vector_ptr_t c_cliques; + igraph_vector_t c_Mu; + int c_niter; + SEXP cliques; + SEXP Mu; + SEXP result, names; + + R_PreserveObject(R_igraph_attribute_protected=NEW_LIST(100)); + R_igraph_attribute_protected_size=0; + IGRAPH_FINALLY(R_igraph_attribute_protected_destroy, 0); + + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (!isNull(weights)) { R_SEXP_to_vector(weights, &c_weights); } + if (0 != igraph_vector_ptr_init(&c_cliques, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(R_igraph_vectorlist_destroy, &c_cliques); + if (0 != igraph_vector_init(&c_Mu, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_Mu); + c_niter=INTEGER(niter)[0]; + /* Call igraph */ + igraph_graphlets(&c_graph, (isNull(weights) ? 0 : &c_weights), &c_cliques, + &c_Mu, c_niter); + + /* Convert output */ + PROTECT(result=NEW_LIST(2)); + PROTECT(names=NEW_CHARACTER(2)); + PROTECT(cliques=R_igraph_vectorlist_to_SEXP_p1(&c_cliques)); + R_igraph_vectorlist_destroy(&c_cliques); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(Mu=R_igraph_vector_to_SEXP(&c_Mu)); + igraph_vector_destroy(&c_Mu); + IGRAPH_FINALLY_CLEAN(1); + SET_VECTOR_ELT(result, 0, cliques); + SET_VECTOR_ELT(result, 1, Mu); + SET_STRING_ELT(names, 0, CREATE_STRING_VECTOR("cliques")); + SET_STRING_ELT(names, 1, CREATE_STRING_VECTOR("Mu")); + SET_NAMES(result, names); + + UNPROTECT(4); + IGRAPH_FINALLY_CLEAN(1); + R_igraph_attribute_protected_destroy(0); + + return(result); +} + +/*-------------------------------------------/ +/ igraph_graphlets_candidate_basis / +/-------------------------------------------*/ +SEXP R_igraph_graphlets_candidate_basis(SEXP graph, SEXP weights) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_t c_weights; + igraph_vector_ptr_t c_cliques; + igraph_vector_t c_thresholds; + SEXP cliques; + SEXP thresholds; + + SEXP result, names; + + R_PreserveObject(R_igraph_attribute_protected=NEW_LIST(100)); + R_igraph_attribute_protected_size=0; + IGRAPH_FINALLY(R_igraph_attribute_protected_destroy, 0); + + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (!isNull(weights)) { R_SEXP_to_vector(weights, &c_weights); } + if (0 != igraph_vector_ptr_init(&c_cliques, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(R_igraph_vectorlist_destroy, &c_cliques); + if (0 != igraph_vector_init(&c_thresholds, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_thresholds); + /* Call igraph */ + igraph_graphlets_candidate_basis(&c_graph, (isNull(weights) ? 0 : &c_weights), + &c_cliques, &c_thresholds); + + /* Convert output */ + PROTECT(result=NEW_LIST(2)); + PROTECT(names=NEW_CHARACTER(2)); + PROTECT(cliques=R_igraph_vectorlist_to_SEXP_p1(&c_cliques)); + R_igraph_vectorlist_destroy(&c_cliques); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(thresholds=R_igraph_vector_to_SEXP(&c_thresholds)); + igraph_vector_destroy(&c_thresholds); + IGRAPH_FINALLY_CLEAN(1); + SET_VECTOR_ELT(result, 0, cliques); + SET_VECTOR_ELT(result, 1, thresholds); + SET_STRING_ELT(names, 0, CREATE_STRING_VECTOR("cliques")); + SET_STRING_ELT(names, 1, CREATE_STRING_VECTOR("thresholds")); + SET_NAMES(result, names); + + UNPROTECT(4); + IGRAPH_FINALLY_CLEAN(1); + R_igraph_attribute_protected_destroy(0); + + return(result); +} + +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); + +/*-------------------------------------------/ +/ igraph_graphlets_project / +/-------------------------------------------*/ + +SEXP R_igraph_graphlets_project(SEXP graph, SEXP weights, SEXP cliques, + SEXP Mu, SEXP niter) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_t c_weights; + igraph_vector_ptr_t c_cliques; + igraph_vector_t c_Mu; + int c_niter; + + SEXP result; + + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (!isNull(weights)) { R_SEXP_to_vector(weights, &c_weights); } + if (!isNull(cliques)) { R_igraph_SEXP_to_vectorlist(cliques, &c_cliques); } + if (0 != R_SEXP_to_vector_copy(Mu, &c_Mu)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_Mu); + c_niter=INTEGER(niter)[0]; + + /* TODO: Change igraph_i_graphlets_project to igraph_graphlets_project, because + * we should not depend on non-public functions from igraph. + */ + /* Call igraph */ + igraph_i_graphlets_project(&c_graph, (isNull(weights) ? 0 : &c_weights), + &c_cliques, &c_Mu, /*startMu=*/ 1, c_niter, + /*vid1=*/ 1); + + /* Convert output */ + PROTECT(Mu=R_igraph_vector_to_SEXP(&c_Mu)); + igraph_vector_destroy(&c_Mu); + IGRAPH_FINALLY_CLEAN(1); + result=Mu; + + UNPROTECT(1); + return(result); +} + + +SEXP R_igraph_adjacency_spectral_embedding(SEXP graph, SEXP no, + SEXP pweights, SEXP pwhich, + SEXP scaled, SEXP cvec, + SEXP options) { + + /* Declarations */ + igraph_t c_graph; + igraph_vector_t weights; + igraph_eigen_which_position_t c_which; + igraph_integer_t c_no; + igraph_bool_t c_scaled; + igraph_matrix_t c_X; + igraph_matrix_t c_Y; + igraph_vector_t c_D; + igraph_vector_t c_cvec; + igraph_arpack_options_t c_options; + SEXP X; + SEXP Y; + SEXP D; + igraph_bool_t directed; + + SEXP result, names; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + directed=igraph_is_directed(&c_graph); + if (!isNull(pweights)) { + R_SEXP_to_vector(pweights, &weights); + } + c_which=INTEGER(pwhich)[0]; + c_no=INTEGER(no)[0]; + c_scaled=LOGICAL(scaled)[0]; + if (0 != igraph_matrix_init(&c_X, 0, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_matrix_destroy, &c_X); + if (directed) { + if (0 != igraph_matrix_init(&c_Y, 0, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_matrix_destroy, &c_Y); + } + if (0 != igraph_vector_init(&c_D, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_D); + R_SEXP_to_vector(cvec, &c_cvec); + R_SEXP_to_igraph_arpack_options(options, &c_options); + +/* Call igraph */ + igraph_adjacency_spectral_embedding(&c_graph, c_no, + isNull(pweights) ? 0 : &weights, + c_which, c_scaled, &c_X, + directed ? &c_Y : 0, &c_D, + &c_cvec, &c_options); + + /* Convert output */ + PROTECT(result=NEW_LIST(4)); + PROTECT(names=NEW_CHARACTER(4)); + PROTECT(X=R_igraph_matrix_to_SEXP(&c_X)); + igraph_matrix_destroy(&c_X); + IGRAPH_FINALLY_CLEAN(1); + if (directed) { + PROTECT(Y=R_igraph_matrix_to_SEXP(&c_Y)); + igraph_matrix_destroy(&c_Y); + IGRAPH_FINALLY_CLEAN(1); + } else { + PROTECT(Y=R_NilValue); + } + PROTECT(D=R_igraph_vector_to_SEXP(&c_D)); + igraph_vector_destroy(&c_D); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(options=R_igraph_arpack_options_to_SEXP(&c_options)); + SET_VECTOR_ELT(result, 0, X); + SET_VECTOR_ELT(result, 1, Y); + SET_VECTOR_ELT(result, 2, D); + SET_VECTOR_ELT(result, 3, options); + SET_STRING_ELT(names, 0, CREATE_STRING_VECTOR("X")); + SET_STRING_ELT(names, 1, CREATE_STRING_VECTOR("Y")); + SET_STRING_ELT(names, 2, CREATE_STRING_VECTOR("D")); + SET_STRING_ELT(names, 3, CREATE_STRING_VECTOR("options")); + SET_NAMES(result, names); + UNPROTECT(5); + + UNPROTECT(1); + return(result); +} + +SEXP R_igraph_laplacian_spectral_embedding(SEXP graph, SEXP no, + SEXP weights, SEXP which, + SEXP type, + SEXP scaled, SEXP options) { + /* Declarations */ + igraph_t c_graph; + igraph_integer_t c_no; + igraph_vector_t c_weights; + igraph_eigen_which_position_t c_which; + igraph_laplacian_spectral_embedding_type_t c_type; + igraph_bool_t c_scaled; + igraph_matrix_t c_X; + igraph_matrix_t c_Y; + igraph_vector_t c_D; + igraph_arpack_options_t c_options; + SEXP X; + SEXP Y; + SEXP D; + igraph_bool_t directed; + + SEXP result, names; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + directed=igraph_is_directed(&c_graph); + c_no=INTEGER(no)[0]; + if (!isNull(weights)) { R_SEXP_to_vector(weights, &c_weights); } + c_which=INTEGER(which)[0]; + c_type=(igraph_laplacian_spectral_embedding_type_t) INTEGER(type)[0]; + c_scaled=LOGICAL(scaled)[0]; + if (0 != igraph_matrix_init(&c_X, 0, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_matrix_destroy, &c_X); + if (directed) { + if (0 != igraph_matrix_init(&c_Y, 0, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_matrix_destroy, &c_Y); + } + if (0 != igraph_vector_init(&c_D, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_D); + R_SEXP_to_igraph_arpack_options(options, &c_options); + /* Call igraph */ + igraph_laplacian_spectral_embedding(&c_graph, c_no, + (isNull(weights) ? 0 : &c_weights), + c_which, c_type, c_scaled, + &c_X, directed ? &c_Y : 0, + &c_D, &c_options); + + /* Convert output */ + PROTECT(result=NEW_LIST(4)); + PROTECT(names=NEW_CHARACTER(4)); + PROTECT(X=R_igraph_matrix_to_SEXP(&c_X)); + igraph_matrix_destroy(&c_X); + IGRAPH_FINALLY_CLEAN(1); + if (directed) { + PROTECT(Y=R_igraph_matrix_to_SEXP(&c_Y)); + igraph_matrix_destroy(&c_Y); + IGRAPH_FINALLY_CLEAN(1); + } else { + PROTECT(Y=R_NilValue); + } + PROTECT(D=R_igraph_0orvector_to_SEXP(&c_D)); + igraph_vector_destroy(&c_D); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(options=R_igraph_arpack_options_to_SEXP(&c_options)); + SET_VECTOR_ELT(result, 0, X); + SET_VECTOR_ELT(result, 1, Y); + SET_VECTOR_ELT(result, 2, D); + SET_VECTOR_ELT(result, 3, options); + SET_STRING_ELT(names, 0, CREATE_STRING_VECTOR("X")); + SET_STRING_ELT(names, 1, CREATE_STRING_VECTOR("Y")); + SET_STRING_ELT(names, 2, CREATE_STRING_VECTOR("D")); + SET_STRING_ELT(names, 3, CREATE_STRING_VECTOR("options")); + SET_NAMES(result, names); + UNPROTECT(5); + + UNPROTECT(1); + return(result); +} + +SEXP R_igraph_simple_interconnected_islands_game(SEXP islands_n, SEXP islands_size, SEXP islands_pin, SEXP n_inter) { + + igraph_t g; + igraph_integer_t a=INTEGER(islands_n)[0]; + igraph_integer_t b=INTEGER(islands_size)[0]; + igraph_real_t c=REAL(islands_pin)[0]; + igraph_integer_t d=INTEGER(n_inter)[0]; + SEXP result; + + igraph_simple_interconnected_islands_game(&g, a, b, c, d); + PROTECT(result=R_igraph_to_SEXP(&g)); + igraph_destroy(&g); + + UNPROTECT(1); + return result; +} + +SEXP R_igraph_version() { + const char *version; + SEXP result; + igraph_version(&version, /*major=*/ 0, /*minor=*/ 0, /*patch=*/ 0); + PROTECT(result=NEW_CHARACTER(1)); + SET_STRING_ELT(result, 0, mkChar(version)); + UNPROTECT(1); + return result; +} + +SEXP R_igraph_bipartite_projection(SEXP graph, SEXP types, SEXP probe1, + SEXP pwhich) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_bool_t c_types; + igraph_t c_proj1; + igraph_t c_proj2; + igraph_vector_t c_multiplicity1; + igraph_vector_t c_multiplicity2; + igraph_integer_t c_probe1; + igraph_integer_t which=INTEGER(pwhich)[0]; + igraph_bool_t do_1=(which == 0 || which == 1); + igraph_bool_t do_2=(which == 0 || which == 2); + SEXP proj1; + SEXP proj2; + SEXP multiplicity1; + SEXP multiplicity2; + + SEXP result, names; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (!isNull(types)) { R_SEXP_to_vector_bool(types, &c_types); } + if (0 != igraph_vector_init(&c_multiplicity1, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_multiplicity1); + multiplicity1 = R_GlobalEnv; /* hack to have a non-NULL value */ + if (0 != igraph_vector_init(&c_multiplicity2, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_multiplicity2); + multiplicity2=R_GlobalEnv; /* hack to have a non-NULL value */ + c_probe1=INTEGER(probe1)[0]; + /* Call igraph */ + igraph_bipartite_projection(&c_graph, (isNull(types) ? 0 : &c_types), + do_1 ? &c_proj1 : 0, do_2 ? &c_proj2 : 0, + (isNull(multiplicity1) ? 0 : &c_multiplicity1), + (isNull(multiplicity2) ? 0 : &c_multiplicity2), + c_probe1); + + /* Convert output */ + PROTECT(result=NEW_LIST(4)); + PROTECT(names=NEW_CHARACTER(4)); + if (do_1) { + IGRAPH_FINALLY(igraph_destroy, &c_proj1); + PROTECT(proj1=R_igraph_to_SEXP(&c_proj1)); + igraph_destroy(&c_proj1); + IGRAPH_FINALLY_CLEAN(1); + } else { + PROTECT(proj1=R_NilValue); + } + if (do_2) { + IGRAPH_FINALLY(igraph_destroy, &c_proj2); + PROTECT(proj2=R_igraph_to_SEXP(&c_proj2)); + igraph_destroy(&c_proj2); + IGRAPH_FINALLY_CLEAN(1); + } else { + PROTECT(proj2=R_NilValue); + } + PROTECT(multiplicity1=R_igraph_0orvector_to_SEXP(&c_multiplicity1)); + igraph_vector_destroy(&c_multiplicity1); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(multiplicity2=R_igraph_0orvector_to_SEXP(&c_multiplicity2)); + igraph_vector_destroy(&c_multiplicity2); + IGRAPH_FINALLY_CLEAN(1); + SET_VECTOR_ELT(result, 0, proj1); + SET_VECTOR_ELT(result, 1, proj2); + SET_VECTOR_ELT(result, 2, multiplicity1); + SET_VECTOR_ELT(result, 3, multiplicity2); + SET_STRING_ELT(names, 0, CREATE_STRING_VECTOR("proj1")); + SET_STRING_ELT(names, 1, CREATE_STRING_VECTOR("proj2")); + SET_STRING_ELT(names, 2, CREATE_STRING_VECTOR("multiplicity1")); + SET_STRING_ELT(names, 3, CREATE_STRING_VECTOR("multiplicity2")); + SET_NAMES(result, names); + UNPROTECT(5); + + UNPROTECT(1); + return(result); +} + +SEXP R_igraph_solve_lsap(SEXP px, SEXP pnc) { + + igraph_matrix_t x; + igraph_integer_t nc = INTEGER(pnc)[0]; + igraph_vector_int_t p; + SEXP result; + + R_SEXP_to_matrix(px, &x); + + igraph_vector_int_init(&p, nc); + IGRAPH_FINALLY(igraph_vector_int_destroy, &p); + + igraph_solve_lsap(&x, nc, &p); + + PROTECT(result = R_igraph_vector_int_to_SEXP(&p)); + igraph_vector_int_destroy(&p); + IGRAPH_FINALLY_CLEAN(1); + + UNPROTECT(1); + return result; +} + +SEXP R_igraph_adjacent_vertices(SEXP pgraph, SEXP pv, SEXP pmode) { + + igraph_t graph; + igraph_vs_t vs; + igraph_vit_t vit; + igraph_integer_t mode = (igraph_integer_t) REAL(pmode)[0]; + SEXP result; + size_t i, n; + igraph_lazy_adjlist_t adjlist; + + R_SEXP_to_igraph(pgraph, &graph); + R_SEXP_to_igraph_vs(pv, &graph, &vs); + IGRAPH_FINALLY(igraph_vs_destroy, &vs); + + igraph_vit_create(&graph, vs, &vit); + IGRAPH_FINALLY(igraph_vit_destroy, &vit); + n = IGRAPH_VIT_SIZE(vit); + + igraph_lazy_adjlist_init( + &graph, &adjlist, mode, + /* loops = */ IGRAPH_LOOPS_TWICE, + /* multiple = */ IGRAPH_MULTIPLE + ); + IGRAPH_FINALLY(igraph_lazy_adjlist_destroy, &adjlist); + + PROTECT(result = NEW_LIST(n)); + for (IGRAPH_VIT_RESET(vit), i=0; + !IGRAPH_VIT_END(vit); + IGRAPH_VIT_NEXT(vit), i++) { + long int vid = IGRAPH_VIT_GET(vit); + igraph_vector_int_t *neis = igraph_lazy_adjlist_get(&adjlist, vid); + SET_VECTOR_ELT(result, i, R_igraph_vector_int_to_SEXP(neis)); + } + + igraph_lazy_adjlist_destroy(&adjlist); + igraph_vit_destroy(&vit); + igraph_vs_destroy(&vs); + IGRAPH_FINALLY_CLEAN(3); + + UNPROTECT(1); + return result; +} + +SEXP R_igraph_incident_edges(SEXP pgraph, SEXP pe, SEXP pmode) { + + igraph_t graph; + igraph_vs_t vs; + igraph_vit_t vit; + igraph_integer_t mode = (igraph_integer_t) REAL(pmode)[0]; + SEXP result; + size_t i, n; + igraph_lazy_inclist_t adjlist; + + R_SEXP_to_igraph(pgraph, &graph); + R_SEXP_to_igraph_vs(pe, &graph, &vs); + IGRAPH_FINALLY(igraph_vs_destroy, &vs); + + igraph_vit_create(&graph, vs, &vit); + IGRAPH_FINALLY(igraph_vit_destroy, &vit); + n = IGRAPH_VIT_SIZE(vit); + + igraph_lazy_inclist_init(&graph, &adjlist, mode, IGRAPH_LOOPS_TWICE); + IGRAPH_FINALLY(igraph_lazy_inclist_destroy, &adjlist); + + PROTECT(result = NEW_LIST(n)); + for (IGRAPH_VIT_RESET(vit), i=0; + !IGRAPH_VIT_END(vit); + IGRAPH_VIT_NEXT(vit), i++) { + long int eid = IGRAPH_VIT_GET(vit); + igraph_vector_int_t *neis = igraph_lazy_inclist_get(&adjlist, eid); + SET_VECTOR_ELT(result, i, R_igraph_vector_int_to_SEXP(neis)); + } + + igraph_lazy_inclist_destroy(&adjlist); + igraph_vit_destroy(&vit); + igraph_vs_destroy(&vs); + IGRAPH_FINALLY_CLEAN(3); + + UNPROTECT(1); + return result; +} + +/***********************************************/ +/* THE REST IS GENERATED BY stimulus */ +/***********************************************/ + +/*-------------------------------------------/ +/ igraph_empty / +/-------------------------------------------*/ +SEXP R_igraph_empty(SEXP n, SEXP directed) { + /* Declarations */ + igraph_t c_graph; + igraph_integer_t c_n; + igraph_bool_t c_directed; + SEXP graph; + + SEXP r_result; + /* Convert input */ + c_n=INTEGER(n)[0]; + c_directed=LOGICAL(directed)[0]; + /* Call igraph */ + igraph_empty(&c_graph, c_n, c_directed); + + /* Convert output */ + IGRAPH_FINALLY(igraph_destroy, &c_graph); + PROTECT(graph=R_igraph_to_SEXP(&c_graph)); + igraph_destroy(&c_graph); + IGRAPH_FINALLY_CLEAN(1); + r_result = graph; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_vcount / +/-------------------------------------------*/ +SEXP R_igraph_vcount(SEXP graph) { + /* Declarations */ + igraph_t c_graph; + igraph_integer_t c_result; + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + /* Call igraph */ + c_result= igraph_vcount(&c_graph); + + /* Convert output */ + + PROTECT(r_result=NEW_INTEGER(1)); + INTEGER(r_result)[0]=c_result; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_full_citation / +/-------------------------------------------*/ +SEXP R_igraph_full_citation(SEXP n, SEXP directed) { + /* Declarations */ + igraph_t c_graph; + igraph_integer_t c_n; + igraph_bool_t c_directed; + SEXP graph; + + SEXP r_result; + /* Convert input */ + c_n=INTEGER(n)[0]; + c_directed=LOGICAL(directed)[0]; + /* Call igraph */ + igraph_full_citation(&c_graph, c_n, c_directed); + + /* Convert output */ + IGRAPH_FINALLY(igraph_destroy, &c_graph); + PROTECT(graph=R_igraph_to_SEXP(&c_graph)); + igraph_destroy(&c_graph); + IGRAPH_FINALLY_CLEAN(1); + r_result = graph; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_extended_chordal_ring / +/-------------------------------------------*/ +SEXP R_igraph_extended_chordal_ring(SEXP nodes, SEXP W, SEXP directed) { + /* Declarations */ + igraph_t c_graph; + igraph_integer_t c_nodes; + igraph_matrix_t c_W; + igraph_bool_t c_directed; + SEXP graph; + + SEXP r_result; + /* Convert input */ + c_nodes=INTEGER(nodes)[0]; + R_SEXP_to_matrix(W, &c_W); + c_directed=LOGICAL(directed)[0]; + /* Call igraph */ + igraph_extended_chordal_ring(&c_graph, c_nodes, &c_W, c_directed); + + /* Convert output */ + IGRAPH_FINALLY(igraph_destroy, &c_graph); + PROTECT(graph=R_igraph_to_SEXP(&c_graph)); + igraph_destroy(&c_graph); + IGRAPH_FINALLY_CLEAN(1); + r_result = graph; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_lcf_vector / +/-------------------------------------------*/ +SEXP R_igraph_lcf_vector(SEXP n, SEXP shifts, SEXP repeats) { + /* Declarations */ + igraph_t c_graph; + igraph_integer_t c_n; + igraph_vector_t c_shifts; + igraph_integer_t c_repeats; + SEXP graph; + + SEXP r_result; + /* Convert input */ + c_n=INTEGER(n)[0]; + R_SEXP_to_vector(shifts, &c_shifts); + c_repeats=INTEGER(repeats)[0]; + /* Call igraph */ + igraph_lcf_vector(&c_graph, c_n, &c_shifts, c_repeats); + + /* Convert output */ + IGRAPH_FINALLY(igraph_destroy, &c_graph); + PROTECT(graph=R_igraph_to_SEXP(&c_graph)); + igraph_destroy(&c_graph); + IGRAPH_FINALLY_CLEAN(1); + r_result = graph; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_adjlist / +/-------------------------------------------*/ +SEXP R_igraph_adjlist(SEXP adjlist, SEXP mode, SEXP duplicate) { + /* Declarations */ + igraph_t c_graph; + igraph_adjlist_t c_adjlist; + igraph_neimode_t c_mode; + igraph_bool_t c_duplicate; + SEXP graph; + + SEXP r_result; + /* Convert input */ + if (0 != R_SEXP_to_igraph_adjlist(adjlist, &c_adjlist)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + c_mode = (igraph_neimode_t) Rf_asInteger(mode); + c_duplicate=LOGICAL(duplicate)[0]; + /* Call igraph */ + igraph_adjlist(&c_graph, &c_adjlist, c_mode, c_duplicate); + + /* Convert output */ + IGRAPH_FINALLY(igraph_destroy, &c_graph); + PROTECT(graph=R_igraph_to_SEXP(&c_graph)); + igraph_destroy(&c_graph); + IGRAPH_FINALLY_CLEAN(1); + r_result = graph; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_full_bipartite / +/-------------------------------------------*/ +SEXP R_igraph_full_bipartite(SEXP n1, SEXP n2, SEXP directed, SEXP mode) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_bool_t c_types; + igraph_integer_t c_n1; + igraph_integer_t c_n2; + igraph_bool_t c_directed; + igraph_neimode_t c_mode; + SEXP graph; + SEXP types; + + SEXP r_result, r_names; + /* Convert input */ + if (0 != igraph_vector_bool_init(&c_types, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_bool_destroy, &c_types); + types=R_GlobalEnv; /* hack to have a non-NULL value */ + c_n1=INTEGER(n1)[0]; + c_n2=INTEGER(n2)[0]; + c_directed=LOGICAL(directed)[0]; + c_mode = (igraph_neimode_t) Rf_asInteger(mode); + /* Call igraph */ + igraph_full_bipartite(&c_graph, (isNull(types) ? 0 : &c_types), c_n1, c_n2, c_directed, c_mode); + + /* Convert output */ + PROTECT(r_result=NEW_LIST(2)); + PROTECT(r_names=NEW_CHARACTER(2)); + IGRAPH_FINALLY(igraph_destroy, &c_graph); + PROTECT(graph=R_igraph_to_SEXP(&c_graph)); + igraph_destroy(&c_graph); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(types=R_igraph_0orvector_bool_to_SEXP(&c_types)); + igraph_vector_bool_destroy(&c_types); + IGRAPH_FINALLY_CLEAN(1); + SET_VECTOR_ELT(r_result, 0, graph); + SET_VECTOR_ELT(r_result, 1, types); + SET_STRING_ELT(r_names, 0, CREATE_STRING_VECTOR("graph")); + SET_STRING_ELT(r_names, 1, CREATE_STRING_VECTOR("types")); + SET_NAMES(r_result, r_names); + UNPROTECT(3); + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_realize_degree_sequence / +/-------------------------------------------*/ +SEXP R_igraph_realize_degree_sequence(SEXP out_deg, SEXP in_deg, SEXP allowed_edge_types, SEXP method) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_t c_out_deg; + igraph_vector_t c_in_deg; + igraph_edge_type_sw_t c_allowed_edge_types; + igraph_realize_degseq_t c_method; + SEXP graph; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_vector(out_deg, &c_out_deg); + if (!isNull(in_deg)) { R_SEXP_to_vector(in_deg, &c_in_deg); } + c_allowed_edge_types = (igraph_edge_type_sw_t) Rf_asInteger(allowed_edge_types); + c_method = (igraph_realize_degseq_t) Rf_asInteger(method); + /* Call igraph */ + igraph_realize_degree_sequence(&c_graph, &c_out_deg, (isNull(in_deg) ? 0 : &c_in_deg), c_allowed_edge_types, c_method); + + /* Convert output */ + IGRAPH_FINALLY(igraph_destroy, &c_graph); + PROTECT(graph=R_igraph_to_SEXP(&c_graph)); + igraph_destroy(&c_graph); + IGRAPH_FINALLY_CLEAN(1); + r_result = graph; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_rewire_edges / +/-------------------------------------------*/ +SEXP R_igraph_rewire_edges(SEXP graph, SEXP prob, SEXP loops, SEXP multiple) { + /* Declarations */ + igraph_t c_graph; + igraph_real_t c_prob; + igraph_bool_t c_loops; + igraph_bool_t c_multiple; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph_copy(graph, &c_graph); + IGRAPH_FINALLY(igraph_destroy, &c_graph); + c_prob=REAL(prob)[0]; + c_loops=LOGICAL(loops)[0]; + c_multiple=LOGICAL(multiple)[0]; + /* Call igraph */ + igraph_rewire_edges(&c_graph, c_prob, c_loops, c_multiple); + + /* Convert output */ + PROTECT(graph=R_igraph_to_SEXP(&c_graph)); + igraph_destroy(&c_graph); + IGRAPH_FINALLY_CLEAN(1); + r_result = graph; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_rewire_directed_edges / +/-------------------------------------------*/ +SEXP R_igraph_rewire_directed_edges(SEXP graph, SEXP prob, SEXP loops, SEXP mode) { + /* Declarations */ + igraph_t c_graph; + igraph_real_t c_prob; + igraph_bool_t c_loops; + igraph_neimode_t c_mode; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph_copy(graph, &c_graph); + IGRAPH_FINALLY(igraph_destroy, &c_graph); + c_prob=REAL(prob)[0]; + c_loops=LOGICAL(loops)[0]; + c_mode = (igraph_neimode_t) Rf_asInteger(mode); + /* Call igraph */ + igraph_rewire_directed_edges(&c_graph, c_prob, c_loops, c_mode); + + /* Convert output */ + PROTECT(graph=R_igraph_to_SEXP(&c_graph)); + igraph_destroy(&c_graph); + IGRAPH_FINALLY_CLEAN(1); + r_result = graph; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_forest_fire_game / +/-------------------------------------------*/ +SEXP R_igraph_forest_fire_game(SEXP nodes, SEXP fw_prob, SEXP bw_factor, SEXP ambs, SEXP directed) { + /* Declarations */ + igraph_t c_graph; + igraph_integer_t c_nodes; + igraph_real_t c_fw_prob; + igraph_real_t c_bw_factor; + igraph_integer_t c_ambs; + igraph_bool_t c_directed; + SEXP graph; + + SEXP r_result; + /* Convert input */ + c_nodes=INTEGER(nodes)[0]; + c_fw_prob=REAL(fw_prob)[0]; + c_bw_factor=REAL(bw_factor)[0]; + c_ambs=INTEGER(ambs)[0]; + c_directed=LOGICAL(directed)[0]; + /* Call igraph */ + igraph_forest_fire_game(&c_graph, c_nodes, c_fw_prob, c_bw_factor, c_ambs, c_directed); + + /* Convert output */ + IGRAPH_FINALLY(igraph_destroy, &c_graph); + PROTECT(graph=R_igraph_to_SEXP(&c_graph)); + igraph_destroy(&c_graph); + IGRAPH_FINALLY_CLEAN(1); + r_result = graph; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_static_fitness_game / +/-------------------------------------------*/ +SEXP R_igraph_static_fitness_game(SEXP no_of_edges, SEXP fitness_out, SEXP fitness_in, SEXP loops, SEXP multiple) { + /* Declarations */ + igraph_t c_graph; + igraph_integer_t c_no_of_edges; + igraph_vector_t c_fitness_out; + igraph_vector_t c_fitness_in; + igraph_bool_t c_loops; + igraph_bool_t c_multiple; + SEXP graph; + + SEXP r_result; + /* Convert input */ + c_no_of_edges=INTEGER(no_of_edges)[0]; + R_SEXP_to_vector(fitness_out, &c_fitness_out); + if (!isNull(fitness_in)) { R_SEXP_to_vector(fitness_in, &c_fitness_in); } + c_loops=LOGICAL(loops)[0]; + c_multiple=LOGICAL(multiple)[0]; + /* Call igraph */ + igraph_static_fitness_game(&c_graph, c_no_of_edges, &c_fitness_out, (isNull(fitness_in) ? 0 : &c_fitness_in), c_loops, c_multiple); + + /* Convert output */ + IGRAPH_FINALLY(igraph_destroy, &c_graph); + PROTECT(graph=R_igraph_to_SEXP(&c_graph)); + igraph_destroy(&c_graph); + IGRAPH_FINALLY_CLEAN(1); + r_result = graph; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_static_power_law_game / +/-------------------------------------------*/ +SEXP R_igraph_static_power_law_game(SEXP no_of_nodes, SEXP no_of_edges, SEXP exponent_out, SEXP exponent_in, SEXP loops, SEXP multiple, SEXP finite_size_correction) { + /* Declarations */ + igraph_t c_graph; + igraph_integer_t c_no_of_nodes; + igraph_integer_t c_no_of_edges; + igraph_real_t c_exponent_out; + igraph_real_t c_exponent_in; + igraph_bool_t c_loops; + igraph_bool_t c_multiple; + igraph_bool_t c_finite_size_correction; + SEXP graph; + + SEXP r_result; + /* Convert input */ + c_no_of_nodes=INTEGER(no_of_nodes)[0]; + c_no_of_edges=INTEGER(no_of_edges)[0]; + c_exponent_out=REAL(exponent_out)[0]; + c_exponent_in=REAL(exponent_in)[0]; + c_loops=LOGICAL(loops)[0]; + c_multiple=LOGICAL(multiple)[0]; + c_finite_size_correction=LOGICAL(finite_size_correction)[0]; + /* Call igraph */ + igraph_static_power_law_game(&c_graph, c_no_of_nodes, c_no_of_edges, c_exponent_out, c_exponent_in, c_loops, c_multiple, c_finite_size_correction); + + /* Convert output */ + IGRAPH_FINALLY(igraph_destroy, &c_graph); + PROTECT(graph=R_igraph_to_SEXP(&c_graph)); + igraph_destroy(&c_graph); + IGRAPH_FINALLY_CLEAN(1); + r_result = graph; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_k_regular_game / +/-------------------------------------------*/ +SEXP R_igraph_k_regular_game(SEXP no_of_nodes, SEXP k, SEXP directed, SEXP multiple) { + /* Declarations */ + igraph_t c_graph; + igraph_integer_t c_no_of_nodes; + igraph_integer_t c_k; + igraph_bool_t c_directed; + igraph_bool_t c_multiple; + SEXP graph; + + SEXP r_result; + /* Convert input */ + c_no_of_nodes=INTEGER(no_of_nodes)[0]; + c_k=INTEGER(k)[0]; + c_directed=LOGICAL(directed)[0]; + c_multiple=LOGICAL(multiple)[0]; + /* Call igraph */ + igraph_k_regular_game(&c_graph, c_no_of_nodes, c_k, c_directed, c_multiple); + + /* Convert output */ + IGRAPH_FINALLY(igraph_destroy, &c_graph); + PROTECT(graph=R_igraph_to_SEXP(&c_graph)); + igraph_destroy(&c_graph); + IGRAPH_FINALLY_CLEAN(1); + r_result = graph; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_sbm_game / +/-------------------------------------------*/ +SEXP R_igraph_sbm_game(SEXP n, SEXP pref_matrix, SEXP block_sizes, SEXP directed, SEXP loops) { + /* Declarations */ + igraph_t c_graph; + igraph_integer_t c_n; + igraph_matrix_t c_pref_matrix; + igraph_vector_int_t c_block_sizes; + igraph_bool_t c_directed; + igraph_bool_t c_loops; + SEXP graph; + + SEXP r_result; + /* Convert input */ + c_n=INTEGER(n)[0]; + R_SEXP_to_matrix(pref_matrix, &c_pref_matrix); + R_SEXP_to_vector_int(block_sizes, &c_block_sizes); + c_directed=LOGICAL(directed)[0]; + c_loops=LOGICAL(loops)[0]; + /* Call igraph */ + igraph_sbm_game(&c_graph, c_n, &c_pref_matrix, &c_block_sizes, c_directed, c_loops); + + /* Convert output */ + IGRAPH_FINALLY(igraph_destroy, &c_graph); + PROTECT(graph=R_igraph_to_SEXP(&c_graph)); + igraph_destroy(&c_graph); + IGRAPH_FINALLY_CLEAN(1); + r_result = graph; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_hsbm_game / +/-------------------------------------------*/ +SEXP R_igraph_hsbm_game(SEXP n, SEXP m, SEXP rho, SEXP C, SEXP p) { + /* Declarations */ + igraph_t c_graph; + igraph_integer_t c_n; + igraph_integer_t c_m; + igraph_vector_t c_rho; + igraph_matrix_t c_C; + igraph_real_t c_p; + SEXP graph; + + SEXP r_result; + /* Convert input */ + c_n=INTEGER(n)[0]; + c_m=INTEGER(m)[0]; + R_SEXP_to_vector(rho, &c_rho); + R_SEXP_to_matrix(C, &c_C); + c_p=REAL(p)[0]; + /* Call igraph */ + igraph_hsbm_game(&c_graph, c_n, c_m, &c_rho, &c_C, c_p); + + /* Convert output */ + IGRAPH_FINALLY(igraph_destroy, &c_graph); + PROTECT(graph=R_igraph_to_SEXP(&c_graph)); + igraph_destroy(&c_graph); + IGRAPH_FINALLY_CLEAN(1); + r_result = graph; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_hsbm_list_game / +/-------------------------------------------*/ +SEXP R_igraph_hsbm_list_game(SEXP n, SEXP mlist, SEXP rholist, SEXP Clist, SEXP p) { + /* Declarations */ + igraph_t c_graph; + igraph_integer_t c_n; + igraph_vector_int_t c_mlist; + igraph_vector_ptr_t c_rholist; + igraph_vector_ptr_t c_Clist; + igraph_real_t c_p; + SEXP graph; + + SEXP r_result; + /* Convert input */ + c_n=INTEGER(n)[0]; + R_SEXP_to_vector_int(mlist, &c_mlist); + R_igraph_SEXP_to_vectorlist(rholist, &c_rholist); + R_igraph_SEXP_to_matrixlist(Clist, &c_Clist); + c_p=REAL(p)[0]; + /* Call igraph */ + igraph_hsbm_list_game(&c_graph, c_n, &c_mlist, &c_rholist, &c_Clist, c_p); + + /* Convert output */ + IGRAPH_FINALLY(igraph_destroy, &c_graph); + PROTECT(graph=R_igraph_to_SEXP(&c_graph)); + igraph_destroy(&c_graph); + IGRAPH_FINALLY_CLEAN(1); + r_result = graph; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_correlated_game / +/-------------------------------------------*/ +SEXP R_igraph_correlated_game(SEXP old_graph, SEXP corr, SEXP p, SEXP permutation) { + /* Declarations */ + igraph_t c_old_graph; + igraph_t c_new_graph; + igraph_real_t c_corr; + igraph_real_t c_p; + igraph_vector_t c_permutation; + SEXP new_graph; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(old_graph, &c_old_graph); + c_corr=REAL(corr)[0]; + c_p=REAL(p)[0]; + if (!isNull(permutation)) { R_SEXP_to_vector(permutation, &c_permutation); } + /* Call igraph */ + igraph_correlated_game(&c_old_graph, &c_new_graph, c_corr, c_p, (isNull(permutation) ? 0 : &c_permutation)); + + /* Convert output */ + IGRAPH_FINALLY(igraph_destroy, &c_new_graph); + PROTECT(new_graph=R_igraph_to_SEXP(&c_new_graph)); + igraph_destroy(&c_new_graph); + IGRAPH_FINALLY_CLEAN(1); + r_result = new_graph; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_correlated_pair_game / +/-------------------------------------------*/ +SEXP R_igraph_correlated_pair_game(SEXP n, SEXP corr, SEXP p, SEXP directed, SEXP permutation) { + /* Declarations */ + igraph_t c_graph1; + igraph_t c_graph2; + igraph_integer_t c_n; + igraph_real_t c_corr; + igraph_real_t c_p; + igraph_bool_t c_directed; + igraph_vector_t c_permutation; + SEXP graph1; + SEXP graph2; + + SEXP r_result, r_names; + /* Convert input */ + c_n=INTEGER(n)[0]; + c_corr=REAL(corr)[0]; + c_p=REAL(p)[0]; + c_directed=LOGICAL(directed)[0]; + if (!isNull(permutation)) { R_SEXP_to_vector(permutation, &c_permutation); } + /* Call igraph */ + igraph_correlated_pair_game(&c_graph1, &c_graph2, c_n, c_corr, c_p, c_directed, (isNull(permutation) ? 0 : &c_permutation)); + + /* Convert output */ + PROTECT(r_result=NEW_LIST(2)); + PROTECT(r_names=NEW_CHARACTER(2)); + IGRAPH_FINALLY(igraph_destroy, &c_graph1); + PROTECT(graph1=R_igraph_to_SEXP(&c_graph1)); + igraph_destroy(&c_graph1); + IGRAPH_FINALLY_CLEAN(1); + IGRAPH_FINALLY(igraph_destroy, &c_graph2); + PROTECT(graph2=R_igraph_to_SEXP(&c_graph2)); + igraph_destroy(&c_graph2); + IGRAPH_FINALLY_CLEAN(1); + SET_VECTOR_ELT(r_result, 0, graph1); + SET_VECTOR_ELT(r_result, 1, graph2); + SET_STRING_ELT(r_names, 0, CREATE_STRING_VECTOR("graph1")); + SET_STRING_ELT(r_names, 1, CREATE_STRING_VECTOR("graph2")); + SET_NAMES(r_result, r_names); + UNPROTECT(3); + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_dot_product_game / +/-------------------------------------------*/ +SEXP R_igraph_dot_product_game(SEXP vecs, SEXP directed) { + /* Declarations */ + igraph_t c_graph; + igraph_matrix_t c_vecs; + igraph_bool_t c_directed; + SEXP graph; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_matrix(vecs, &c_vecs); + c_directed=LOGICAL(directed)[0]; + /* Call igraph */ + igraph_dot_product_game(&c_graph, &c_vecs, c_directed); + + /* Convert output */ + IGRAPH_FINALLY(igraph_destroy, &c_graph); + PROTECT(graph=R_igraph_to_SEXP(&c_graph)); + igraph_destroy(&c_graph); + IGRAPH_FINALLY_CLEAN(1); + r_result = graph; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_sample_sphere_surface / +/-------------------------------------------*/ +SEXP R_igraph_sample_sphere_surface(SEXP dim, SEXP n, SEXP radius, SEXP positive) { + /* Declarations */ + igraph_integer_t c_dim; + igraph_integer_t c_n; + igraph_real_t c_radius; + igraph_bool_t c_positive; + igraph_matrix_t c_res; + SEXP res; + + SEXP r_result; + /* Convert input */ + c_dim=INTEGER(dim)[0]; + c_n=INTEGER(n)[0]; + c_radius=REAL(radius)[0]; + c_positive=LOGICAL(positive)[0]; + if (0 != igraph_matrix_init(&c_res, 0, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_matrix_destroy, &c_res); + /* Call igraph */ + igraph_sample_sphere_surface(c_dim, c_n, c_radius, c_positive, &c_res); + + /* Convert output */ + PROTECT(res=R_igraph_matrix_to_SEXP(&c_res)); + igraph_matrix_destroy(&c_res); + IGRAPH_FINALLY_CLEAN(1); + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_sample_sphere_volume / +/-------------------------------------------*/ +SEXP R_igraph_sample_sphere_volume(SEXP dim, SEXP n, SEXP radius, SEXP positive) { + /* Declarations */ + igraph_integer_t c_dim; + igraph_integer_t c_n; + igraph_real_t c_radius; + igraph_bool_t c_positive; + igraph_matrix_t c_res; + SEXP res; + + SEXP r_result; + /* Convert input */ + c_dim=INTEGER(dim)[0]; + c_n=INTEGER(n)[0]; + c_radius=REAL(radius)[0]; + c_positive=LOGICAL(positive)[0]; + if (0 != igraph_matrix_init(&c_res, 0, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_matrix_destroy, &c_res); + /* Call igraph */ + igraph_sample_sphere_volume(c_dim, c_n, c_radius, c_positive, &c_res); + + /* Convert output */ + PROTECT(res=R_igraph_matrix_to_SEXP(&c_res)); + igraph_matrix_destroy(&c_res); + IGRAPH_FINALLY_CLEAN(1); + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_sample_dirichlet / +/-------------------------------------------*/ +SEXP R_igraph_sample_dirichlet(SEXP n, SEXP alpha) { + /* Declarations */ + igraph_integer_t c_n; + igraph_vector_t c_alpha; + igraph_matrix_t c_res; + SEXP res; + + SEXP r_result; + /* Convert input */ + c_n=INTEGER(n)[0]; + R_SEXP_to_vector(alpha, &c_alpha); + if (0 != igraph_matrix_init(&c_res, 0, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_matrix_destroy, &c_res); + /* Call igraph */ + igraph_sample_dirichlet(c_n, &c_alpha, &c_res); + + /* Convert output */ + PROTECT(res=R_igraph_matrix_to_SEXP(&c_res)); + igraph_matrix_destroy(&c_res); + IGRAPH_FINALLY_CLEAN(1); + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_closeness / +/-------------------------------------------*/ +SEXP R_igraph_closeness(SEXP graph, SEXP vids, SEXP mode, SEXP weights, SEXP normalized) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_t c_res; + igraph_vector_t c_reachable_count; + igraph_bool_t c_all_reachable; + igraph_vs_t c_vids; + igraph_neimode_t c_mode; + igraph_vector_t c_weights; + igraph_bool_t c_normalized; + SEXP res; + SEXP reachable_count; + SEXP all_reachable; + + SEXP r_result, r_names; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != igraph_vector_init(&c_res, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_res); + if (0 != igraph_vector_init(&c_reachable_count, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_reachable_count); + reachable_count=R_GlobalEnv; /* hack to have a non-NULL value */ + R_SEXP_to_igraph_vs(vids, &c_graph, &c_vids); + c_mode = (igraph_neimode_t) Rf_asInteger(mode); + if (!isNull(weights)) { R_SEXP_to_vector(weights, &c_weights); } + c_normalized=LOGICAL(normalized)[0]; + /* Call igraph */ + igraph_closeness(&c_graph, &c_res, (isNull(reachable_count) ? 0 : &c_reachable_count), &c_all_reachable, c_vids, c_mode, (isNull(weights) ? 0 : &c_weights), c_normalized); + + /* Convert output */ + PROTECT(r_result=NEW_LIST(3)); + PROTECT(r_names=NEW_CHARACTER(3)); + PROTECT(res=R_igraph_vector_to_SEXP(&c_res)); + igraph_vector_destroy(&c_res); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(reachable_count=R_igraph_0orvector_to_SEXP(&c_reachable_count)); + igraph_vector_destroy(&c_reachable_count); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(all_reachable=NEW_LOGICAL(1)); + LOGICAL(all_reachable)[0]=c_all_reachable; + igraph_vs_destroy(&c_vids); + SET_VECTOR_ELT(r_result, 0, res); + SET_VECTOR_ELT(r_result, 1, reachable_count); + SET_VECTOR_ELT(r_result, 2, all_reachable); + SET_STRING_ELT(r_names, 0, CREATE_STRING_VECTOR("res")); + SET_STRING_ELT(r_names, 1, CREATE_STRING_VECTOR("reachable_count")); + SET_STRING_ELT(r_names, 2, CREATE_STRING_VECTOR("all_reachable")); + SET_NAMES(r_result, r_names); + UNPROTECT(4); + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_closeness_cutoff / +/-------------------------------------------*/ +SEXP R_igraph_closeness_cutoff(SEXP graph, SEXP vids, SEXP mode, SEXP weights, SEXP normalized, SEXP cutoff) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_t c_res; + igraph_vector_t c_reachable_count; + igraph_bool_t c_all_reachable; + igraph_vs_t c_vids; + igraph_neimode_t c_mode; + igraph_vector_t c_weights; + igraph_bool_t c_normalized; + igraph_real_t c_cutoff; + SEXP res; + SEXP reachable_count; + SEXP all_reachable; + + SEXP r_result, r_names; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != igraph_vector_init(&c_res, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_res); + if (0 != igraph_vector_init(&c_reachable_count, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_reachable_count); + reachable_count=R_GlobalEnv; /* hack to have a non-NULL value */ + R_SEXP_to_igraph_vs(vids, &c_graph, &c_vids); + c_mode = (igraph_neimode_t) Rf_asInteger(mode); + if (!isNull(weights)) { R_SEXP_to_vector(weights, &c_weights); } + c_normalized=LOGICAL(normalized)[0]; + c_cutoff=REAL(cutoff)[0]; + /* Call igraph */ + igraph_closeness_cutoff(&c_graph, &c_res, (isNull(reachable_count) ? 0 : &c_reachable_count), &c_all_reachable, c_vids, c_mode, (isNull(weights) ? 0 : &c_weights), c_normalized, c_cutoff); + + /* Convert output */ + PROTECT(r_result=NEW_LIST(3)); + PROTECT(r_names=NEW_CHARACTER(3)); + PROTECT(res=R_igraph_vector_to_SEXP(&c_res)); + igraph_vector_destroy(&c_res); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(reachable_count=R_igraph_0orvector_to_SEXP(&c_reachable_count)); + igraph_vector_destroy(&c_reachable_count); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(all_reachable=NEW_LOGICAL(1)); + LOGICAL(all_reachable)[0]=c_all_reachable; + igraph_vs_destroy(&c_vids); + SET_VECTOR_ELT(r_result, 0, res); + SET_VECTOR_ELT(r_result, 1, reachable_count); + SET_VECTOR_ELT(r_result, 2, all_reachable); + SET_STRING_ELT(r_names, 0, CREATE_STRING_VECTOR("res")); + SET_STRING_ELT(r_names, 1, CREATE_STRING_VECTOR("reachable_count")); + SET_STRING_ELT(r_names, 2, CREATE_STRING_VECTOR("all_reachable")); + SET_NAMES(r_result, r_names); + UNPROTECT(4); + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_get_all_shortest_paths / +/-------------------------------------------*/ +SEXP R_igraph_get_all_shortest_paths(SEXP graph, SEXP from, SEXP to, SEXP mode) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_ptr_t c_res; + igraph_vector_t c_nrgeo; + igraph_integer_t c_from; + igraph_vs_t c_to; + igraph_neimode_t c_mode; + SEXP res; + SEXP nrgeo; + + SEXP r_result, r_names; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != igraph_vector_ptr_init(&c_res, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(R_igraph_vectorlist_destroy, &c_res); + if (0 != igraph_vector_init(&c_nrgeo, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_nrgeo); + c_from = (igraph_integer_t) REAL(from)[0]; + R_SEXP_to_igraph_vs(to, &c_graph, &c_to); + c_mode = (igraph_neimode_t) Rf_asInteger(mode); + /* Call igraph */ + igraph_get_all_shortest_paths(&c_graph, &c_res, &c_nrgeo, c_from, c_to, c_mode); + + /* Convert output */ + PROTECT(r_result=NEW_LIST(2)); + PROTECT(r_names=NEW_CHARACTER(2)); + PROTECT(res=R_igraph_vectorlist_to_SEXP_p1(&c_res)); + R_igraph_vectorlist_destroy(&c_res); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(nrgeo=R_igraph_vector_to_SEXP(&c_nrgeo)); + igraph_vector_destroy(&c_nrgeo); + IGRAPH_FINALLY_CLEAN(1); + igraph_vs_destroy(&c_to); + SET_VECTOR_ELT(r_result, 0, res); + SET_VECTOR_ELT(r_result, 1, nrgeo); + SET_STRING_ELT(r_names, 0, CREATE_STRING_VECTOR("res")); + SET_STRING_ELT(r_names, 1, CREATE_STRING_VECTOR("nrgeo")); + SET_NAMES(r_result, r_names); + UNPROTECT(3); + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_get_all_shortest_paths_dijkstra / +/-------------------------------------------*/ +SEXP R_igraph_get_all_shortest_paths_dijkstra(SEXP graph, SEXP from, SEXP to, SEXP weights, SEXP mode) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_ptr_t c_res; + igraph_vector_t c_nrgeo; + igraph_integer_t c_from; + igraph_vs_t c_to; + igraph_vector_t c_weights; + igraph_neimode_t c_mode; + SEXP res; + SEXP nrgeo; + + SEXP r_result, r_names; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != igraph_vector_ptr_init(&c_res, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(R_igraph_vectorlist_destroy, &c_res); + if (0 != igraph_vector_init(&c_nrgeo, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_nrgeo); + c_from = (igraph_integer_t) REAL(from)[0]; + R_SEXP_to_igraph_vs(to, &c_graph, &c_to); + if (!isNull(weights)) { R_SEXP_to_vector(weights, &c_weights); } + c_mode = (igraph_neimode_t) Rf_asInteger(mode); + /* Call igraph */ + igraph_get_all_shortest_paths_dijkstra(&c_graph, &c_res, &c_nrgeo, c_from, c_to, (isNull(weights) ? 0 : &c_weights), c_mode); + + /* Convert output */ + PROTECT(r_result=NEW_LIST(2)); + PROTECT(r_names=NEW_CHARACTER(2)); + PROTECT(res=R_igraph_vectorlist_to_SEXP_p1(&c_res)); + R_igraph_vectorlist_destroy(&c_res); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(nrgeo=R_igraph_vector_to_SEXP(&c_nrgeo)); + igraph_vector_destroy(&c_nrgeo); + IGRAPH_FINALLY_CLEAN(1); + igraph_vs_destroy(&c_to); + SET_VECTOR_ELT(r_result, 0, res); + SET_VECTOR_ELT(r_result, 1, nrgeo); + SET_STRING_ELT(r_names, 0, CREATE_STRING_VECTOR("res")); + SET_STRING_ELT(r_names, 1, CREATE_STRING_VECTOR("nrgeo")); + SET_NAMES(r_result, r_names); + UNPROTECT(3); + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_get_all_simple_paths / +/-------------------------------------------*/ +SEXP R_igraph_get_all_simple_paths(SEXP graph, SEXP from, SEXP to, SEXP cutoff, SEXP mode) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_int_t c_res; + igraph_integer_t c_from; + igraph_vs_t c_to; + igraph_integer_t c_cutoff; + igraph_neimode_t c_mode; + SEXP res; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != igraph_vector_int_init(&c_res, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_int_destroy, &c_res); + c_from = (igraph_integer_t) REAL(from)[0]; + R_SEXP_to_igraph_vs(to, &c_graph, &c_to); + c_cutoff=INTEGER(cutoff)[0]; + c_mode = (igraph_neimode_t) Rf_asInteger(mode); + /* Call igraph */ + igraph_get_all_simple_paths(&c_graph, &c_res, c_from, c_to, c_cutoff, c_mode); + + /* Convert output */ + PROTECT(res=R_igraph_vector_int_to_SEXPp1(&c_res)); + igraph_vector_int_destroy(&c_res); + IGRAPH_FINALLY_CLEAN(1); + igraph_vs_destroy(&c_to); + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_betweenness_cutoff / +/-------------------------------------------*/ +SEXP R_igraph_betweenness_cutoff(SEXP graph, SEXP vids, SEXP directed, SEXP weights, SEXP cutoff) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_t c_res; + igraph_vs_t c_vids; + igraph_bool_t c_directed; + igraph_vector_t c_weights; + igraph_real_t c_cutoff; + SEXP res; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != igraph_vector_init(&c_res, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_res); + R_SEXP_to_igraph_vs(vids, &c_graph, &c_vids); + c_directed=LOGICAL(directed)[0]; + if (!isNull(weights)) { R_SEXP_to_vector(weights, &c_weights); } + c_cutoff=REAL(cutoff)[0]; + /* Call igraph */ + igraph_betweenness_cutoff(&c_graph, &c_res, c_vids, c_directed, (isNull(weights) ? 0 : &c_weights), c_cutoff); + + /* Convert output */ + PROTECT(res=R_igraph_vector_to_SEXP(&c_res)); + igraph_vector_destroy(&c_res); + IGRAPH_FINALLY_CLEAN(1); + igraph_vs_destroy(&c_vids); + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_edge_betweenness / +/-------------------------------------------*/ +SEXP R_igraph_edge_betweenness(SEXP graph, SEXP directed, SEXP weights) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_t c_res; + igraph_bool_t c_directed; + igraph_vector_t c_weights; + SEXP res; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != igraph_vector_init(&c_res, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_res); + c_directed=LOGICAL(directed)[0]; + if (!isNull(weights)) { R_SEXP_to_vector(weights, &c_weights); } + /* Call igraph */ + igraph_edge_betweenness(&c_graph, &c_res, c_directed, (isNull(weights) ? 0 : &c_weights)); + + /* Convert output */ + PROTECT(res=R_igraph_vector_to_SEXP(&c_res)); + igraph_vector_destroy(&c_res); + IGRAPH_FINALLY_CLEAN(1); + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_edge_betweenness_cutoff / +/-------------------------------------------*/ +SEXP R_igraph_edge_betweenness_cutoff(SEXP graph, SEXP directed, SEXP weights, SEXP cutoff) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_t c_res; + igraph_bool_t c_directed; + igraph_vector_t c_weights; + igraph_real_t c_cutoff; + SEXP res; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != igraph_vector_init(&c_res, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_res); + c_directed=LOGICAL(directed)[0]; + if (!isNull(weights)) { R_SEXP_to_vector(weights, &c_weights); } + c_cutoff=REAL(cutoff)[0]; + /* Call igraph */ + igraph_edge_betweenness_cutoff(&c_graph, &c_res, c_directed, (isNull(weights) ? 0 : &c_weights), c_cutoff); + + /* Convert output */ + PROTECT(res=R_igraph_vector_to_SEXP(&c_res)); + igraph_vector_destroy(&c_res); + IGRAPH_FINALLY_CLEAN(1); + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_harmonic_centrality_cutoff / +/-------------------------------------------*/ +SEXP R_igraph_harmonic_centrality_cutoff(SEXP graph, SEXP vids, SEXP mode, SEXP weights, SEXP normalized, SEXP cutoff) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_t c_res; + igraph_vs_t c_vids; + igraph_neimode_t c_mode; + igraph_vector_t c_weights; + igraph_bool_t c_normalized; + igraph_real_t c_cutoff; + SEXP res; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != igraph_vector_init(&c_res, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_res); + R_SEXP_to_igraph_vs(vids, &c_graph, &c_vids); + c_mode = (igraph_neimode_t) Rf_asInteger(mode); + if (!isNull(weights)) { R_SEXP_to_vector(weights, &c_weights); } + c_normalized=LOGICAL(normalized)[0]; + c_cutoff=REAL(cutoff)[0]; + /* Call igraph */ + igraph_harmonic_centrality_cutoff(&c_graph, &c_res, c_vids, c_mode, (isNull(weights) ? 0 : &c_weights), c_normalized, c_cutoff); + + /* Convert output */ + PROTECT(res=R_igraph_vector_to_SEXP(&c_res)); + igraph_vector_destroy(&c_res); + IGRAPH_FINALLY_CLEAN(1); + igraph_vs_destroy(&c_vids); + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_personalized_pagerank / +/-------------------------------------------*/ +SEXP R_igraph_personalized_pagerank(SEXP graph, SEXP algo, SEXP vids, SEXP directed, SEXP damping, SEXP personalized, SEXP weights, SEXP options) { + /* Declarations */ + igraph_t c_graph; + igraph_pagerank_algo_t c_algo; + igraph_vector_t c_vector; + igraph_real_t c_value; + igraph_vs_t c_vids; + igraph_bool_t c_directed; + igraph_real_t c_damping; + igraph_vector_t c_personalized; + igraph_vector_t c_weights; + igraph_arpack_options_t c_options1; + void* c_options; + SEXP vector; + SEXP value; + + SEXP r_result, r_names; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + c_algo = (igraph_pagerank_algo_t) Rf_asInteger(algo); + if (0 != igraph_vector_init(&c_vector, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_vector); + R_SEXP_to_igraph_vs(vids, &c_graph, &c_vids); + c_directed=LOGICAL(directed)[0]; + c_damping=REAL(damping)[0]; + if (!isNull(personalized)) { R_SEXP_to_vector(personalized, &c_personalized); } + if (!isNull(weights)) { R_SEXP_to_vector(weights, &c_weights); } + if (c_algo == IGRAPH_PAGERANK_ALGO_ARPACK) { + R_SEXP_to_igraph_arpack_options(options, &c_options1); + c_options = &c_options1; + } else { + c_options = 0; + } + /* Call igraph */ + igraph_personalized_pagerank(&c_graph, c_algo, &c_vector, &c_value, c_vids, c_directed, c_damping, (isNull(personalized) ? 0 : &c_personalized), (isNull(weights) ? 0 : &c_weights), c_options); + + /* Convert output */ + PROTECT(r_result=NEW_LIST(3)); + PROTECT(r_names=NEW_CHARACTER(3)); + PROTECT(vector=R_igraph_vector_to_SEXP(&c_vector)); + igraph_vector_destroy(&c_vector); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(value=NEW_NUMERIC(1)); + REAL(value)[0]=c_value; + igraph_vs_destroy(&c_vids); + if (c_algo == IGRAPH_PAGERANK_ALGO_ARPACK) { + PROTECT(options = R_igraph_arpack_options_to_SEXP(&c_options1)); + } else { + PROTECT(options); + } + SET_VECTOR_ELT(r_result, 0, vector); + SET_VECTOR_ELT(r_result, 1, value); + SET_VECTOR_ELT(r_result, 2, options); + SET_STRING_ELT(r_names, 0, CREATE_STRING_VECTOR("vector")); + SET_STRING_ELT(r_names, 1, CREATE_STRING_VECTOR("value")); + SET_STRING_ELT(r_names, 2, CREATE_STRING_VECTOR("options")); + SET_NAMES(r_result, r_names); + UNPROTECT(4); + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_induced_subgraph / +/-------------------------------------------*/ +SEXP R_igraph_induced_subgraph(SEXP graph, SEXP vids, SEXP impl) { + /* Declarations */ + igraph_t c_graph; + igraph_t c_res; + igraph_vs_t c_vids; + igraph_subgraph_implementation_t c_impl; + SEXP res; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + R_SEXP_to_igraph_vs(vids, &c_graph, &c_vids); + c_impl = (igraph_subgraph_implementation_t) Rf_asInteger(impl); + /* Call igraph */ + igraph_induced_subgraph(&c_graph, &c_res, c_vids, c_impl); + + /* Convert output */ + IGRAPH_FINALLY(igraph_destroy, &c_res); + PROTECT(res=R_igraph_to_SEXP(&c_res)); + igraph_destroy(&c_res); + IGRAPH_FINALLY_CLEAN(1); + igraph_vs_destroy(&c_vids); + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_subgraph_edges / +/-------------------------------------------*/ +SEXP R_igraph_subgraph_edges(SEXP graph, SEXP eids, SEXP delete_vertices) { + /* Declarations */ + igraph_t c_graph; + igraph_t c_res; + igraph_es_t c_eids; + igraph_bool_t c_delete_vertices; + SEXP res; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + R_SEXP_to_igraph_es(eids, &c_graph, &c_eids); + c_delete_vertices=LOGICAL(delete_vertices)[0]; + /* Call igraph */ + igraph_subgraph_edges(&c_graph, &c_res, c_eids, c_delete_vertices); + + /* Convert output */ + IGRAPH_FINALLY(igraph_destroy, &c_res); + PROTECT(res=R_igraph_to_SEXP(&c_res)); + igraph_destroy(&c_res); + IGRAPH_FINALLY_CLEAN(1); + igraph_es_destroy(&c_eids); + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_average_path_length_dijkstra / +/-------------------------------------------*/ +SEXP R_igraph_average_path_length_dijkstra(SEXP graph, SEXP weights, SEXP directed, SEXP unconn) { + /* Declarations */ + igraph_t c_graph; + igraph_real_t c_res; + igraph_real_t c_unconn_pairs; + igraph_vector_t c_weights; + igraph_bool_t c_directed; + igraph_bool_t c_unconn; + SEXP res; + SEXP unconn_pairs; + + SEXP r_result, r_names; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (!isNull(weights)) { R_SEXP_to_vector(weights, &c_weights); } + c_directed=LOGICAL(directed)[0]; + c_unconn=LOGICAL(unconn)[0]; + /* Call igraph */ + igraph_average_path_length_dijkstra(&c_graph, &c_res, &c_unconn_pairs, (isNull(weights) ? 0 : &c_weights), c_directed, c_unconn); + + /* Convert output */ + PROTECT(r_result=NEW_LIST(2)); + PROTECT(r_names=NEW_CHARACTER(2)); + PROTECT(res=NEW_NUMERIC(1)); + REAL(res)[0]=c_res; + PROTECT(unconn_pairs=NEW_NUMERIC(1)); + REAL(unconn_pairs)[0]=c_unconn_pairs; + SET_VECTOR_ELT(r_result, 0, res); + SET_VECTOR_ELT(r_result, 1, unconn_pairs); + SET_STRING_ELT(r_names, 0, CREATE_STRING_VECTOR("res")); + SET_STRING_ELT(r_names, 1, CREATE_STRING_VECTOR("unconnected")); + SET_NAMES(r_result, r_names); + UNPROTECT(3); + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_path_length_hist / +/-------------------------------------------*/ +SEXP R_igraph_path_length_hist(SEXP graph, SEXP directed) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_t c_res; + igraph_real_t c_unconnected; + igraph_bool_t c_directed; + SEXP res; + SEXP unconnected; + + SEXP r_result, r_names; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != igraph_vector_init(&c_res, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_res); + c_directed=LOGICAL(directed)[0]; + /* Call igraph */ + igraph_path_length_hist(&c_graph, &c_res, &c_unconnected, c_directed); + + /* Convert output */ + PROTECT(r_result=NEW_LIST(2)); + PROTECT(r_names=NEW_CHARACTER(2)); + PROTECT(res=R_igraph_vector_to_SEXP(&c_res)); + igraph_vector_destroy(&c_res); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(unconnected=NEW_NUMERIC(1)); + REAL(unconnected)[0]=c_unconnected; + SET_VECTOR_ELT(r_result, 0, res); + SET_VECTOR_ELT(r_result, 1, unconnected); + SET_STRING_ELT(r_names, 0, CREATE_STRING_VECTOR("res")); + SET_STRING_ELT(r_names, 1, CREATE_STRING_VECTOR("unconnected")); + SET_NAMES(r_result, r_names); + UNPROTECT(3); + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_simplify / +/-------------------------------------------*/ +SEXP R_igraph_simplify(SEXP graph, SEXP remove_multiple, SEXP remove_loops, SEXP edge_attr_comb) { + /* Declarations */ + igraph_t c_graph; + igraph_bool_t c_remove_multiple; + igraph_bool_t c_remove_loops; + igraph_attribute_combination_t c_edge_attr_comb; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph_copy(graph, &c_graph); + IGRAPH_FINALLY(igraph_destroy, &c_graph); + c_remove_multiple=LOGICAL(remove_multiple)[0]; + c_remove_loops=LOGICAL(remove_loops)[0]; + R_SEXP_to_attr_comb(edge_attr_comb, &c_edge_attr_comb); + /* Call igraph */ + igraph_simplify(&c_graph, c_remove_multiple, c_remove_loops, &c_edge_attr_comb); + + /* Convert output */ + PROTECT(graph=R_igraph_to_SEXP(&c_graph)); + igraph_destroy(&c_graph); + IGRAPH_FINALLY_CLEAN(1); + igraph_attribute_combination_destroy(&c_edge_attr_comb); + r_result = graph; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_feedback_arc_set / +/-------------------------------------------*/ +SEXP R_igraph_feedback_arc_set(SEXP graph, SEXP weights, SEXP algo) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_t c_result; + igraph_vector_t c_weights; + igraph_fas_algorithm_t c_algo; + SEXP result; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != igraph_vector_init(&c_result, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_result); + if (!isNull(weights)) { R_SEXP_to_vector(weights, &c_weights); } + c_algo = (igraph_fas_algorithm_t) Rf_asInteger(algo); + /* Call igraph */ + igraph_feedback_arc_set(&c_graph, &c_result, (isNull(weights) ? 0 : &c_weights), c_algo); + + /* Convert output */ + PROTECT(result=R_igraph_vector_to_SEXPp1(&c_result)); + igraph_vector_destroy(&c_result); + IGRAPH_FINALLY_CLEAN(1); + r_result = result; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_is_loop / +/-------------------------------------------*/ +SEXP R_igraph_is_loop(SEXP graph, SEXP es) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_bool_t c_res; + igraph_es_t c_es; + SEXP res; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != igraph_vector_bool_init(&c_res, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_bool_destroy, &c_res); + R_SEXP_to_igraph_es(es, &c_graph, &c_es); + /* Call igraph */ + igraph_is_loop(&c_graph, &c_res, c_es); + + /* Convert output */ + PROTECT(res=R_igraph_vector_bool_to_SEXP(&c_res)); + igraph_vector_bool_destroy(&c_res); + IGRAPH_FINALLY_CLEAN(1); + igraph_es_destroy(&c_es); + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_is_dag / +/-------------------------------------------*/ +SEXP R_igraph_is_dag(SEXP graph) { + /* Declarations */ + igraph_t c_graph; + igraph_bool_t c_res; + SEXP res; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + /* Call igraph */ + igraph_is_dag(&c_graph, &c_res); + + /* Convert output */ + PROTECT(res=NEW_LOGICAL(1)); + LOGICAL(res)[0]=c_res; + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_is_simple / +/-------------------------------------------*/ +SEXP R_igraph_is_simple(SEXP graph) { + /* Declarations */ + igraph_t c_graph; + igraph_bool_t c_res; + SEXP res; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + /* Call igraph */ + igraph_is_simple(&c_graph, &c_res); + + /* Convert output */ + PROTECT(res=NEW_LOGICAL(1)); + LOGICAL(res)[0]=c_res; + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_is_multiple / +/-------------------------------------------*/ +SEXP R_igraph_is_multiple(SEXP graph, SEXP es) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_bool_t c_res; + igraph_es_t c_es; + SEXP res; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != igraph_vector_bool_init(&c_res, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_bool_destroy, &c_res); + R_SEXP_to_igraph_es(es, &c_graph, &c_es); + /* Call igraph */ + igraph_is_multiple(&c_graph, &c_res, c_es); + + /* Convert output */ + PROTECT(res=R_igraph_vector_bool_to_SEXP(&c_res)); + igraph_vector_bool_destroy(&c_res); + IGRAPH_FINALLY_CLEAN(1); + igraph_es_destroy(&c_es); + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_has_loop / +/-------------------------------------------*/ +SEXP R_igraph_has_loop(SEXP graph) { + /* Declarations */ + igraph_t c_graph; + igraph_bool_t c_res; + SEXP res; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + /* Call igraph */ + igraph_has_loop(&c_graph, &c_res); + + /* Convert output */ + PROTECT(res=NEW_LOGICAL(1)); + LOGICAL(res)[0]=c_res; + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_has_multiple / +/-------------------------------------------*/ +SEXP R_igraph_has_multiple(SEXP graph) { + /* Declarations */ + igraph_t c_graph; + igraph_bool_t c_res; + SEXP res; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + /* Call igraph */ + igraph_has_multiple(&c_graph, &c_res); + + /* Convert output */ + PROTECT(res=NEW_LOGICAL(1)); + LOGICAL(res)[0]=c_res; + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_count_multiple / +/-------------------------------------------*/ +SEXP R_igraph_count_multiple(SEXP graph, SEXP es) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_t c_res; + igraph_es_t c_es; + SEXP res; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != igraph_vector_init(&c_res, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_res); + R_SEXP_to_igraph_es(es, &c_graph, &c_es); + /* Call igraph */ + igraph_count_multiple(&c_graph, &c_res, c_es); + + /* Convert output */ + PROTECT(res=R_igraph_vector_to_SEXP(&c_res)); + igraph_vector_destroy(&c_res); + IGRAPH_FINALLY_CLEAN(1); + igraph_es_destroy(&c_es); + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_eigenvector_centrality / +/-------------------------------------------*/ +SEXP R_igraph_eigenvector_centrality(SEXP graph, SEXP directed, SEXP scale, SEXP weights, SEXP options) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_t c_vector; + igraph_real_t c_value; + igraph_bool_t c_directed; + igraph_bool_t c_scale; + igraph_vector_t c_weights; + igraph_arpack_options_t c_options; + SEXP vector; + SEXP value; + + SEXP r_result, r_names; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != igraph_vector_init(&c_vector, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_vector); + c_directed=LOGICAL(directed)[0]; + c_scale=LOGICAL(scale)[0]; + if (!isNull(weights)) { R_SEXP_to_vector(weights, &c_weights); } + R_SEXP_to_igraph_arpack_options(options, &c_options); + /* Call igraph */ + igraph_eigenvector_centrality(&c_graph, &c_vector, &c_value, c_directed, c_scale, (isNull(weights) ? 0 : &c_weights), &c_options); + + /* Convert output */ + PROTECT(r_result=NEW_LIST(3)); + PROTECT(r_names=NEW_CHARACTER(3)); + PROTECT(vector=R_igraph_vector_to_SEXP(&c_vector)); + igraph_vector_destroy(&c_vector); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(value=NEW_NUMERIC(1)); + REAL(value)[0]=c_value; + PROTECT(options=R_igraph_arpack_options_to_SEXP(&c_options)); + SET_VECTOR_ELT(r_result, 0, vector); + SET_VECTOR_ELT(r_result, 1, value); + SET_VECTOR_ELT(r_result, 2, options); + SET_STRING_ELT(r_names, 0, CREATE_STRING_VECTOR("vector")); + SET_STRING_ELT(r_names, 1, CREATE_STRING_VECTOR("value")); + SET_STRING_ELT(r_names, 2, CREATE_STRING_VECTOR("options")); + SET_NAMES(r_result, r_names); + UNPROTECT(4); + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_hub_score / +/-------------------------------------------*/ +SEXP R_igraph_hub_score(SEXP graph, SEXP scale, SEXP weights, SEXP options) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_t c_vector; + igraph_real_t c_value; + igraph_bool_t c_scale; + igraph_vector_t c_weights; + igraph_arpack_options_t c_options; + SEXP vector; + SEXP value; + + SEXP r_result, r_names; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != igraph_vector_init(&c_vector, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_vector); + c_scale=LOGICAL(scale)[0]; + if (!isNull(weights)) { R_SEXP_to_vector(weights, &c_weights); } + R_SEXP_to_igraph_arpack_options(options, &c_options); + /* Call igraph */ + igraph_hub_score(&c_graph, &c_vector, &c_value, c_scale, (isNull(weights) ? 0 : &c_weights), &c_options); + + /* Convert output */ + PROTECT(r_result=NEW_LIST(3)); + PROTECT(r_names=NEW_CHARACTER(3)); + PROTECT(vector=R_igraph_vector_to_SEXP(&c_vector)); + igraph_vector_destroy(&c_vector); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(value=NEW_NUMERIC(1)); + REAL(value)[0]=c_value; + PROTECT(options=R_igraph_arpack_options_to_SEXP(&c_options)); + SET_VECTOR_ELT(r_result, 0, vector); + SET_VECTOR_ELT(r_result, 1, value); + SET_VECTOR_ELT(r_result, 2, options); + SET_STRING_ELT(r_names, 0, CREATE_STRING_VECTOR("vector")); + SET_STRING_ELT(r_names, 1, CREATE_STRING_VECTOR("value")); + SET_STRING_ELT(r_names, 2, CREATE_STRING_VECTOR("options")); + SET_NAMES(r_result, r_names); + UNPROTECT(4); + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_authority_score / +/-------------------------------------------*/ +SEXP R_igraph_authority_score(SEXP graph, SEXP scale, SEXP weights, SEXP options) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_t c_vector; + igraph_real_t c_value; + igraph_bool_t c_scale; + igraph_vector_t c_weights; + igraph_arpack_options_t c_options; + SEXP vector; + SEXP value; + + SEXP r_result, r_names; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != igraph_vector_init(&c_vector, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_vector); + c_scale=LOGICAL(scale)[0]; + if (!isNull(weights)) { R_SEXP_to_vector(weights, &c_weights); } + R_SEXP_to_igraph_arpack_options(options, &c_options); + /* Call igraph */ + igraph_authority_score(&c_graph, &c_vector, &c_value, c_scale, (isNull(weights) ? 0 : &c_weights), &c_options); + + /* Convert output */ + PROTECT(r_result=NEW_LIST(3)); + PROTECT(r_names=NEW_CHARACTER(3)); + PROTECT(vector=R_igraph_vector_to_SEXP(&c_vector)); + igraph_vector_destroy(&c_vector); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(value=NEW_NUMERIC(1)); + REAL(value)[0]=c_value; + PROTECT(options=R_igraph_arpack_options_to_SEXP(&c_options)); + SET_VECTOR_ELT(r_result, 0, vector); + SET_VECTOR_ELT(r_result, 1, value); + SET_VECTOR_ELT(r_result, 2, options); + SET_STRING_ELT(r_names, 0, CREATE_STRING_VECTOR("vector")); + SET_STRING_ELT(r_names, 1, CREATE_STRING_VECTOR("value")); + SET_STRING_ELT(r_names, 2, CREATE_STRING_VECTOR("options")); + SET_NAMES(r_result, r_names); + UNPROTECT(4); + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_arpack_unpack_complex / +/-------------------------------------------*/ +SEXP R_igraph_arpack_unpack_complex(SEXP vectors, SEXP values, SEXP nev) { + /* Declarations */ + igraph_matrix_t c_vectors; + igraph_matrix_t c_values; + long int c_nev; + + SEXP r_result, r_names; + /* Convert input */ + if (0 != R_SEXP_to_igraph_matrix_copy(vectors, &c_vectors)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_matrix_destroy, &c_vectors); + if (0 != R_SEXP_to_igraph_matrix_copy(values, &c_values)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_matrix_destroy, &c_values); + c_nev=INTEGER(nev)[0]; + /* Call igraph */ + igraph_arpack_unpack_complex(&c_vectors, &c_values, c_nev); + + /* Convert output */ + PROTECT(r_result=NEW_LIST(2)); + PROTECT(r_names=NEW_CHARACTER(2)); + PROTECT(vectors=R_igraph_matrix_to_SEXP(&c_vectors)); + igraph_matrix_destroy(&c_vectors); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(values=R_igraph_matrix_to_SEXP(&c_values)); + igraph_matrix_destroy(&c_values); + IGRAPH_FINALLY_CLEAN(1); + SET_VECTOR_ELT(r_result, 0, vectors); + SET_VECTOR_ELT(r_result, 1, values); + SET_STRING_ELT(r_names, 0, CREATE_STRING_VECTOR("vectors")); + SET_STRING_ELT(r_names, 1, CREATE_STRING_VECTOR("values")); + SET_NAMES(r_result, r_names); + UNPROTECT(3); + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_unfold_tree / +/-------------------------------------------*/ +SEXP R_igraph_unfold_tree(SEXP graph, SEXP mode, SEXP roots) { + /* Declarations */ + igraph_t c_graph; + igraph_t c_tree; + igraph_neimode_t c_mode; + igraph_vector_t c_roots; + igraph_vector_t c_vertex_index; + SEXP tree; + SEXP vertex_index; + + SEXP r_result, r_names; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + c_mode = (igraph_neimode_t) Rf_asInteger(mode); + R_SEXP_to_vector(roots, &c_roots); + if (0 != igraph_vector_init(&c_vertex_index, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_vertex_index); + vertex_index=R_GlobalEnv; /* hack to have a non-NULL value */ + /* Call igraph */ + igraph_unfold_tree(&c_graph, &c_tree, c_mode, &c_roots, (isNull(vertex_index) ? 0 : &c_vertex_index)); + + /* Convert output */ + PROTECT(r_result=NEW_LIST(2)); + PROTECT(r_names=NEW_CHARACTER(2)); + IGRAPH_FINALLY(igraph_destroy, &c_tree); + PROTECT(tree=R_igraph_to_SEXP(&c_tree)); + igraph_destroy(&c_tree); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(vertex_index=R_igraph_0orvector_to_SEXPp1(&c_vertex_index)); + igraph_vector_destroy(&c_vertex_index); + IGRAPH_FINALLY_CLEAN(1); + SET_VECTOR_ELT(r_result, 0, tree); + SET_VECTOR_ELT(r_result, 1, vertex_index); + SET_STRING_ELT(r_names, 0, CREATE_STRING_VECTOR("tree")); + SET_STRING_ELT(r_names, 1, CREATE_STRING_VECTOR("vertex_index")); + SET_NAMES(r_result, r_names); + UNPROTECT(3); + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_is_mutual / +/-------------------------------------------*/ +SEXP R_igraph_is_mutual(SEXP graph, SEXP es) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_bool_t c_res; + igraph_es_t c_es; + SEXP res; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != igraph_vector_bool_init(&c_res, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_bool_destroy, &c_res); + R_SEXP_to_igraph_es(es, &c_graph, &c_es); + /* Call igraph */ + igraph_is_mutual(&c_graph, &c_res, c_es); + + /* Convert output */ + PROTECT(res=R_igraph_vector_bool_to_SEXP(&c_res)); + igraph_vector_bool_destroy(&c_res); + IGRAPH_FINALLY_CLEAN(1); + igraph_es_destroy(&c_es); + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_maximum_cardinality_search / +/-------------------------------------------*/ +SEXP R_igraph_maximum_cardinality_search(SEXP graph) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_t c_alpha; + igraph_vector_t c_alpham1; + SEXP alpha; + SEXP alpham1; + + SEXP r_result, r_names; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != igraph_vector_init(&c_alpha, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_alpha); + if (0 != igraph_vector_init(&c_alpham1, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_alpham1); + /* Call igraph */ + igraph_maximum_cardinality_search(&c_graph, &c_alpha, &c_alpham1); + + /* Convert output */ + PROTECT(r_result=NEW_LIST(2)); + PROTECT(r_names=NEW_CHARACTER(2)); + PROTECT(alpha=R_igraph_vector_to_SEXPp1(&c_alpha)); + igraph_vector_destroy(&c_alpha); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(alpham1=R_igraph_vector_to_SEXPp1(&c_alpham1)); + igraph_vector_destroy(&c_alpham1); + IGRAPH_FINALLY_CLEAN(1); + SET_VECTOR_ELT(r_result, 0, alpha); + SET_VECTOR_ELT(r_result, 1, alpham1); + SET_STRING_ELT(r_names, 0, CREATE_STRING_VECTOR("alpha")); + SET_STRING_ELT(r_names, 1, CREATE_STRING_VECTOR("alpham1")); + SET_NAMES(r_result, r_names); + UNPROTECT(3); + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_avg_nearest_neighbor_degree / +/-------------------------------------------*/ +SEXP R_igraph_avg_nearest_neighbor_degree(SEXP graph, SEXP vids, SEXP mode, SEXP neighbor_degree_mode, SEXP weights) { + /* Declarations */ + igraph_t c_graph; + igraph_vs_t c_vids; + igraph_neimode_t c_mode; + igraph_neimode_t c_neighbor_degree_mode; + igraph_vector_t c_knn; + igraph_vector_t c_knnk; + igraph_vector_t c_weights; + SEXP knn; + SEXP knnk; + + SEXP r_result, r_names; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + R_SEXP_to_igraph_vs(vids, &c_graph, &c_vids); + c_mode = (igraph_neimode_t) Rf_asInteger(mode); + c_neighbor_degree_mode = (igraph_neimode_t) Rf_asInteger(neighbor_degree_mode); + if (0 != igraph_vector_init(&c_knn, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_knn); + if (0 != igraph_vector_init(&c_knnk, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_knnk); + knnk=R_GlobalEnv; /* hack to have a non-NULL value */ + if (!isNull(weights)) { R_SEXP_to_vector(weights, &c_weights); } + /* Call igraph */ + igraph_avg_nearest_neighbor_degree(&c_graph, c_vids, c_mode, c_neighbor_degree_mode, &c_knn, (isNull(knnk) ? 0 : &c_knnk), (isNull(weights) ? 0 : &c_weights)); + + /* Convert output */ + PROTECT(r_result=NEW_LIST(2)); + PROTECT(r_names=NEW_CHARACTER(2)); + igraph_vs_destroy(&c_vids); + PROTECT(knn=R_igraph_vector_to_SEXP(&c_knn)); + igraph_vector_destroy(&c_knn); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(knnk=R_igraph_0orvector_to_SEXP(&c_knnk)); + igraph_vector_destroy(&c_knnk); + IGRAPH_FINALLY_CLEAN(1); + SET_VECTOR_ELT(r_result, 0, knn); + SET_VECTOR_ELT(r_result, 1, knnk); + SET_STRING_ELT(r_names, 0, CREATE_STRING_VECTOR("knn")); + SET_STRING_ELT(r_names, 1, CREATE_STRING_VECTOR("knnk")); + SET_NAMES(r_result, r_names); + UNPROTECT(3); + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_strength / +/-------------------------------------------*/ +SEXP R_igraph_strength(SEXP graph, SEXP vids, SEXP mode, SEXP loops, SEXP weights) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_t c_res; + igraph_vs_t c_vids; + igraph_neimode_t c_mode; + igraph_bool_t c_loops; + igraph_vector_t c_weights; + SEXP res; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != igraph_vector_init(&c_res, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_res); + R_SEXP_to_igraph_vs(vids, &c_graph, &c_vids); + c_mode = (igraph_neimode_t) Rf_asInteger(mode); + c_loops=LOGICAL(loops)[0]; + if (!isNull(weights)) { R_SEXP_to_vector(weights, &c_weights); } + /* Call igraph */ + igraph_strength(&c_graph, &c_res, c_vids, c_mode, c_loops, (isNull(weights) ? 0 : &c_weights)); + + /* Convert output */ + PROTECT(res=R_igraph_vector_to_SEXP(&c_res)); + igraph_vector_destroy(&c_res); + IGRAPH_FINALLY_CLEAN(1); + igraph_vs_destroy(&c_vids); + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_centralization / +/-------------------------------------------*/ +SEXP R_igraph_centralization(SEXP scores, SEXP theoretical_max, SEXP normalized) { + /* Declarations */ + igraph_vector_t c_scores; + igraph_real_t c_theoretical_max; + igraph_bool_t c_normalized; + igraph_real_t c_result; + SEXP r_result; + /* Convert input */ + R_SEXP_to_vector(scores, &c_scores); + c_theoretical_max=REAL(theoretical_max)[0]; + c_normalized=LOGICAL(normalized)[0]; + /* Call igraph */ + c_result= igraph_centralization(&c_scores, c_theoretical_max, c_normalized); + + /* Convert output */ + + PROTECT(r_result=NEW_NUMERIC(1)); + REAL(r_result)[0]=c_result; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_centralization_degree / +/-------------------------------------------*/ +SEXP R_igraph_centralization_degree(SEXP graph, SEXP mode, SEXP loops, SEXP normalized) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_t c_res; + igraph_neimode_t c_mode; + igraph_bool_t c_loops; + igraph_real_t c_centralization; + igraph_real_t c_theoretical_max; + igraph_bool_t c_normalized; + SEXP res; + SEXP centralization; + SEXP theoretical_max; + + SEXP r_result, r_names; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != igraph_vector_init(&c_res, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_res); + c_mode = (igraph_neimode_t) Rf_asInteger(mode); + c_loops=LOGICAL(loops)[0]; + c_normalized=LOGICAL(normalized)[0]; + /* Call igraph */ + igraph_centralization_degree(&c_graph, &c_res, c_mode, c_loops, &c_centralization, &c_theoretical_max, c_normalized); + + /* Convert output */ + PROTECT(r_result=NEW_LIST(3)); + PROTECT(r_names=NEW_CHARACTER(3)); + PROTECT(res=R_igraph_vector_to_SEXP(&c_res)); + igraph_vector_destroy(&c_res); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(centralization=NEW_NUMERIC(1)); + REAL(centralization)[0]=c_centralization; + PROTECT(theoretical_max=NEW_NUMERIC(1)); + REAL(theoretical_max)[0]=c_theoretical_max; + SET_VECTOR_ELT(r_result, 0, res); + SET_VECTOR_ELT(r_result, 1, centralization); + SET_VECTOR_ELT(r_result, 2, theoretical_max); + SET_STRING_ELT(r_names, 0, CREATE_STRING_VECTOR("res")); + SET_STRING_ELT(r_names, 1, CREATE_STRING_VECTOR("centralization")); + SET_STRING_ELT(r_names, 2, CREATE_STRING_VECTOR("theoretical_max")); + SET_NAMES(r_result, r_names); + UNPROTECT(4); + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_centralization_degree_tmax / +/-------------------------------------------*/ +SEXP R_igraph_centralization_degree_tmax(SEXP graph, SEXP nodes, SEXP mode, SEXP loops) { + /* Declarations */ + igraph_t c_graph; + igraph_integer_t c_nodes; + igraph_neimode_t c_mode; + igraph_bool_t c_loops; + igraph_real_t c_res; + SEXP res; + + SEXP r_result; + /* Convert input */ + if (!isNull(graph)) { R_SEXP_to_igraph(graph, &c_graph); } + c_nodes=INTEGER(nodes)[0]; + c_mode = (igraph_neimode_t) Rf_asInteger(mode); + c_loops=LOGICAL(loops)[0]; + /* Call igraph */ + igraph_centralization_degree_tmax((isNull(graph) ? 0 : &c_graph), c_nodes, c_mode, c_loops, &c_res); + + /* Convert output */ + PROTECT(res=NEW_NUMERIC(1)); + REAL(res)[0]=c_res; + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_centralization_betweenness / +/-------------------------------------------*/ +SEXP R_igraph_centralization_betweenness(SEXP graph, SEXP directed, SEXP normalized) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_t c_res; + igraph_bool_t c_directed; + igraph_real_t c_centralization; + igraph_real_t c_theoretical_max; + igraph_bool_t c_normalized; + SEXP res; + SEXP centralization; + SEXP theoretical_max; + + SEXP r_result, r_names; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != igraph_vector_init(&c_res, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_res); + c_directed=LOGICAL(directed)[0]; + c_normalized=LOGICAL(normalized)[0]; + /* Call igraph */ + igraph_centralization_betweenness(&c_graph, &c_res, c_directed, &c_centralization, &c_theoretical_max, c_normalized); + + /* Convert output */ + PROTECT(r_result=NEW_LIST(3)); + PROTECT(r_names=NEW_CHARACTER(3)); + PROTECT(res=R_igraph_vector_to_SEXP(&c_res)); + igraph_vector_destroy(&c_res); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(centralization=NEW_NUMERIC(1)); + REAL(centralization)[0]=c_centralization; + PROTECT(theoretical_max=NEW_NUMERIC(1)); + REAL(theoretical_max)[0]=c_theoretical_max; + SET_VECTOR_ELT(r_result, 0, res); + SET_VECTOR_ELT(r_result, 1, centralization); + SET_VECTOR_ELT(r_result, 2, theoretical_max); + SET_STRING_ELT(r_names, 0, CREATE_STRING_VECTOR("res")); + SET_STRING_ELT(r_names, 1, CREATE_STRING_VECTOR("centralization")); + SET_STRING_ELT(r_names, 2, CREATE_STRING_VECTOR("theoretical_max")); + SET_NAMES(r_result, r_names); + UNPROTECT(4); + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_centralization_betweenness_tmax / +/-------------------------------------------*/ +SEXP R_igraph_centralization_betweenness_tmax(SEXP graph, SEXP nodes, SEXP directed) { + /* Declarations */ + igraph_t c_graph; + igraph_integer_t c_nodes; + igraph_bool_t c_directed; + igraph_real_t c_res; + SEXP res; + + SEXP r_result; + /* Convert input */ + if (!isNull(graph)) { R_SEXP_to_igraph(graph, &c_graph); } + c_nodes=INTEGER(nodes)[0]; + c_directed=LOGICAL(directed)[0]; + /* Call igraph */ + igraph_centralization_betweenness_tmax((isNull(graph) ? 0 : &c_graph), c_nodes, c_directed, &c_res); + + /* Convert output */ + PROTECT(res=NEW_NUMERIC(1)); + REAL(res)[0]=c_res; + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_centralization_closeness / +/-------------------------------------------*/ +SEXP R_igraph_centralization_closeness(SEXP graph, SEXP mode, SEXP normalized) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_t c_res; + igraph_neimode_t c_mode; + igraph_real_t c_centralization; + igraph_real_t c_theoretical_max; + igraph_bool_t c_normalized; + SEXP res; + SEXP centralization; + SEXP theoretical_max; + + SEXP r_result, r_names; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != igraph_vector_init(&c_res, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_res); + c_mode = (igraph_neimode_t) Rf_asInteger(mode); + c_normalized=LOGICAL(normalized)[0]; + /* Call igraph */ + igraph_centralization_closeness(&c_graph, &c_res, c_mode, &c_centralization, &c_theoretical_max, c_normalized); + + /* Convert output */ + PROTECT(r_result=NEW_LIST(3)); + PROTECT(r_names=NEW_CHARACTER(3)); + PROTECT(res=R_igraph_vector_to_SEXP(&c_res)); + igraph_vector_destroy(&c_res); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(centralization=NEW_NUMERIC(1)); + REAL(centralization)[0]=c_centralization; + PROTECT(theoretical_max=NEW_NUMERIC(1)); + REAL(theoretical_max)[0]=c_theoretical_max; + SET_VECTOR_ELT(r_result, 0, res); + SET_VECTOR_ELT(r_result, 1, centralization); + SET_VECTOR_ELT(r_result, 2, theoretical_max); + SET_STRING_ELT(r_names, 0, CREATE_STRING_VECTOR("res")); + SET_STRING_ELT(r_names, 1, CREATE_STRING_VECTOR("centralization")); + SET_STRING_ELT(r_names, 2, CREATE_STRING_VECTOR("theoretical_max")); + SET_NAMES(r_result, r_names); + UNPROTECT(4); + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_centralization_closeness_tmax / +/-------------------------------------------*/ +SEXP R_igraph_centralization_closeness_tmax(SEXP graph, SEXP nodes, SEXP mode) { + /* Declarations */ + igraph_t c_graph; + igraph_integer_t c_nodes; + igraph_neimode_t c_mode; + igraph_real_t c_res; + SEXP res; + + SEXP r_result; + /* Convert input */ + if (!isNull(graph)) { R_SEXP_to_igraph(graph, &c_graph); } + c_nodes=INTEGER(nodes)[0]; + c_mode = (igraph_neimode_t) Rf_asInteger(mode); + /* Call igraph */ + igraph_centralization_closeness_tmax((isNull(graph) ? 0 : &c_graph), c_nodes, c_mode, &c_res); + + /* Convert output */ + PROTECT(res=NEW_NUMERIC(1)); + REAL(res)[0]=c_res; + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_centralization_eigenvector_centrality / +/-------------------------------------------*/ +SEXP R_igraph_centralization_eigenvector_centrality(SEXP graph, SEXP directed, SEXP scale, SEXP options, SEXP normalized) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_t c_vector; + igraph_real_t c_value; + igraph_bool_t c_directed; + igraph_bool_t c_scale; + igraph_arpack_options_t c_options; + igraph_real_t c_centralization; + igraph_real_t c_theoretical_max; + igraph_bool_t c_normalized; + SEXP vector; + SEXP value; + SEXP centralization; + SEXP theoretical_max; + + SEXP r_result, r_names; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != igraph_vector_init(&c_vector, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_vector); + c_directed=LOGICAL(directed)[0]; + c_scale=LOGICAL(scale)[0]; + R_SEXP_to_igraph_arpack_options(options, &c_options); + c_normalized=LOGICAL(normalized)[0]; + /* Call igraph */ + igraph_centralization_eigenvector_centrality(&c_graph, &c_vector, &c_value, c_directed, c_scale, &c_options, &c_centralization, &c_theoretical_max, c_normalized); + + /* Convert output */ + PROTECT(r_result=NEW_LIST(5)); + PROTECT(r_names=NEW_CHARACTER(5)); + PROTECT(vector=R_igraph_vector_to_SEXP(&c_vector)); + igraph_vector_destroy(&c_vector); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(value=NEW_NUMERIC(1)); + REAL(value)[0]=c_value; + PROTECT(options=R_igraph_arpack_options_to_SEXP(&c_options)); + PROTECT(centralization=NEW_NUMERIC(1)); + REAL(centralization)[0]=c_centralization; + PROTECT(theoretical_max=NEW_NUMERIC(1)); + REAL(theoretical_max)[0]=c_theoretical_max; + SET_VECTOR_ELT(r_result, 0, vector); + SET_VECTOR_ELT(r_result, 1, value); + SET_VECTOR_ELT(r_result, 2, options); + SET_VECTOR_ELT(r_result, 3, centralization); + SET_VECTOR_ELT(r_result, 4, theoretical_max); + SET_STRING_ELT(r_names, 0, CREATE_STRING_VECTOR("vector")); + SET_STRING_ELT(r_names, 1, CREATE_STRING_VECTOR("value")); + SET_STRING_ELT(r_names, 2, CREATE_STRING_VECTOR("options")); + SET_STRING_ELT(r_names, 3, CREATE_STRING_VECTOR("centralization")); + SET_STRING_ELT(r_names, 4, CREATE_STRING_VECTOR("theoretical_max")); + SET_NAMES(r_result, r_names); + UNPROTECT(6); + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_centralization_eigenvector_centrality_tmax / +/-------------------------------------------*/ +SEXP R_igraph_centralization_eigenvector_centrality_tmax(SEXP graph, SEXP nodes, SEXP directed, SEXP scale) { + /* Declarations */ + igraph_t c_graph; + igraph_integer_t c_nodes; + igraph_bool_t c_directed; + igraph_bool_t c_scale; + igraph_real_t c_res; + SEXP res; + + SEXP r_result; + /* Convert input */ + if (!isNull(graph)) { R_SEXP_to_igraph(graph, &c_graph); } + c_nodes=INTEGER(nodes)[0]; + c_directed=LOGICAL(directed)[0]; + c_scale=LOGICAL(scale)[0]; + /* Call igraph */ + igraph_centralization_eigenvector_centrality_tmax((isNull(graph) ? 0 : &c_graph), c_nodes, c_directed, c_scale, &c_res); + + /* Convert output */ + PROTECT(res=NEW_NUMERIC(1)); + REAL(res)[0]=c_res; + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_assortativity_nominal / +/-------------------------------------------*/ +SEXP R_igraph_assortativity_nominal(SEXP graph, SEXP types, SEXP directed) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_t c_types; + igraph_real_t c_res; + igraph_bool_t c_directed; + SEXP res; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + R_SEXP_to_vector(types, &c_types); + c_directed=LOGICAL(directed)[0]; + /* Call igraph */ + igraph_assortativity_nominal(&c_graph, &c_types, &c_res, c_directed); + + /* Convert output */ + PROTECT(res=NEW_NUMERIC(1)); + REAL(res)[0]=c_res; + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_assortativity / +/-------------------------------------------*/ +SEXP R_igraph_assortativity(SEXP graph, SEXP types1, SEXP types2, SEXP directed) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_t c_types1; + igraph_vector_t c_types2; + igraph_real_t c_res; + igraph_bool_t c_directed; + SEXP res; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + R_SEXP_to_vector(types1, &c_types1); + if (!isNull(types2)) { R_SEXP_to_vector(types2, &c_types2); } + c_directed=LOGICAL(directed)[0]; + /* Call igraph */ + igraph_assortativity(&c_graph, &c_types1, (isNull(types2) ? 0 : &c_types2), &c_res, c_directed); + + /* Convert output */ + PROTECT(res=NEW_NUMERIC(1)); + REAL(res)[0]=c_res; + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_assortativity_degree / +/-------------------------------------------*/ +SEXP R_igraph_assortativity_degree(SEXP graph, SEXP directed) { + /* Declarations */ + igraph_t c_graph; + igraph_real_t c_res; + igraph_bool_t c_directed; + SEXP res; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + c_directed=LOGICAL(directed)[0]; + /* Call igraph */ + igraph_assortativity_degree(&c_graph, &c_res, c_directed); + + /* Convert output */ + PROTECT(res=NEW_NUMERIC(1)); + REAL(res)[0]=c_res; + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_contract_vertices / +/-------------------------------------------*/ +SEXP R_igraph_contract_vertices(SEXP graph, SEXP mapping, SEXP vertex_attr_comb) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_t c_mapping; + igraph_attribute_combination_t c_vertex_attr_comb; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph_copy(graph, &c_graph); + IGRAPH_FINALLY(igraph_destroy, &c_graph); + R_SEXP_to_vector(mapping, &c_mapping); + R_SEXP_to_attr_comb(vertex_attr_comb, &c_vertex_attr_comb); + /* Call igraph */ + igraph_contract_vertices(&c_graph, &c_mapping, &c_vertex_attr_comb); + + /* Convert output */ + PROTECT(graph=R_igraph_to_SEXP(&c_graph)); + igraph_destroy(&c_graph); + IGRAPH_FINALLY_CLEAN(1); + igraph_attribute_combination_destroy(&c_vertex_attr_comb); + r_result = graph; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_eccentricity / +/-------------------------------------------*/ +SEXP R_igraph_eccentricity(SEXP graph, SEXP vids, SEXP mode) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_t c_res; + igraph_vs_t c_vids; + igraph_neimode_t c_mode; + SEXP res; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != igraph_vector_init(&c_res, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_res); + R_SEXP_to_igraph_vs(vids, &c_graph, &c_vids); + c_mode = (igraph_neimode_t) Rf_asInteger(mode); + /* Call igraph */ + igraph_eccentricity(&c_graph, &c_res, c_vids, c_mode); + + /* Convert output */ + PROTECT(res=R_igraph_vector_to_SEXP(&c_res)); + igraph_vector_destroy(&c_res); + IGRAPH_FINALLY_CLEAN(1); + igraph_vs_destroy(&c_vids); + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_radius / +/-------------------------------------------*/ +SEXP R_igraph_radius(SEXP graph, SEXP mode) { + /* Declarations */ + igraph_t c_graph; + igraph_real_t c_radius; + igraph_neimode_t c_mode; + SEXP radius; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + c_mode = (igraph_neimode_t) Rf_asInteger(mode); + /* Call igraph */ + igraph_radius(&c_graph, &c_radius, c_mode); + + /* Convert output */ + PROTECT(radius=NEW_NUMERIC(1)); + REAL(radius)[0]=c_radius; + r_result = radius; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_diversity / +/-------------------------------------------*/ +SEXP R_igraph_diversity(SEXP graph, SEXP weights, SEXP vids) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_t c_weights; + igraph_vector_t c_res; + igraph_vs_t c_vids; + SEXP res; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (!isNull(weights)) { R_SEXP_to_vector(weights, &c_weights); } + if (0 != igraph_vector_init(&c_res, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_res); + R_SEXP_to_igraph_vs(vids, &c_graph, &c_vids); + /* Call igraph */ + igraph_diversity(&c_graph, (isNull(weights) ? 0 : &c_weights), &c_res, c_vids); + + /* Convert output */ + PROTECT(res=R_igraph_vector_to_SEXP(&c_res)); + igraph_vector_destroy(&c_res); + IGRAPH_FINALLY_CLEAN(1); + igraph_vs_destroy(&c_vids); + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_random_walk / +/-------------------------------------------*/ +SEXP R_igraph_random_walk(SEXP graph, SEXP start, SEXP mode, SEXP steps, SEXP stuck) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_t c_walk; + igraph_integer_t c_start; + igraph_neimode_t c_mode; + igraph_integer_t c_steps; + igraph_random_walk_stuck_t c_stuck; + SEXP walk; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != igraph_vector_init(&c_walk, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_walk); + c_start = (igraph_integer_t) REAL(start)[0]; + c_mode = (igraph_neimode_t) Rf_asInteger(mode); + c_steps=INTEGER(steps)[0]; + c_stuck = (igraph_random_walk_stuck_t) Rf_asInteger(stuck); + /* Call igraph */ + igraph_random_walk(&c_graph, &c_walk, c_start, c_mode, c_steps, c_stuck); + + /* Convert output */ + PROTECT(walk=R_igraph_vector_to_SEXPp1(&c_walk)); + igraph_vector_destroy(&c_walk); + IGRAPH_FINALLY_CLEAN(1); + r_result = walk; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_random_edge_walk / +/-------------------------------------------*/ +SEXP R_igraph_random_edge_walk(SEXP graph, SEXP weights, SEXP start, SEXP mode, SEXP steps, SEXP stuck) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_t c_weights; + igraph_vector_t c_edgewalk; + igraph_integer_t c_start; + igraph_neimode_t c_mode; + igraph_integer_t c_steps; + igraph_random_walk_stuck_t c_stuck; + SEXP edgewalk; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (!isNull(weights)) { R_SEXP_to_vector(weights, &c_weights); } + if (0 != igraph_vector_init(&c_edgewalk, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_edgewalk); + c_start = (igraph_integer_t) REAL(start)[0]; + c_mode = (igraph_neimode_t) Rf_asInteger(mode); + c_steps=INTEGER(steps)[0]; + c_stuck = (igraph_random_walk_stuck_t) Rf_asInteger(stuck); + /* Call igraph */ + igraph_random_edge_walk(&c_graph, (isNull(weights) ? 0 : &c_weights), &c_edgewalk, c_start, c_mode, c_steps, c_stuck); + + /* Convert output */ + PROTECT(edgewalk=R_igraph_vector_to_SEXPp1(&c_edgewalk)); + igraph_vector_destroy(&c_edgewalk); + IGRAPH_FINALLY_CLEAN(1); + r_result = edgewalk; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_global_efficiency / +/-------------------------------------------*/ +SEXP R_igraph_global_efficiency(SEXP graph, SEXP weights, SEXP directed) { + /* Declarations */ + igraph_t c_graph; + igraph_real_t c_res; + igraph_vector_t c_weights; + igraph_bool_t c_directed; + SEXP res; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (!isNull(weights)) { R_SEXP_to_vector(weights, &c_weights); } + c_directed=LOGICAL(directed)[0]; + /* Call igraph */ + igraph_global_efficiency(&c_graph, &c_res, (isNull(weights) ? 0 : &c_weights), c_directed); + + /* Convert output */ + PROTECT(res=NEW_NUMERIC(1)); + REAL(res)[0]=c_res; + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_local_efficiency / +/-------------------------------------------*/ +SEXP R_igraph_local_efficiency(SEXP graph, SEXP vids, SEXP weights, SEXP directed, SEXP mode) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_t c_res; + igraph_vs_t c_vids; + igraph_vector_t c_weights; + igraph_bool_t c_directed; + igraph_neimode_t c_mode; + SEXP res; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != igraph_vector_init(&c_res, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_res); + R_SEXP_to_igraph_vs(vids, &c_graph, &c_vids); + if (!isNull(weights)) { R_SEXP_to_vector(weights, &c_weights); } + c_directed=LOGICAL(directed)[0]; + c_mode = (igraph_neimode_t) Rf_asInteger(mode); + /* Call igraph */ + igraph_local_efficiency(&c_graph, &c_res, c_vids, (isNull(weights) ? 0 : &c_weights), c_directed, c_mode); + + /* Convert output */ + PROTECT(res=R_igraph_vector_to_SEXP(&c_res)); + igraph_vector_destroy(&c_res); + IGRAPH_FINALLY_CLEAN(1); + igraph_vs_destroy(&c_vids); + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_average_local_efficiency / +/-------------------------------------------*/ +SEXP R_igraph_average_local_efficiency(SEXP graph, SEXP weights, SEXP directed, SEXP mode) { + /* Declarations */ + igraph_t c_graph; + igraph_real_t c_res; + igraph_vector_t c_weights; + igraph_bool_t c_directed; + igraph_neimode_t c_mode; + SEXP res; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (!isNull(weights)) { R_SEXP_to_vector(weights, &c_weights); } + c_directed=LOGICAL(directed)[0]; + c_mode = (igraph_neimode_t) Rf_asInteger(mode); + /* Call igraph */ + igraph_average_local_efficiency(&c_graph, &c_res, (isNull(weights) ? 0 : &c_weights), c_directed, c_mode); + + /* Convert output */ + PROTECT(res=NEW_NUMERIC(1)); + REAL(res)[0]=c_res; + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_is_graphical / +/-------------------------------------------*/ +SEXP R_igraph_is_graphical(SEXP out_deg, SEXP in_deg, SEXP allowed_edge_types) { + /* Declarations */ + igraph_vector_t c_out_deg; + igraph_vector_t c_in_deg; + igraph_edge_type_sw_t c_allowed_edge_types; + igraph_bool_t c_res; + SEXP res; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_vector(out_deg, &c_out_deg); + if (!isNull(in_deg)) { R_SEXP_to_vector(in_deg, &c_in_deg); } + c_allowed_edge_types = (igraph_edge_type_sw_t) Rf_asInteger(allowed_edge_types); + /* Call igraph */ + igraph_is_graphical(&c_out_deg, (isNull(in_deg) ? 0 : &c_in_deg), c_allowed_edge_types, &c_res); + + /* Convert output */ + PROTECT(res=NEW_LOGICAL(1)); + LOGICAL(res)[0]=c_res; + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_bipartite_projection_size / +/-------------------------------------------*/ +SEXP R_igraph_bipartite_projection_size(SEXP graph, SEXP types) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_bool_t c_types; + igraph_integer_t c_vcount1; + igraph_integer_t c_ecount1; + igraph_integer_t c_vcount2; + igraph_integer_t c_ecount2; + SEXP vcount1; + SEXP ecount1; + SEXP vcount2; + SEXP ecount2; + + SEXP r_result, r_names; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (!isNull(types)) { R_SEXP_to_vector_bool(types, &c_types); } + c_vcount1=0; + c_ecount1=0; + c_vcount2=0; + c_ecount2=0; + /* Call igraph */ + igraph_bipartite_projection_size(&c_graph, (isNull(types) ? 0 : &c_types), &c_vcount1, &c_ecount1, &c_vcount2, &c_ecount2); + + /* Convert output */ + PROTECT(r_result=NEW_LIST(4)); + PROTECT(r_names=NEW_CHARACTER(4)); + PROTECT(vcount1=NEW_INTEGER(1)); + INTEGER(vcount1)[0]=c_vcount1; + PROTECT(ecount1=NEW_INTEGER(1)); + INTEGER(ecount1)[0]=c_ecount1; + PROTECT(vcount2=NEW_INTEGER(1)); + INTEGER(vcount2)[0]=c_vcount2; + PROTECT(ecount2=NEW_INTEGER(1)); + INTEGER(ecount2)[0]=c_ecount2; + SET_VECTOR_ELT(r_result, 0, vcount1); + SET_VECTOR_ELT(r_result, 1, ecount1); + SET_VECTOR_ELT(r_result, 2, vcount2); + SET_VECTOR_ELT(r_result, 3, ecount2); + SET_STRING_ELT(r_names, 0, CREATE_STRING_VECTOR("vcount1")); + SET_STRING_ELT(r_names, 1, CREATE_STRING_VECTOR("ecount1")); + SET_STRING_ELT(r_names, 2, CREATE_STRING_VECTOR("vcount2")); + SET_STRING_ELT(r_names, 3, CREATE_STRING_VECTOR("ecount2")); + SET_NAMES(r_result, r_names); + UNPROTECT(5); + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_create_bipartite / +/-------------------------------------------*/ +SEXP R_igraph_create_bipartite(SEXP types, SEXP edges, SEXP directed) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_bool_t c_types; + igraph_vector_t c_edges; + igraph_bool_t c_directed; + SEXP graph; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_vector_bool(types, &c_types); + R_SEXP_to_vector(edges, &c_edges); + c_directed=LOGICAL(directed)[0]; + /* Call igraph */ + igraph_create_bipartite(&c_graph, &c_types, &c_edges, c_directed); + + /* Convert output */ + IGRAPH_FINALLY(igraph_destroy, &c_graph); + PROTECT(graph=R_igraph_to_SEXP(&c_graph)); + igraph_destroy(&c_graph); + IGRAPH_FINALLY_CLEAN(1); + r_result = graph; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_incidence / +/-------------------------------------------*/ +SEXP R_igraph_incidence(SEXP incidence, SEXP directed, SEXP mode, SEXP multiple) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_bool_t c_types; + igraph_matrix_t c_incidence; + igraph_bool_t c_directed; + igraph_neimode_t c_mode; + igraph_bool_t c_multiple; + SEXP graph; + SEXP types; + + SEXP r_result, r_names; + /* Convert input */ + if (0 != igraph_vector_bool_init(&c_types, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_bool_destroy, &c_types); + R_SEXP_to_matrix(incidence, &c_incidence); + c_directed=LOGICAL(directed)[0]; + c_mode = (igraph_neimode_t) Rf_asInteger(mode); + c_multiple=LOGICAL(multiple)[0]; + /* Call igraph */ + igraph_incidence(&c_graph, &c_types, &c_incidence, c_directed, c_mode, c_multiple); + + /* Convert output */ + PROTECT(r_result=NEW_LIST(2)); + PROTECT(r_names=NEW_CHARACTER(2)); + IGRAPH_FINALLY(igraph_destroy, &c_graph); + PROTECT(graph=R_igraph_to_SEXP(&c_graph)); + igraph_destroy(&c_graph); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(types=R_igraph_vector_bool_to_SEXP(&c_types)); + igraph_vector_bool_destroy(&c_types); + IGRAPH_FINALLY_CLEAN(1); + SET_VECTOR_ELT(r_result, 0, graph); + SET_VECTOR_ELT(r_result, 1, types); + SET_STRING_ELT(r_names, 0, CREATE_STRING_VECTOR("graph")); + SET_STRING_ELT(r_names, 1, CREATE_STRING_VECTOR("types")); + SET_NAMES(r_result, r_names); + UNPROTECT(3); + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_get_incidence / +/-------------------------------------------*/ +SEXP R_igraph_get_incidence(SEXP graph, SEXP types) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_bool_t c_types; + igraph_matrix_t c_res; + igraph_vector_t c_row_ids; + igraph_vector_t c_col_ids; + SEXP res; + SEXP row_ids; + SEXP col_ids; + + SEXP r_result, r_names; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (!isNull(types)) { R_SEXP_to_vector_bool(types, &c_types); } + if (0 != igraph_matrix_init(&c_res, 0, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_matrix_destroy, &c_res); + if (0 != igraph_vector_init(&c_row_ids, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_row_ids); + row_ids=R_GlobalEnv; /* hack to have a non-NULL value */ + if (0 != igraph_vector_init(&c_col_ids, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_col_ids); + col_ids=R_GlobalEnv; /* hack to have a non-NULL value */ + /* Call igraph */ + igraph_get_incidence(&c_graph, (isNull(types) ? 0 : &c_types), &c_res, (isNull(row_ids) ? 0 : &c_row_ids), (isNull(col_ids) ? 0 : &c_col_ids)); + + /* Convert output */ + PROTECT(r_result=NEW_LIST(3)); + PROTECT(r_names=NEW_CHARACTER(3)); + PROTECT(res=R_igraph_matrix_to_SEXP(&c_res)); + igraph_matrix_destroy(&c_res); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(row_ids=R_igraph_0orvector_to_SEXP(&c_row_ids)); + igraph_vector_destroy(&c_row_ids); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(col_ids=R_igraph_0orvector_to_SEXP(&c_col_ids)); + igraph_vector_destroy(&c_col_ids); + IGRAPH_FINALLY_CLEAN(1); + SET_VECTOR_ELT(r_result, 0, res); + SET_VECTOR_ELT(r_result, 1, row_ids); + SET_VECTOR_ELT(r_result, 2, col_ids); + SET_STRING_ELT(r_names, 0, CREATE_STRING_VECTOR("res")); + SET_STRING_ELT(r_names, 1, CREATE_STRING_VECTOR("row_ids")); + SET_STRING_ELT(r_names, 2, CREATE_STRING_VECTOR("col_ids")); + SET_NAMES(r_result, r_names); + UNPROTECT(4); + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_is_bipartite / +/-------------------------------------------*/ +SEXP R_igraph_is_bipartite(SEXP graph) { + /* Declarations */ + igraph_t c_graph; + igraph_bool_t c_res; + igraph_vector_bool_t c_type; + SEXP res; + SEXP type; + + SEXP r_result, r_names; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != igraph_vector_bool_init(&c_type, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_bool_destroy, &c_type); + type=R_GlobalEnv; /* hack to have a non-NULL value */ + /* Call igraph */ + igraph_is_bipartite(&c_graph, &c_res, (isNull(type) ? 0 : &c_type)); + + /* Convert output */ + PROTECT(r_result=NEW_LIST(2)); + PROTECT(r_names=NEW_CHARACTER(2)); + PROTECT(res=NEW_LOGICAL(1)); + LOGICAL(res)[0]=c_res; + PROTECT(type=R_igraph_0orvector_bool_to_SEXP(&c_type)); + igraph_vector_bool_destroy(&c_type); + IGRAPH_FINALLY_CLEAN(1); + SET_VECTOR_ELT(r_result, 0, res); + SET_VECTOR_ELT(r_result, 1, type); + SET_STRING_ELT(r_names, 0, CREATE_STRING_VECTOR("res")); + SET_STRING_ELT(r_names, 1, CREATE_STRING_VECTOR("type")); + SET_NAMES(r_result, r_names); + UNPROTECT(3); + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_bipartite_game_gnp / +/-------------------------------------------*/ +SEXP R_igraph_bipartite_game_gnp(SEXP n1, SEXP n2, SEXP p, SEXP directed, SEXP mode) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_bool_t c_types; + igraph_integer_t c_n1; + igraph_integer_t c_n2; + igraph_real_t c_p; + igraph_bool_t c_directed; + igraph_neimode_t c_mode; + SEXP graph; + SEXP types; + + SEXP r_result, r_names; + /* Convert input */ + if (0 != igraph_vector_bool_init(&c_types, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_bool_destroy, &c_types); + types=R_GlobalEnv; /* hack to have a non-NULL value */ + c_n1=INTEGER(n1)[0]; + c_n2=INTEGER(n2)[0]; + c_p=REAL(p)[0]; + c_directed=LOGICAL(directed)[0]; + c_mode = (igraph_neimode_t) Rf_asInteger(mode); + /* Call igraph */ + igraph_bipartite_game_gnp(&c_graph, (isNull(types) ? 0 : &c_types), c_n1, c_n2, c_p, c_directed, c_mode); + + /* Convert output */ + PROTECT(r_result=NEW_LIST(2)); + PROTECT(r_names=NEW_CHARACTER(2)); + IGRAPH_FINALLY(igraph_destroy, &c_graph); + PROTECT(graph=R_igraph_to_SEXP(&c_graph)); + igraph_destroy(&c_graph); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(types=R_igraph_0orvector_bool_to_SEXP(&c_types)); + igraph_vector_bool_destroy(&c_types); + IGRAPH_FINALLY_CLEAN(1); + SET_VECTOR_ELT(r_result, 0, graph); + SET_VECTOR_ELT(r_result, 1, types); + SET_STRING_ELT(r_names, 0, CREATE_STRING_VECTOR("graph")); + SET_STRING_ELT(r_names, 1, CREATE_STRING_VECTOR("types")); + SET_NAMES(r_result, r_names); + UNPROTECT(3); + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_bipartite_game_gnm / +/-------------------------------------------*/ +SEXP R_igraph_bipartite_game_gnm(SEXP n1, SEXP n2, SEXP m, SEXP directed, SEXP mode) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_bool_t c_types; + igraph_integer_t c_n1; + igraph_integer_t c_n2; + igraph_integer_t c_m; + igraph_bool_t c_directed; + igraph_neimode_t c_mode; + SEXP graph; + SEXP types; + + SEXP r_result, r_names; + /* Convert input */ + if (0 != igraph_vector_bool_init(&c_types, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_bool_destroy, &c_types); + types=R_GlobalEnv; /* hack to have a non-NULL value */ + c_n1=INTEGER(n1)[0]; + c_n2=INTEGER(n2)[0]; + c_m=INTEGER(m)[0]; + c_directed=LOGICAL(directed)[0]; + c_mode = (igraph_neimode_t) Rf_asInteger(mode); + /* Call igraph */ + igraph_bipartite_game_gnm(&c_graph, (isNull(types) ? 0 : &c_types), c_n1, c_n2, c_m, c_directed, c_mode); + + /* Convert output */ + PROTECT(r_result=NEW_LIST(2)); + PROTECT(r_names=NEW_CHARACTER(2)); + IGRAPH_FINALLY(igraph_destroy, &c_graph); + PROTECT(graph=R_igraph_to_SEXP(&c_graph)); + igraph_destroy(&c_graph); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(types=R_igraph_0orvector_bool_to_SEXP(&c_types)); + igraph_vector_bool_destroy(&c_types); + IGRAPH_FINALLY_CLEAN(1); + SET_VECTOR_ELT(r_result, 0, graph); + SET_VECTOR_ELT(r_result, 1, types); + SET_STRING_ELT(r_names, 0, CREATE_STRING_VECTOR("graph")); + SET_STRING_ELT(r_names, 1, CREATE_STRING_VECTOR("types")); + SET_NAMES(r_result, r_names); + UNPROTECT(3); + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_clusters / +/-------------------------------------------*/ +SEXP R_igraph_clusters(SEXP graph, SEXP mode) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_t c_membership; + igraph_vector_t c_csize; + igraph_integer_t c_no; + igraph_connectedness_t c_mode; + SEXP membership; + SEXP csize; + SEXP no; + + SEXP r_result, r_names; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != igraph_vector_init(&c_membership, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_membership); + if (0 != igraph_vector_init(&c_csize, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_csize); + c_no=0; + c_mode=REAL(mode)[0]; + /* Call igraph */ + igraph_clusters(&c_graph, &c_membership, &c_csize, &c_no, c_mode); + + /* Convert output */ + PROTECT(r_result=NEW_LIST(3)); + PROTECT(r_names=NEW_CHARACTER(3)); + PROTECT(membership=R_igraph_vector_to_SEXP(&c_membership)); + igraph_vector_destroy(&c_membership); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(csize=R_igraph_vector_to_SEXP(&c_csize)); + igraph_vector_destroy(&c_csize); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(no=NEW_INTEGER(1)); + INTEGER(no)[0]=c_no; + SET_VECTOR_ELT(r_result, 0, membership); + SET_VECTOR_ELT(r_result, 1, csize); + SET_VECTOR_ELT(r_result, 2, no); + SET_STRING_ELT(r_names, 0, CREATE_STRING_VECTOR("membership")); + SET_STRING_ELT(r_names, 1, CREATE_STRING_VECTOR("csize")); + SET_STRING_ELT(r_names, 2, CREATE_STRING_VECTOR("no")); + SET_NAMES(r_result, r_names); + UNPROTECT(4); + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_is_connected / +/-------------------------------------------*/ +SEXP R_igraph_is_connected(SEXP graph, SEXP mode) { + /* Declarations */ + igraph_t c_graph; + igraph_bool_t c_res; + igraph_connectedness_t c_mode; + SEXP res; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + c_mode=REAL(mode)[0]; + /* Call igraph */ + igraph_is_connected(&c_graph, &c_res, c_mode); + + /* Convert output */ + PROTECT(res=NEW_LOGICAL(1)); + LOGICAL(res)[0]=c_res; + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_articulation_points / +/-------------------------------------------*/ +SEXP R_igraph_articulation_points(SEXP graph) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_t c_res; + SEXP res; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != igraph_vector_init(&c_res, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_res); + /* Call igraph */ + igraph_articulation_points(&c_graph, &c_res); + + /* Convert output */ + PROTECT(res=R_igraph_vector_to_SEXPp1(&c_res)); + igraph_vector_destroy(&c_res); + IGRAPH_FINALLY_CLEAN(1); + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_biconnected_components / +/-------------------------------------------*/ +SEXP R_igraph_biconnected_components(SEXP graph) { + /* Declarations */ + igraph_t c_graph; + igraph_integer_t c_no; + igraph_vector_ptr_t c_tree_edges; + igraph_vector_ptr_t c_component_edges; + igraph_vector_ptr_t c_components; + igraph_vector_t c_articulation_points; + SEXP no; + SEXP tree_edges; + SEXP component_edges; + SEXP components; + SEXP articulation_points; + + SEXP r_result, r_names; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + c_no=0; + if (0 != igraph_vector_ptr_init(&c_tree_edges, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(R_igraph_vectorlist_destroy, &c_tree_edges); + if (0 != igraph_vector_ptr_init(&c_component_edges, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(R_igraph_vectorlist_destroy, &c_component_edges); + if (0 != igraph_vector_ptr_init(&c_components, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(R_igraph_vectorlist_destroy, &c_components); + if (0 != igraph_vector_init(&c_articulation_points, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_articulation_points); + /* Call igraph */ + igraph_biconnected_components(&c_graph, &c_no, &c_tree_edges, &c_component_edges, &c_components, &c_articulation_points); + + /* Convert output */ + PROTECT(r_result=NEW_LIST(5)); + PROTECT(r_names=NEW_CHARACTER(5)); + PROTECT(no=NEW_INTEGER(1)); + INTEGER(no)[0]=c_no; + PROTECT(tree_edges=R_igraph_vectorlist_to_SEXP_p1(&c_tree_edges)); + R_igraph_vectorlist_destroy(&c_tree_edges); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(component_edges=R_igraph_vectorlist_to_SEXP_p1(&c_component_edges)); + R_igraph_vectorlist_destroy(&c_component_edges); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(components=R_igraph_vectorlist_to_SEXP_p1(&c_components)); + R_igraph_vectorlist_destroy(&c_components); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(articulation_points=R_igraph_vector_to_SEXPp1(&c_articulation_points)); + igraph_vector_destroy(&c_articulation_points); + IGRAPH_FINALLY_CLEAN(1); + SET_VECTOR_ELT(r_result, 0, no); + SET_VECTOR_ELT(r_result, 1, tree_edges); + SET_VECTOR_ELT(r_result, 2, component_edges); + SET_VECTOR_ELT(r_result, 3, components); + SET_VECTOR_ELT(r_result, 4, articulation_points); + SET_STRING_ELT(r_names, 0, CREATE_STRING_VECTOR("no")); + SET_STRING_ELT(r_names, 1, CREATE_STRING_VECTOR("tree_edges")); + SET_STRING_ELT(r_names, 2, CREATE_STRING_VECTOR("component_edges")); + SET_STRING_ELT(r_names, 3, CREATE_STRING_VECTOR("components")); + SET_STRING_ELT(r_names, 4, CREATE_STRING_VECTOR("articulation_points")); + SET_NAMES(r_result, r_names); + UNPROTECT(6); + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_bridges / +/-------------------------------------------*/ +SEXP R_igraph_bridges(SEXP graph) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_t c_res; + SEXP res; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != igraph_vector_init(&c_res, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_res); + /* Call igraph */ + igraph_bridges(&c_graph, &c_res); + + /* Convert output */ + PROTECT(res=R_igraph_vector_to_SEXPp1(&c_res)); + igraph_vector_destroy(&c_res); + IGRAPH_FINALLY_CLEAN(1); + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_cliques / +/-------------------------------------------*/ +SEXP R_igraph_cliques(SEXP graph, SEXP min_size, SEXP max_size) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_ptr_t c_res; + igraph_integer_t c_min_size; + igraph_integer_t c_max_size; + SEXP res; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != igraph_vector_ptr_init(&c_res, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(R_igraph_vectorlist_destroy, &c_res); + c_min_size=INTEGER(min_size)[0]; + c_max_size=INTEGER(max_size)[0]; + /* Call igraph */ + igraph_cliques(&c_graph, &c_res, c_min_size, c_max_size); + + /* Convert output */ + PROTECT(res=R_igraph_vectorlist_to_SEXP_p1(&c_res)); + R_igraph_vectorlist_destroy(&c_res); + IGRAPH_FINALLY_CLEAN(1); + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_clique_size_hist / +/-------------------------------------------*/ +SEXP R_igraph_clique_size_hist(SEXP graph, SEXP min_size, SEXP max_size) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_t c_hist; + igraph_integer_t c_min_size; + igraph_integer_t c_max_size; + SEXP hist; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != igraph_vector_init(&c_hist, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_hist); + c_min_size=INTEGER(min_size)[0]; + c_max_size=INTEGER(max_size)[0]; + /* Call igraph */ + igraph_clique_size_hist(&c_graph, &c_hist, c_min_size, c_max_size); + + /* Convert output */ + PROTECT(hist=R_igraph_vector_to_SEXP(&c_hist)); + igraph_vector_destroy(&c_hist); + IGRAPH_FINALLY_CLEAN(1); + r_result = hist; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_largest_cliques / +/-------------------------------------------*/ +SEXP R_igraph_largest_cliques(SEXP graph) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_ptr_t c_res; + SEXP res; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != igraph_vector_ptr_init(&c_res, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(R_igraph_vectorlist_destroy, &c_res); + /* Call igraph */ + igraph_largest_cliques(&c_graph, &c_res); + + /* Convert output */ + PROTECT(res=R_igraph_vectorlist_to_SEXP_p1(&c_res)); + R_igraph_vectorlist_destroy(&c_res); + IGRAPH_FINALLY_CLEAN(1); + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_maximal_cliques_hist / +/-------------------------------------------*/ +SEXP R_igraph_maximal_cliques_hist(SEXP graph, SEXP min_size, SEXP max_size) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_t c_hist; + igraph_integer_t c_min_size; + igraph_integer_t c_max_size; + SEXP hist; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != igraph_vector_init(&c_hist, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_hist); + c_min_size=INTEGER(min_size)[0]; + c_max_size=INTEGER(max_size)[0]; + /* Call igraph */ + igraph_maximal_cliques_hist(&c_graph, &c_hist, c_min_size, c_max_size); + + /* Convert output */ + PROTECT(hist=R_igraph_vector_to_SEXP(&c_hist)); + igraph_vector_destroy(&c_hist); + IGRAPH_FINALLY_CLEAN(1); + r_result = hist; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_clique_number / +/-------------------------------------------*/ +SEXP R_igraph_clique_number(SEXP graph) { + /* Declarations */ + igraph_t c_graph; + igraph_integer_t c_no; + SEXP no; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + c_no=0; + /* Call igraph */ + igraph_clique_number(&c_graph, &c_no); + + /* Convert output */ + PROTECT(no=NEW_INTEGER(1)); + INTEGER(no)[0]=c_no; + r_result = no; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_weighted_cliques / +/-------------------------------------------*/ +SEXP R_igraph_weighted_cliques(SEXP graph, SEXP vertex_weights, SEXP min_weight, SEXP max_weight, SEXP maximal) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_t c_vertex_weights; + igraph_vector_ptr_t c_res; + igraph_real_t c_min_weight; + igraph_real_t c_max_weight; + igraph_bool_t c_maximal; + SEXP res; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (!isNull(vertex_weights)) { R_SEXP_to_vector(vertex_weights, &c_vertex_weights); } + if (0 != igraph_vector_ptr_init(&c_res, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(R_igraph_vectorlist_destroy, &c_res); + c_min_weight=REAL(min_weight)[0]; + c_max_weight=REAL(max_weight)[0]; + c_maximal=LOGICAL(maximal)[0]; + /* Call igraph */ + igraph_weighted_cliques(&c_graph, (isNull(vertex_weights) ? 0 : &c_vertex_weights), &c_res, c_min_weight, c_max_weight, c_maximal); + + /* Convert output */ + PROTECT(res=R_igraph_vectorlist_to_SEXP_p1(&c_res)); + R_igraph_vectorlist_destroy(&c_res); + IGRAPH_FINALLY_CLEAN(1); + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_largest_weighted_cliques / +/-------------------------------------------*/ +SEXP R_igraph_largest_weighted_cliques(SEXP graph, SEXP vertex_weights) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_t c_vertex_weights; + igraph_vector_ptr_t c_res; + SEXP res; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (!isNull(vertex_weights)) { R_SEXP_to_vector(vertex_weights, &c_vertex_weights); } + if (0 != igraph_vector_ptr_init(&c_res, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(R_igraph_vectorlist_destroy, &c_res); + /* Call igraph */ + igraph_largest_weighted_cliques(&c_graph, (isNull(vertex_weights) ? 0 : &c_vertex_weights), &c_res); + + /* Convert output */ + PROTECT(res=R_igraph_vectorlist_to_SEXP_p1(&c_res)); + R_igraph_vectorlist_destroy(&c_res); + IGRAPH_FINALLY_CLEAN(1); + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_weighted_clique_number / +/-------------------------------------------*/ +SEXP R_igraph_weighted_clique_number(SEXP graph, SEXP vertex_weights) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_t c_vertex_weights; + igraph_real_t c_res; + SEXP res; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (!isNull(vertex_weights)) { R_SEXP_to_vector(vertex_weights, &c_vertex_weights); } + /* Call igraph */ + igraph_weighted_clique_number(&c_graph, (isNull(vertex_weights) ? 0 : &c_vertex_weights), &c_res); + + /* Convert output */ + PROTECT(res=NEW_NUMERIC(1)); + REAL(res)[0]=c_res; + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_layout_star / +/-------------------------------------------*/ +SEXP R_igraph_layout_star(SEXP graph, SEXP center, SEXP order) { + /* Declarations */ + igraph_t c_graph; + igraph_matrix_t c_res; + igraph_integer_t c_center; + igraph_vector_t c_order; + SEXP res; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != igraph_matrix_init(&c_res, 0, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_matrix_destroy, &c_res); + c_center = (igraph_integer_t) REAL(center)[0]; + if (!isNull(order)) { R_SEXP_to_vector(order, &c_order); } + /* Call igraph */ + igraph_layout_star(&c_graph, &c_res, c_center, (isNull(order) ? 0 : &c_order)); + + /* Convert output */ + PROTECT(res=R_igraph_matrix_to_SEXP(&c_res)); + igraph_matrix_destroy(&c_res); + IGRAPH_FINALLY_CLEAN(1); + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_layout_grid / +/-------------------------------------------*/ +SEXP R_igraph_layout_grid(SEXP graph, SEXP width) { + /* Declarations */ + igraph_t c_graph; + igraph_matrix_t c_res; + long int c_width; + SEXP res; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != igraph_matrix_init(&c_res, 0, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_matrix_destroy, &c_res); + c_width=INTEGER(width)[0]; + /* Call igraph */ + igraph_layout_grid(&c_graph, &c_res, c_width); + + /* Convert output */ + PROTECT(res=R_igraph_matrix_to_SEXP(&c_res)); + igraph_matrix_destroy(&c_res); + IGRAPH_FINALLY_CLEAN(1); + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_layout_grid_3d / +/-------------------------------------------*/ +SEXP R_igraph_layout_grid_3d(SEXP graph, SEXP width, SEXP height) { + /* Declarations */ + igraph_t c_graph; + igraph_matrix_t c_res; + long int c_width; + long int c_height; + SEXP res; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != igraph_matrix_init(&c_res, 0, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_matrix_destroy, &c_res); + c_width=INTEGER(width)[0]; + c_height=INTEGER(height)[0]; + /* Call igraph */ + igraph_layout_grid_3d(&c_graph, &c_res, c_width, c_height); + + /* Convert output */ + PROTECT(res=R_igraph_matrix_to_SEXP(&c_res)); + igraph_matrix_destroy(&c_res); + IGRAPH_FINALLY_CLEAN(1); + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_layout_drl / +/-------------------------------------------*/ +SEXP R_igraph_layout_drl(SEXP graph, SEXP res, SEXP use_seed, SEXP options, SEXP weights, SEXP fixed) { + /* Declarations */ + igraph_t c_graph; + igraph_matrix_t c_res; + igraph_bool_t c_use_seed; + igraph_layout_drl_options_t c_options; + igraph_vector_t c_weights; + igraph_vector_bool_t c_fixed; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != R_SEXP_to_igraph_matrix_copy(res, &c_res)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_matrix_destroy, &c_res); + c_use_seed=LOGICAL(use_seed)[0]; + R_SEXP_to_igraph_layout_drl_options(options, &c_options); + if (!isNull(weights)) { R_SEXP_to_vector(weights, &c_weights); } + if (!isNull(fixed)) { R_SEXP_to_vector_bool(fixed, &c_fixed); } + /* Call igraph */ + igraph_layout_drl(&c_graph, &c_res, c_use_seed, &c_options, (isNull(weights) ? 0 : &c_weights), (isNull(fixed) ? 0 : &c_fixed)); + + /* Convert output */ + PROTECT(res=R_igraph_matrix_to_SEXP(&c_res)); + igraph_matrix_destroy(&c_res); + IGRAPH_FINALLY_CLEAN(1); + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_layout_drl_3d / +/-------------------------------------------*/ +SEXP R_igraph_layout_drl_3d(SEXP graph, SEXP res, SEXP use_seed, SEXP options, SEXP weights, SEXP fixed) { + /* Declarations */ + igraph_t c_graph; + igraph_matrix_t c_res; + igraph_bool_t c_use_seed; + igraph_layout_drl_options_t c_options; + igraph_vector_t c_weights; + igraph_vector_bool_t c_fixed; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != R_SEXP_to_igraph_matrix_copy(res, &c_res)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_matrix_destroy, &c_res); + c_use_seed=LOGICAL(use_seed)[0]; + R_SEXP_to_igraph_layout_drl_options(options, &c_options); + if (!isNull(weights)) { R_SEXP_to_vector(weights, &c_weights); } + if (!isNull(fixed)) { R_SEXP_to_vector_bool(fixed, &c_fixed); } + /* Call igraph */ + igraph_layout_drl_3d(&c_graph, &c_res, c_use_seed, &c_options, (isNull(weights) ? 0 : &c_weights), (isNull(fixed) ? 0 : &c_fixed)); + + /* Convert output */ + PROTECT(res=R_igraph_matrix_to_SEXP(&c_res)); + igraph_matrix_destroy(&c_res); + IGRAPH_FINALLY_CLEAN(1); + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_layout_sugiyama / +/-------------------------------------------*/ +SEXP R_igraph_layout_sugiyama(SEXP graph, SEXP layers, SEXP hgap, SEXP vgap, SEXP maxiter, SEXP weights) { + /* Declarations */ + igraph_t c_graph; + igraph_matrix_t c_res; + igraph_t c_extd_graph; + igraph_vector_t c_extd_to_orig_eids; + igraph_vector_t c_layers; + igraph_real_t c_hgap; + igraph_real_t c_vgap; + long int c_maxiter; + igraph_vector_t c_weights; + SEXP res; + SEXP extd_graph; + SEXP extd_to_orig_eids; + + SEXP r_result, r_names; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != igraph_matrix_init(&c_res, 0, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_matrix_destroy, &c_res); + extd_graph=R_GlobalEnv; /* hack to have a non-NULL value */ + if (0 != igraph_vector_init(&c_extd_to_orig_eids, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_extd_to_orig_eids); + extd_to_orig_eids=R_GlobalEnv; /* hack to have a non-NULL value */ + if (!isNull(layers)) { R_SEXP_to_vector(layers, &c_layers); } + c_hgap=REAL(hgap)[0]; + c_vgap=REAL(vgap)[0]; + c_maxiter=INTEGER(maxiter)[0]; + if (!isNull(weights)) { R_SEXP_to_vector(weights, &c_weights); } + /* Call igraph */ + igraph_layout_sugiyama(&c_graph, &c_res, (isNull(extd_graph) ? 0 : &c_extd_graph), (isNull(extd_to_orig_eids) ? 0 : &c_extd_to_orig_eids), (isNull(layers) ? 0 : &c_layers), c_hgap, c_vgap, c_maxiter, (isNull(weights) ? 0 : &c_weights)); + + /* Convert output */ + PROTECT(r_result=NEW_LIST(3)); + PROTECT(r_names=NEW_CHARACTER(3)); + PROTECT(res=R_igraph_matrix_to_SEXP(&c_res)); + igraph_matrix_destroy(&c_res); + IGRAPH_FINALLY_CLEAN(1); + IGRAPH_FINALLY(igraph_destroy, &c_extd_graph); + PROTECT(extd_graph=R_igraph_to_SEXP(&c_extd_graph)); + igraph_destroy(&c_extd_graph); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(extd_to_orig_eids=R_igraph_0orvector_to_SEXPp1(&c_extd_to_orig_eids)); + igraph_vector_destroy(&c_extd_to_orig_eids); + IGRAPH_FINALLY_CLEAN(1); + SET_VECTOR_ELT(r_result, 0, res); + SET_VECTOR_ELT(r_result, 1, extd_graph); + SET_VECTOR_ELT(r_result, 2, extd_to_orig_eids); + SET_STRING_ELT(r_names, 0, CREATE_STRING_VECTOR("res")); + SET_STRING_ELT(r_names, 1, CREATE_STRING_VECTOR("extd_graph")); + SET_STRING_ELT(r_names, 2, CREATE_STRING_VECTOR("extd_to_orig_eids")); + SET_NAMES(r_result, r_names); + UNPROTECT(4); + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_layout_mds / +/-------------------------------------------*/ +SEXP R_igraph_layout_mds(SEXP graph, SEXP dist, SEXP dim) { + /* Declarations */ + igraph_t c_graph; + igraph_matrix_t c_res; + igraph_matrix_t c_dist; + long int c_dim; + SEXP res; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != igraph_matrix_init(&c_res, 0, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_matrix_destroy, &c_res); + if (!isNull(dist)) { R_SEXP_to_matrix(dist, &c_dist); } + c_dim=INTEGER(dim)[0]; + /* Call igraph */ + igraph_layout_mds(&c_graph, &c_res, (isNull(dist) ? 0 : &c_dist), c_dim); + + /* Convert output */ + PROTECT(res=R_igraph_matrix_to_SEXP(&c_res)); + igraph_matrix_destroy(&c_res); + IGRAPH_FINALLY_CLEAN(1); + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_layout_bipartite / +/-------------------------------------------*/ +SEXP R_igraph_layout_bipartite(SEXP graph, SEXP types, SEXP hgap, SEXP vgap, SEXP maxiter) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_bool_t c_types; + igraph_matrix_t c_res; + igraph_real_t c_hgap; + igraph_real_t c_vgap; + long int c_maxiter; + SEXP res; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (!isNull(types)) { R_SEXP_to_vector_bool(types, &c_types); } + if (0 != igraph_matrix_init(&c_res, 0, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_matrix_destroy, &c_res); + c_hgap=REAL(hgap)[0]; + c_vgap=REAL(vgap)[0]; + c_maxiter=INTEGER(maxiter)[0]; + /* Call igraph */ + igraph_layout_bipartite(&c_graph, (isNull(types) ? 0 : &c_types), &c_res, c_hgap, c_vgap, c_maxiter); + + /* Convert output */ + PROTECT(res=R_igraph_matrix_to_SEXP(&c_res)); + igraph_matrix_destroy(&c_res); + IGRAPH_FINALLY_CLEAN(1); + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_layout_gem / +/-------------------------------------------*/ +SEXP R_igraph_layout_gem(SEXP graph, SEXP res, SEXP use_seed, SEXP maxiter, SEXP temp_max, SEXP temp_min, SEXP temp_init) { + /* Declarations */ + igraph_t c_graph; + igraph_matrix_t c_res; + igraph_bool_t c_use_seed; + igraph_integer_t c_maxiter; + igraph_real_t c_temp_max; + igraph_real_t c_temp_min; + igraph_real_t c_temp_init; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != R_SEXP_to_igraph_matrix_copy(res, &c_res)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_matrix_destroy, &c_res); + c_use_seed=LOGICAL(use_seed)[0]; + c_maxiter=INTEGER(maxiter)[0]; + c_temp_max=REAL(temp_max)[0]; + c_temp_min=REAL(temp_min)[0]; + c_temp_init=REAL(temp_init)[0]; + /* Call igraph */ + igraph_layout_gem(&c_graph, &c_res, c_use_seed, c_maxiter, c_temp_max, c_temp_min, c_temp_init); + + /* Convert output */ + PROTECT(res=R_igraph_matrix_to_SEXP(&c_res)); + igraph_matrix_destroy(&c_res); + IGRAPH_FINALLY_CLEAN(1); + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_layout_davidson_harel / +/-------------------------------------------*/ +SEXP R_igraph_layout_davidson_harel(SEXP graph, SEXP res, SEXP use_seed, SEXP maxiter, SEXP fineiter, SEXP cool_fact, SEXP weight_node_dist, SEXP weight_border, SEXP weight_edge_lengths, SEXP weight_edge_crossings, SEXP weight_node_edge_dist) { + /* Declarations */ + igraph_t c_graph; + igraph_matrix_t c_res; + igraph_bool_t c_use_seed; + igraph_integer_t c_maxiter; + igraph_integer_t c_fineiter; + igraph_real_t c_cool_fact; + igraph_real_t c_weight_node_dist; + igraph_real_t c_weight_border; + igraph_real_t c_weight_edge_lengths; + igraph_real_t c_weight_edge_crossings; + igraph_real_t c_weight_node_edge_dist; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != R_SEXP_to_igraph_matrix_copy(res, &c_res)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_matrix_destroy, &c_res); + c_use_seed=LOGICAL(use_seed)[0]; + c_maxiter=INTEGER(maxiter)[0]; + c_fineiter=INTEGER(fineiter)[0]; + c_cool_fact=REAL(cool_fact)[0]; + c_weight_node_dist=REAL(weight_node_dist)[0]; + c_weight_border=REAL(weight_border)[0]; + c_weight_edge_lengths=REAL(weight_edge_lengths)[0]; + c_weight_edge_crossings=REAL(weight_edge_crossings)[0]; + c_weight_node_edge_dist=REAL(weight_node_edge_dist)[0]; + /* Call igraph */ + igraph_layout_davidson_harel(&c_graph, &c_res, c_use_seed, c_maxiter, c_fineiter, c_cool_fact, c_weight_node_dist, c_weight_border, c_weight_edge_lengths, c_weight_edge_crossings, c_weight_node_edge_dist); + + /* Convert output */ + PROTECT(res=R_igraph_matrix_to_SEXP(&c_res)); + igraph_matrix_destroy(&c_res); + IGRAPH_FINALLY_CLEAN(1); + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_similarity_jaccard / +/-------------------------------------------*/ +SEXP R_igraph_similarity_jaccard(SEXP graph, SEXP vids, SEXP mode, SEXP loops) { + /* Declarations */ + igraph_t c_graph; + igraph_matrix_t c_res; + igraph_vs_t c_vids; + igraph_neimode_t c_mode; + igraph_bool_t c_loops; + SEXP res; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != igraph_matrix_init(&c_res, 0, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_matrix_destroy, &c_res); + R_SEXP_to_igraph_vs(vids, &c_graph, &c_vids); + c_mode = (igraph_neimode_t) Rf_asInteger(mode); + c_loops=LOGICAL(loops)[0]; + /* Call igraph */ + igraph_similarity_jaccard(&c_graph, &c_res, c_vids, c_mode, c_loops); + + /* Convert output */ + PROTECT(res=R_igraph_matrix_to_SEXP(&c_res)); + igraph_matrix_destroy(&c_res); + IGRAPH_FINALLY_CLEAN(1); + igraph_vs_destroy(&c_vids); + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_similarity_dice / +/-------------------------------------------*/ +SEXP R_igraph_similarity_dice(SEXP graph, SEXP vids, SEXP mode, SEXP loops) { + /* Declarations */ + igraph_t c_graph; + igraph_matrix_t c_res; + igraph_vs_t c_vids; + igraph_neimode_t c_mode; + igraph_bool_t c_loops; + SEXP res; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != igraph_matrix_init(&c_res, 0, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_matrix_destroy, &c_res); + R_SEXP_to_igraph_vs(vids, &c_graph, &c_vids); + c_mode = (igraph_neimode_t) Rf_asInteger(mode); + c_loops=LOGICAL(loops)[0]; + /* Call igraph */ + igraph_similarity_dice(&c_graph, &c_res, c_vids, c_mode, c_loops); + + /* Convert output */ + PROTECT(res=R_igraph_matrix_to_SEXP(&c_res)); + igraph_matrix_destroy(&c_res); + IGRAPH_FINALLY_CLEAN(1); + igraph_vs_destroy(&c_vids); + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_similarity_inverse_log_weighted / +/-------------------------------------------*/ +SEXP R_igraph_similarity_inverse_log_weighted(SEXP graph, SEXP vids, SEXP mode) { + /* Declarations */ + igraph_t c_graph; + igraph_matrix_t c_res; + igraph_vs_t c_vids; + igraph_neimode_t c_mode; + SEXP res; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != igraph_matrix_init(&c_res, 0, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_matrix_destroy, &c_res); + R_SEXP_to_igraph_vs(vids, &c_graph, &c_vids); + c_mode = (igraph_neimode_t) Rf_asInteger(mode); + /* Call igraph */ + igraph_similarity_inverse_log_weighted(&c_graph, &c_res, c_vids, c_mode); + + /* Convert output */ + PROTECT(res=R_igraph_matrix_to_SEXP(&c_res)); + igraph_matrix_destroy(&c_res); + IGRAPH_FINALLY_CLEAN(1); + igraph_vs_destroy(&c_vids); + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_compare_communities / +/-------------------------------------------*/ +SEXP R_igraph_compare_communities(SEXP comm1, SEXP comm2, SEXP method) { + /* Declarations */ + igraph_vector_t c_comm1; + igraph_vector_t c_comm2; + igraph_real_t c_res; + igraph_community_comparison_t c_method; + SEXP res; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_vector(comm1, &c_comm1); + R_SEXP_to_vector(comm2, &c_comm2); + c_method = (igraph_community_comparison_t) Rf_asInteger(method); + /* Call igraph */ + igraph_compare_communities(&c_comm1, &c_comm2, &c_res, c_method); + + /* Convert output */ + PROTECT(res=NEW_NUMERIC(1)); + REAL(res)[0]=c_res; + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_modularity / +/-------------------------------------------*/ +SEXP R_igraph_modularity(SEXP graph, SEXP membership, SEXP weights, SEXP resolution, SEXP directed) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_t c_membership; + igraph_vector_t c_weights; + igraph_real_t c_resolution; + igraph_bool_t c_directed; + igraph_real_t c_modularity; + SEXP modularity; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + R_SEXP_to_vector(membership, &c_membership); + if (!isNull(weights)) { R_SEXP_to_vector(weights, &c_weights); } + c_resolution=REAL(resolution)[0]; + c_directed=LOGICAL(directed)[0]; + /* Call igraph */ + igraph_modularity(&c_graph, &c_membership, (isNull(weights) ? 0 : &c_weights), c_resolution, c_directed, &c_modularity); + + /* Convert output */ + PROTECT(modularity=NEW_NUMERIC(1)); + REAL(modularity)[0]=c_modularity; + r_result = modularity; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_modularity_matrix / +/-------------------------------------------*/ +SEXP R_igraph_modularity_matrix(SEXP graph, SEXP weights, SEXP resolution, SEXP directed) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_t c_weights; + igraph_real_t c_resolution; + igraph_matrix_t c_modmat; + igraph_bool_t c_directed; + SEXP modmat; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (!isNull(weights)) { R_SEXP_to_vector(weights, &c_weights); } + c_resolution=REAL(resolution)[0]; + if (0 != igraph_matrix_init(&c_modmat, 0, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_matrix_destroy, &c_modmat); + c_directed=LOGICAL(directed)[0]; + /* Call igraph */ + igraph_modularity_matrix(&c_graph, (isNull(weights) ? 0 : &c_weights), c_resolution, &c_modmat, c_directed); + + /* Convert output */ + PROTECT(modmat=R_igraph_matrix_to_SEXP(&c_modmat)); + igraph_matrix_destroy(&c_modmat); + IGRAPH_FINALLY_CLEAN(1); + r_result = modmat; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_community_fluid_communities / +/-------------------------------------------*/ +SEXP R_igraph_community_fluid_communities(SEXP graph, SEXP no_of_communities) { + /* Declarations */ + igraph_t c_graph; + igraph_integer_t c_no_of_communities; + igraph_vector_t c_membership; + igraph_real_t c_modularity; + SEXP membership; + SEXP modularity; + + SEXP r_result, r_names; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + c_no_of_communities=INTEGER(no_of_communities)[0]; + if (0 != igraph_vector_init(&c_membership, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_membership); + /* Call igraph */ + igraph_community_fluid_communities(&c_graph, c_no_of_communities, &c_membership, &c_modularity); + + /* Convert output */ + PROTECT(r_result=NEW_LIST(2)); + PROTECT(r_names=NEW_CHARACTER(2)); + PROTECT(membership=R_igraph_vector_to_SEXP(&c_membership)); + igraph_vector_destroy(&c_membership); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(modularity=NEW_NUMERIC(1)); + REAL(modularity)[0]=c_modularity; + SET_VECTOR_ELT(r_result, 0, membership); + SET_VECTOR_ELT(r_result, 1, modularity); + SET_STRING_ELT(r_names, 0, CREATE_STRING_VECTOR("membership")); + SET_STRING_ELT(r_names, 1, CREATE_STRING_VECTOR("modularity")); + SET_NAMES(r_result, r_names); + UNPROTECT(3); + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_community_label_propagation / +/-------------------------------------------*/ +SEXP R_igraph_community_label_propagation(SEXP graph, SEXP weights, SEXP initial, SEXP fixed) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_t c_membership; + igraph_vector_t c_weights; + igraph_vector_t c_initial; + igraph_vector_bool_t c_fixed; + igraph_real_t c_modularity; + SEXP membership; + SEXP modularity; + + SEXP r_result, r_names; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != igraph_vector_init(&c_membership, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_membership); + if (!isNull(weights)) { R_SEXP_to_vector(weights, &c_weights); } + if (!isNull(initial)) { R_SEXP_to_vector(initial, &c_initial); } + if (!isNull(fixed)) { R_SEXP_to_vector_bool(fixed, &c_fixed); } + /* Call igraph */ + igraph_community_label_propagation(&c_graph, &c_membership, (isNull(weights) ? 0 : &c_weights), (isNull(initial) ? 0 : &c_initial), (isNull(fixed) ? 0 : &c_fixed), &c_modularity); + + /* Convert output */ + PROTECT(r_result=NEW_LIST(2)); + PROTECT(r_names=NEW_CHARACTER(2)); + PROTECT(membership=R_igraph_vector_to_SEXP(&c_membership)); + igraph_vector_destroy(&c_membership); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(modularity=NEW_NUMERIC(1)); + REAL(modularity)[0]=c_modularity; + SET_VECTOR_ELT(r_result, 0, membership); + SET_VECTOR_ELT(r_result, 1, modularity); + SET_STRING_ELT(r_names, 0, CREATE_STRING_VECTOR("membership")); + SET_STRING_ELT(r_names, 1, CREATE_STRING_VECTOR("modularity")); + SET_NAMES(r_result, r_names); + UNPROTECT(3); + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_community_multilevel / +/-------------------------------------------*/ +SEXP R_igraph_community_multilevel(SEXP graph, SEXP weights, SEXP resolution) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_t c_weights; + igraph_real_t c_resolution; + igraph_vector_t c_membership; + igraph_matrix_t c_memberships; + igraph_vector_t c_modularity; + SEXP membership; + SEXP memberships; + SEXP modularity; + + SEXP r_result, r_names; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (!isNull(weights)) { R_SEXP_to_vector(weights, &c_weights); } + c_resolution=REAL(resolution)[0]; + if (0 != igraph_vector_init(&c_membership, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_membership); + if (0 != igraph_matrix_init(&c_memberships, 0, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_matrix_destroy, &c_memberships); + memberships=R_GlobalEnv; /* hack to have a non-NULL value */ + if (0 != igraph_vector_init(&c_modularity, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_modularity); + modularity=R_GlobalEnv; /* hack to have a non-NULL value */ + /* Call igraph */ + igraph_community_multilevel(&c_graph, (isNull(weights) ? 0 : &c_weights), c_resolution, &c_membership, (isNull(memberships) ? 0 : &c_memberships), (isNull(modularity) ? 0 : &c_modularity)); + + /* Convert output */ + PROTECT(r_result=NEW_LIST(3)); + PROTECT(r_names=NEW_CHARACTER(3)); + PROTECT(membership=R_igraph_vector_to_SEXP(&c_membership)); + igraph_vector_destroy(&c_membership); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(memberships=R_igraph_0ormatrix_to_SEXP(&c_memberships)); + igraph_matrix_destroy(&c_memberships); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(modularity=R_igraph_0orvector_to_SEXP(&c_modularity)); + igraph_vector_destroy(&c_modularity); + IGRAPH_FINALLY_CLEAN(1); + SET_VECTOR_ELT(r_result, 0, membership); + SET_VECTOR_ELT(r_result, 1, memberships); + SET_VECTOR_ELT(r_result, 2, modularity); + SET_STRING_ELT(r_names, 0, CREATE_STRING_VECTOR("membership")); + SET_STRING_ELT(r_names, 1, CREATE_STRING_VECTOR("memberships")); + SET_STRING_ELT(r_names, 2, CREATE_STRING_VECTOR("modularity")); + SET_NAMES(r_result, r_names); + UNPROTECT(4); + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_community_optimal_modularity / +/-------------------------------------------*/ +SEXP R_igraph_community_optimal_modularity(SEXP graph, SEXP weights) { + /* Declarations */ + igraph_t c_graph; + igraph_real_t c_modularity; + igraph_vector_t c_membership; + igraph_vector_t c_weights; + SEXP modularity; + SEXP membership; + + SEXP r_result, r_names; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != igraph_vector_init(&c_membership, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_membership); + membership=R_GlobalEnv; /* hack to have a non-NULL value */ + if (!isNull(weights)) { R_SEXP_to_vector(weights, &c_weights); } + /* Call igraph */ + igraph_community_optimal_modularity(&c_graph, &c_modularity, (isNull(membership) ? 0 : &c_membership), (isNull(weights) ? 0 : &c_weights)); + + /* Convert output */ + PROTECT(r_result=NEW_LIST(2)); + PROTECT(r_names=NEW_CHARACTER(2)); + PROTECT(modularity=NEW_NUMERIC(1)); + REAL(modularity)[0]=c_modularity; + PROTECT(membership=R_igraph_0orvector_to_SEXP(&c_membership)); + igraph_vector_destroy(&c_membership); + IGRAPH_FINALLY_CLEAN(1); + SET_VECTOR_ELT(r_result, 0, modularity); + SET_VECTOR_ELT(r_result, 1, membership); + SET_STRING_ELT(r_names, 0, CREATE_STRING_VECTOR("modularity")); + SET_STRING_ELT(r_names, 1, CREATE_STRING_VECTOR("membership")); + SET_NAMES(r_result, r_names); + UNPROTECT(3); + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_community_leiden / +/-------------------------------------------*/ +SEXP R_igraph_community_leiden(SEXP graph, SEXP weights, SEXP vertex_weights, SEXP resolution_parameter, SEXP beta, SEXP start, SEXP membership) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_t c_weights; + igraph_vector_t c_vertex_weights; + igraph_real_t c_resolution_parameter; + igraph_real_t c_beta; + igraph_bool_t c_start; + igraph_vector_t c_membership; + igraph_integer_t c_nb_clusters; + igraph_real_t c_quality; + SEXP nb_clusters; + SEXP quality; + + SEXP r_result, r_names; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (!isNull(weights)) { R_SEXP_to_vector(weights, &c_weights); } + if (!isNull(vertex_weights)) { R_SEXP_to_vector(vertex_weights, &c_vertex_weights); } + c_resolution_parameter=REAL(resolution_parameter)[0]; + c_beta=REAL(beta)[0]; + c_start=LOGICAL(start)[0]; + if (!isNull(membership)) { + if (0 != R_SEXP_to_vector_copy(membership, &c_membership)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + } else { + if (0 != igraph_vector_init(&c_membership, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_membership); + membership=NEW_NUMERIC(0); + c_nb_clusters=0; + /* Call igraph */ + igraph_community_leiden(&c_graph, (isNull(weights) ? 0 : &c_weights), (isNull(vertex_weights) ? 0 : &c_vertex_weights), c_resolution_parameter, c_beta, c_start, (isNull(membership) ? 0 : &c_membership), &c_nb_clusters, &c_quality); + + /* Convert output */ + PROTECT(r_result=NEW_LIST(3)); + PROTECT(r_names=NEW_CHARACTER(3)); + PROTECT(membership=R_igraph_0orvector_to_SEXP(&c_membership)); + igraph_vector_destroy(&c_membership); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(nb_clusters=NEW_INTEGER(1)); + INTEGER(nb_clusters)[0]=c_nb_clusters; + PROTECT(quality=NEW_NUMERIC(1)); + REAL(quality)[0]=c_quality; + SET_VECTOR_ELT(r_result, 0, membership); + SET_VECTOR_ELT(r_result, 1, nb_clusters); + SET_VECTOR_ELT(r_result, 2, quality); + SET_STRING_ELT(r_names, 0, CREATE_STRING_VECTOR("membership")); + SET_STRING_ELT(r_names, 1, CREATE_STRING_VECTOR("nb_clusters")); + SET_STRING_ELT(r_names, 2, CREATE_STRING_VECTOR("quality")); + SET_NAMES(r_result, r_names); + UNPROTECT(4); + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_split_join_distance / +/-------------------------------------------*/ +SEXP R_igraph_split_join_distance(SEXP comm1, SEXP comm2) { + /* Declarations */ + igraph_vector_t c_comm1; + igraph_vector_t c_comm2; + igraph_integer_t c_distance12; + igraph_integer_t c_distance21; + SEXP distance12; + SEXP distance21; + + SEXP r_result, r_names; + /* Convert input */ + R_SEXP_to_vector(comm1, &c_comm1); + R_SEXP_to_vector(comm2, &c_comm2); + c_distance12=0; + c_distance21=0; + /* Call igraph */ + igraph_split_join_distance(&c_comm1, &c_comm2, &c_distance12, &c_distance21); + + /* Convert output */ + PROTECT(r_result=NEW_LIST(2)); + PROTECT(r_names=NEW_CHARACTER(2)); + PROTECT(distance12=NEW_INTEGER(1)); + INTEGER(distance12)[0]=c_distance12; + PROTECT(distance21=NEW_INTEGER(1)); + INTEGER(distance21)[0]=c_distance21; + SET_VECTOR_ELT(r_result, 0, distance12); + SET_VECTOR_ELT(r_result, 1, distance21); + SET_STRING_ELT(r_names, 0, CREATE_STRING_VECTOR("distance12")); + SET_STRING_ELT(r_names, 1, CREATE_STRING_VECTOR("distance21")); + SET_NAMES(r_result, r_names); + UNPROTECT(3); + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_hrg_fit / +/-------------------------------------------*/ +SEXP R_igraph_hrg_fit(SEXP graph, SEXP hrg, SEXP start, SEXP steps) { + /* Declarations */ + igraph_t c_graph; + igraph_hrg_t c_hrg; + igraph_bool_t c_start; + int c_steps; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != R_SEXP_to_hrg_copy(hrg, &c_hrg)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_hrg_destroy, &c_hrg); + c_start=LOGICAL(start)[0]; + c_steps=INTEGER(steps)[0]; + /* Call igraph */ + igraph_hrg_fit(&c_graph, &c_hrg, c_start, c_steps); + + /* Convert output */ + PROTECT(hrg=R_igraph_hrg_to_SEXP(&c_hrg)); + igraph_hrg_destroy(&c_hrg); + IGRAPH_FINALLY_CLEAN(1); + r_result = hrg; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_hrg_game / +/-------------------------------------------*/ +SEXP R_igraph_hrg_game(SEXP hrg) { + /* Declarations */ + igraph_t c_graph; + igraph_hrg_t c_hrg; + SEXP graph; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_hrg(hrg, &c_hrg); + /* Call igraph */ + igraph_hrg_game(&c_graph, &c_hrg); + + /* Convert output */ + IGRAPH_FINALLY(igraph_destroy, &c_graph); + PROTECT(graph=R_igraph_to_SEXP(&c_graph)); + igraph_destroy(&c_graph); + IGRAPH_FINALLY_CLEAN(1); + r_result = graph; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_hrg_dendrogram / +/-------------------------------------------*/ +SEXP R_igraph_hrg_dendrogram(SEXP hrg) { + /* Declarations */ + igraph_t c_graph; + igraph_hrg_t c_hrg; + SEXP graph; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_hrg(hrg, &c_hrg); + /* Call igraph */ + igraph_hrg_dendrogram(&c_graph, &c_hrg); + + /* Convert output */ + IGRAPH_FINALLY(igraph_destroy, &c_graph); + PROTECT(graph=R_igraph_to_SEXP(&c_graph)); + igraph_destroy(&c_graph); + IGRAPH_FINALLY_CLEAN(1); + r_result = graph; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_hrg_consensus / +/-------------------------------------------*/ +SEXP R_igraph_hrg_consensus(SEXP graph, SEXP hrg, SEXP start, SEXP num_samples) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_t c_parents; + igraph_vector_t c_weights; + igraph_hrg_t c_hrg; + igraph_bool_t c_start; + int c_num_samples; + SEXP parents; + SEXP weights; + + SEXP r_result, r_names; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != igraph_vector_init(&c_parents, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_parents); + if (0 != igraph_vector_init(&c_weights, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_weights); + if (0 != R_SEXP_to_hrg_copy(hrg, &c_hrg)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_hrg_destroy, &c_hrg); + c_start=LOGICAL(start)[0]; + c_num_samples=INTEGER(num_samples)[0]; + /* Call igraph */ + igraph_hrg_consensus(&c_graph, &c_parents, &c_weights, &c_hrg, c_start, c_num_samples); + + /* Convert output */ + PROTECT(r_result=NEW_LIST(3)); + PROTECT(r_names=NEW_CHARACTER(3)); + PROTECT(parents=R_igraph_vector_to_SEXP(&c_parents)); + igraph_vector_destroy(&c_parents); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(weights=R_igraph_vector_to_SEXP(&c_weights)); + igraph_vector_destroy(&c_weights); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(hrg=R_igraph_hrg_to_SEXP(&c_hrg)); + igraph_hrg_destroy(&c_hrg); + IGRAPH_FINALLY_CLEAN(1); + SET_VECTOR_ELT(r_result, 0, parents); + SET_VECTOR_ELT(r_result, 1, weights); + SET_VECTOR_ELT(r_result, 2, hrg); + SET_STRING_ELT(r_names, 0, CREATE_STRING_VECTOR("parents")); + SET_STRING_ELT(r_names, 1, CREATE_STRING_VECTOR("weights")); + SET_STRING_ELT(r_names, 2, CREATE_STRING_VECTOR("hrg")); + SET_NAMES(r_result, r_names); + UNPROTECT(4); + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_hrg_predict / +/-------------------------------------------*/ +SEXP R_igraph_hrg_predict(SEXP graph, SEXP hrg, SEXP start, SEXP num_samples, SEXP num_bins) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_t c_edges; + igraph_vector_t c_prob; + igraph_hrg_t c_hrg; + igraph_bool_t c_start; + int c_num_samples; + int c_num_bins; + SEXP edges; + SEXP prob; + + SEXP r_result, r_names; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != igraph_vector_init(&c_edges, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_edges); + if (0 != igraph_vector_init(&c_prob, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_prob); + if (0 != R_SEXP_to_hrg_copy(hrg, &c_hrg)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_hrg_destroy, &c_hrg); + c_start=LOGICAL(start)[0]; + c_num_samples=INTEGER(num_samples)[0]; + c_num_bins=INTEGER(num_bins)[0]; + /* Call igraph */ + igraph_hrg_predict(&c_graph, &c_edges, &c_prob, &c_hrg, c_start, c_num_samples, c_num_bins); + + /* Convert output */ + PROTECT(r_result=NEW_LIST(3)); + PROTECT(r_names=NEW_CHARACTER(3)); + PROTECT(edges=R_igraph_vector_to_SEXPp1(&c_edges)); + igraph_vector_destroy(&c_edges); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(prob=R_igraph_vector_to_SEXP(&c_prob)); + igraph_vector_destroy(&c_prob); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(hrg=R_igraph_hrg_to_SEXP(&c_hrg)); + igraph_hrg_destroy(&c_hrg); + IGRAPH_FINALLY_CLEAN(1); + SET_VECTOR_ELT(r_result, 0, edges); + SET_VECTOR_ELT(r_result, 1, prob); + SET_VECTOR_ELT(r_result, 2, hrg); + SET_STRING_ELT(r_names, 0, CREATE_STRING_VECTOR("edges")); + SET_STRING_ELT(r_names, 1, CREATE_STRING_VECTOR("prob")); + SET_STRING_ELT(r_names, 2, CREATE_STRING_VECTOR("hrg")); + SET_NAMES(r_result, r_names); + UNPROTECT(4); + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_hrg_create / +/-------------------------------------------*/ +SEXP R_igraph_hrg_create(SEXP graph, SEXP prob) { + /* Declarations */ + igraph_hrg_t c_hrg; + igraph_t c_graph; + igraph_vector_t c_prob; + SEXP hrg; + + SEXP r_result; + /* Convert input */ + if (0 != igraph_hrg_init(&c_hrg, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_hrg_destroy, &c_hrg); + R_SEXP_to_igraph(graph, &c_graph); + R_SEXP_to_vector(prob, &c_prob); + /* Call igraph */ + igraph_hrg_create(&c_hrg, &c_graph, &c_prob); + + /* Convert output */ + PROTECT(hrg=R_igraph_hrg_to_SEXP(&c_hrg)); + igraph_hrg_destroy(&c_hrg); + IGRAPH_FINALLY_CLEAN(1); + r_result = hrg; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_community_infomap / +/-------------------------------------------*/ +SEXP R_igraph_community_infomap(SEXP graph, SEXP e_weights, SEXP v_weights, SEXP nb_trials) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_t c_e_weights; + igraph_vector_t c_v_weights; + int c_nb_trials; + igraph_vector_t c_membership; + igraph_real_t c_codelength; + SEXP membership; + SEXP codelength; + + SEXP r_result, r_names; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (!isNull(e_weights)) { R_SEXP_to_vector(e_weights, &c_e_weights); } + if (!isNull(v_weights)) { R_SEXP_to_vector(v_weights, &c_v_weights); } + c_nb_trials=INTEGER(nb_trials)[0]; + if (0 != igraph_vector_init(&c_membership, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_membership); + /* Call igraph */ + igraph_community_infomap(&c_graph, (isNull(e_weights) ? 0 : &c_e_weights), (isNull(v_weights) ? 0 : &c_v_weights), c_nb_trials, &c_membership, &c_codelength); + + /* Convert output */ + PROTECT(r_result=NEW_LIST(2)); + PROTECT(r_names=NEW_CHARACTER(2)); + PROTECT(membership=R_igraph_vector_to_SEXP(&c_membership)); + igraph_vector_destroy(&c_membership); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(codelength=NEW_NUMERIC(1)); + REAL(codelength)[0]=c_codelength; + SET_VECTOR_ELT(r_result, 0, membership); + SET_VECTOR_ELT(r_result, 1, codelength); + SET_STRING_ELT(r_names, 0, CREATE_STRING_VECTOR("membership")); + SET_STRING_ELT(r_names, 1, CREATE_STRING_VECTOR("codelength")); + SET_NAMES(r_result, r_names); + UNPROTECT(3); + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_to_directed / +/-------------------------------------------*/ +SEXP R_igraph_to_directed(SEXP graph, SEXP mode) { + /* Declarations */ + igraph_t c_graph; + igraph_to_directed_t c_mode; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph_copy(graph, &c_graph); + IGRAPH_FINALLY(igraph_destroy, &c_graph); + c_mode = (igraph_to_directed_t) Rf_asInteger(mode); + /* Call igraph */ + igraph_to_directed(&c_graph, c_mode); + + /* Convert output */ + PROTECT(graph=R_igraph_to_SEXP(&c_graph)); + igraph_destroy(&c_graph); + IGRAPH_FINALLY_CLEAN(1); + r_result = graph; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_to_undirected / +/-------------------------------------------*/ +SEXP R_igraph_to_undirected(SEXP graph, SEXP mode, SEXP edge_attr_comb) { + /* Declarations */ + igraph_t c_graph; + igraph_to_undirected_t c_mode; + igraph_attribute_combination_t c_edge_attr_comb; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph_copy(graph, &c_graph); + IGRAPH_FINALLY(igraph_destroy, &c_graph); + c_mode = (igraph_to_undirected_t) Rf_asInteger(mode); + R_SEXP_to_attr_comb(edge_attr_comb, &c_edge_attr_comb); + /* Call igraph */ + igraph_to_undirected(&c_graph, c_mode, &c_edge_attr_comb); + + /* Convert output */ + PROTECT(graph=R_igraph_to_SEXP(&c_graph)); + igraph_destroy(&c_graph); + IGRAPH_FINALLY_CLEAN(1); + igraph_attribute_combination_destroy(&c_edge_attr_comb); + r_result = graph; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_get_stochastic / +/-------------------------------------------*/ +SEXP R_igraph_get_stochastic(SEXP graph, SEXP column_wise) { + /* Declarations */ + igraph_t c_graph; + igraph_matrix_t c_res; + igraph_bool_t c_column_wise; + SEXP res; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != igraph_matrix_init(&c_res, 0, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_matrix_destroy, &c_res); + c_column_wise=LOGICAL(column_wise)[0]; + /* Call igraph */ + igraph_get_stochastic(&c_graph, &c_res, c_column_wise); + + /* Convert output */ + PROTECT(res=R_igraph_matrix_to_SEXP(&c_res)); + igraph_matrix_destroy(&c_res); + IGRAPH_FINALLY_CLEAN(1); + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_get_stochastic_sparsemat / +/-------------------------------------------*/ +SEXP R_igraph_get_stochastic_sparsemat(SEXP graph, SEXP column_wise) { + /* Declarations */ + igraph_t c_graph; + igraph_sparsemat_t c_sparsemat; + igraph_bool_t c_column_wise; + SEXP sparsemat; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + /* Don't need to init. */ + c_column_wise=LOGICAL(column_wise)[0]; + /* Call igraph */ + igraph_get_stochastic_sparsemat(&c_graph, &c_sparsemat, c_column_wise); + + /* Convert output */ + PROTECT(sparsemat=R_igraph_sparsemat_to_SEXP(&c_sparsemat)); + igraph_sparsemat_destroy(&c_sparsemat); + r_result = sparsemat; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_dyad_census / +/-------------------------------------------*/ +SEXP R_igraph_dyad_census(SEXP graph) { + /* Declarations */ + igraph_t c_graph; + igraph_integer_t c_mut; + igraph_integer_t c_asym; + igraph_integer_t c_null; + SEXP mut; + SEXP asym; + SEXP null; + + SEXP r_result, r_names; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + c_mut=0; + c_asym=0; + c_null=0; + /* Call igraph */ + igraph_dyad_census(&c_graph, &c_mut, &c_asym, &c_null); + + /* Convert output */ + PROTECT(r_result=NEW_LIST(3)); + PROTECT(r_names=NEW_CHARACTER(3)); + PROTECT(mut=NEW_INTEGER(1)); + INTEGER(mut)[0]=c_mut; + PROTECT(asym=NEW_INTEGER(1)); + INTEGER(asym)[0]=c_asym; + PROTECT(null=NEW_INTEGER(1)); + INTEGER(null)[0]=c_null; + SET_VECTOR_ELT(r_result, 0, mut); + SET_VECTOR_ELT(r_result, 1, asym); + SET_VECTOR_ELT(r_result, 2, null); + SET_STRING_ELT(r_names, 0, CREATE_STRING_VECTOR("mut")); + SET_STRING_ELT(r_names, 1, CREATE_STRING_VECTOR("asym")); + SET_STRING_ELT(r_names, 2, CREATE_STRING_VECTOR("null")); + SET_NAMES(r_result, r_names); + UNPROTECT(4); + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_triad_census / +/-------------------------------------------*/ +SEXP R_igraph_triad_census(SEXP graph) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_t c_res; + SEXP res; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != igraph_vector_init(&c_res, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_res); + /* Call igraph */ + igraph_triad_census(&c_graph, &c_res); + + /* Convert output */ + PROTECT(res=R_igraph_vector_to_SEXP(&c_res)); + igraph_vector_destroy(&c_res); + IGRAPH_FINALLY_CLEAN(1); + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_adjacent_triangles / +/-------------------------------------------*/ +SEXP R_igraph_adjacent_triangles(SEXP graph, SEXP vids) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_t c_res; + igraph_vs_t c_vids; + SEXP res; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != igraph_vector_init(&c_res, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_res); + R_SEXP_to_igraph_vs(vids, &c_graph, &c_vids); + /* Call igraph */ + igraph_adjacent_triangles(&c_graph, &c_res, c_vids); + + /* Convert output */ + PROTECT(res=R_igraph_vector_to_SEXP(&c_res)); + igraph_vector_destroy(&c_res); + IGRAPH_FINALLY_CLEAN(1); + igraph_vs_destroy(&c_vids); + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_local_scan_0 / +/-------------------------------------------*/ +SEXP R_igraph_local_scan_0(SEXP graph, SEXP weights, SEXP mode) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_t c_res; + igraph_vector_t c_weights; + igraph_neimode_t c_mode; + SEXP res; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != igraph_vector_init(&c_res, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_res); + if (!isNull(weights)) { R_SEXP_to_vector(weights, &c_weights); } + c_mode = (igraph_neimode_t) Rf_asInteger(mode); + /* Call igraph */ + igraph_local_scan_0(&c_graph, &c_res, (isNull(weights) ? 0 : &c_weights), c_mode); + + /* Convert output */ + PROTECT(res=R_igraph_vector_to_SEXP(&c_res)); + igraph_vector_destroy(&c_res); + IGRAPH_FINALLY_CLEAN(1); + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_local_scan_0_them / +/-------------------------------------------*/ +SEXP R_igraph_local_scan_0_them(SEXP us, SEXP them, SEXP weights_them, SEXP mode) { + /* Declarations */ + igraph_t c_us; + igraph_t c_them; + igraph_vector_t c_res; + igraph_vector_t c_weights_them; + igraph_neimode_t c_mode; + SEXP res; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(us, &c_us); + R_SEXP_to_igraph(them, &c_them); + if (0 != igraph_vector_init(&c_res, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_res); + if (!isNull(weights_them)) { R_SEXP_to_vector(weights_them, &c_weights_them); } + c_mode = (igraph_neimode_t) Rf_asInteger(mode); + /* Call igraph */ + igraph_local_scan_0_them(&c_us, &c_them, &c_res, (isNull(weights_them) ? 0 : &c_weights_them), c_mode); + + /* Convert output */ + PROTECT(res=R_igraph_vector_to_SEXP(&c_res)); + igraph_vector_destroy(&c_res); + IGRAPH_FINALLY_CLEAN(1); + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_local_scan_1_ecount / +/-------------------------------------------*/ +SEXP R_igraph_local_scan_1_ecount(SEXP graph, SEXP weights, SEXP mode) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_t c_res; + igraph_vector_t c_weights; + igraph_neimode_t c_mode; + SEXP res; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != igraph_vector_init(&c_res, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_res); + if (!isNull(weights)) { R_SEXP_to_vector(weights, &c_weights); } + c_mode = (igraph_neimode_t) Rf_asInteger(mode); + /* Call igraph */ + igraph_local_scan_1_ecount(&c_graph, &c_res, (isNull(weights) ? 0 : &c_weights), c_mode); + + /* Convert output */ + PROTECT(res=R_igraph_vector_to_SEXP(&c_res)); + igraph_vector_destroy(&c_res); + IGRAPH_FINALLY_CLEAN(1); + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_local_scan_1_ecount_them / +/-------------------------------------------*/ +SEXP R_igraph_local_scan_1_ecount_them(SEXP us, SEXP them, SEXP weights_them, SEXP mode) { + /* Declarations */ + igraph_t c_us; + igraph_t c_them; + igraph_vector_t c_res; + igraph_vector_t c_weights_them; + igraph_neimode_t c_mode; + SEXP res; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(us, &c_us); + R_SEXP_to_igraph(them, &c_them); + if (0 != igraph_vector_init(&c_res, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_res); + if (!isNull(weights_them)) { R_SEXP_to_vector(weights_them, &c_weights_them); } + c_mode = (igraph_neimode_t) Rf_asInteger(mode); + /* Call igraph */ + igraph_local_scan_1_ecount_them(&c_us, &c_them, &c_res, (isNull(weights_them) ? 0 : &c_weights_them), c_mode); + + /* Convert output */ + PROTECT(res=R_igraph_vector_to_SEXP(&c_res)); + igraph_vector_destroy(&c_res); + IGRAPH_FINALLY_CLEAN(1); + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_local_scan_k_ecount / +/-------------------------------------------*/ +SEXP R_igraph_local_scan_k_ecount(SEXP graph, SEXP k, SEXP weights, SEXP mode) { + /* Declarations */ + igraph_t c_graph; + int c_k; + igraph_vector_t c_res; + igraph_vector_t c_weights; + igraph_neimode_t c_mode; + SEXP res; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + c_k=INTEGER(k)[0]; + if (0 != igraph_vector_init(&c_res, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_res); + if (!isNull(weights)) { R_SEXP_to_vector(weights, &c_weights); } + c_mode = (igraph_neimode_t) Rf_asInteger(mode); + /* Call igraph */ + igraph_local_scan_k_ecount(&c_graph, c_k, &c_res, (isNull(weights) ? 0 : &c_weights), c_mode); + + /* Convert output */ + PROTECT(res=R_igraph_vector_to_SEXP(&c_res)); + igraph_vector_destroy(&c_res); + IGRAPH_FINALLY_CLEAN(1); + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_local_scan_k_ecount_them / +/-------------------------------------------*/ +SEXP R_igraph_local_scan_k_ecount_them(SEXP us, SEXP them, SEXP k, SEXP weights_them, SEXP mode) { + /* Declarations */ + igraph_t c_us; + igraph_t c_them; + int c_k; + igraph_vector_t c_res; + igraph_vector_t c_weights_them; + igraph_neimode_t c_mode; + SEXP res; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(us, &c_us); + R_SEXP_to_igraph(them, &c_them); + c_k=INTEGER(k)[0]; + if (0 != igraph_vector_init(&c_res, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_res); + if (!isNull(weights_them)) { R_SEXP_to_vector(weights_them, &c_weights_them); } + c_mode = (igraph_neimode_t) Rf_asInteger(mode); + /* Call igraph */ + igraph_local_scan_k_ecount_them(&c_us, &c_them, c_k, &c_res, (isNull(weights_them) ? 0 : &c_weights_them), c_mode); + + /* Convert output */ + PROTECT(res=R_igraph_vector_to_SEXP(&c_res)); + igraph_vector_destroy(&c_res); + IGRAPH_FINALLY_CLEAN(1); + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_local_scan_neighborhood_ecount / +/-------------------------------------------*/ +SEXP R_igraph_local_scan_neighborhood_ecount(SEXP graph, SEXP weights, SEXP neighborhoods) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_t c_res; + igraph_vector_t c_weights; + igraph_vector_ptr_t c_neighborhoods; + SEXP res; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != igraph_vector_init(&c_res, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_res); + if (!isNull(weights)) { R_SEXP_to_vector(weights, &c_weights); } + if (!isNull(neighborhoods)) { R_igraph_SEXP_to_vectorlist_int(neighborhoods, &c_neighborhoods); } + /* Call igraph */ + igraph_local_scan_neighborhood_ecount(&c_graph, &c_res, (isNull(weights) ? 0 : &c_weights), &c_neighborhoods); + + /* Convert output */ + PROTECT(res=R_igraph_vector_to_SEXP(&c_res)); + igraph_vector_destroy(&c_res); + IGRAPH_FINALLY_CLEAN(1); + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_list_triangles / +/-------------------------------------------*/ +SEXP R_igraph_list_triangles(SEXP graph) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_int_t c_res; + SEXP res; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != igraph_vector_int_init(&c_res, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_int_destroy, &c_res); + /* Call igraph */ + igraph_list_triangles(&c_graph, &c_res); + + /* Convert output */ + PROTECT(res=R_igraph_vector_int_to_SEXPp1(&c_res)); + igraph_vector_int_destroy(&c_res); + IGRAPH_FINALLY_CLEAN(1); + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_maxflow / +/-------------------------------------------*/ +SEXP R_igraph_maxflow(SEXP graph, SEXP source, SEXP target, SEXP capacity) { + /* Declarations */ + igraph_t c_graph; + igraph_real_t c_value; + igraph_vector_t c_flow; + igraph_vector_t c_cut; + igraph_vector_t c_partition1; + igraph_vector_t c_partition2; + igraph_integer_t c_source; + igraph_integer_t c_target; + igraph_vector_t c_capacity; + igraph_maxflow_stats_t c_stats; + SEXP value; + SEXP flow; + SEXP cut; + SEXP partition1; + SEXP partition2; + SEXP stats; + + SEXP r_result, r_names; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != igraph_vector_init(&c_flow, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_flow); + flow=R_GlobalEnv; /* hack to have a non-NULL value */ + if (0 != igraph_vector_init(&c_cut, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_cut); + cut=R_GlobalEnv; /* hack to have a non-NULL value */ + if (0 != igraph_vector_init(&c_partition1, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_partition1); + if (0 != igraph_vector_init(&c_partition2, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_partition2); + c_source = (igraph_integer_t) REAL(source)[0]; + c_target = (igraph_integer_t) REAL(target)[0]; + if (!isNull(capacity)) { R_SEXP_to_vector(capacity, &c_capacity); } + /* Call igraph */ + igraph_maxflow(&c_graph, &c_value, (isNull(flow) ? 0 : &c_flow), (isNull(cut) ? 0 : &c_cut), &c_partition1, &c_partition2, c_source, c_target, (isNull(capacity) ? 0 : &c_capacity), &c_stats); + + /* Convert output */ + PROTECT(r_result=NEW_LIST(6)); + PROTECT(r_names=NEW_CHARACTER(6)); + PROTECT(value=NEW_NUMERIC(1)); + REAL(value)[0]=c_value; + PROTECT(flow=R_igraph_0orvector_to_SEXP(&c_flow)); + igraph_vector_destroy(&c_flow); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(cut=R_igraph_0orvector_to_SEXPp1(&c_cut)); + igraph_vector_destroy(&c_cut); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(partition1=R_igraph_vector_to_SEXPp1(&c_partition1)); + igraph_vector_destroy(&c_partition1); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(partition2=R_igraph_vector_to_SEXPp1(&c_partition2)); + igraph_vector_destroy(&c_partition2); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(stats=R_igraph_maxflow_stats_to_SEXP(&c_stats)); + SET_VECTOR_ELT(r_result, 0, value); + SET_VECTOR_ELT(r_result, 1, flow); + SET_VECTOR_ELT(r_result, 2, cut); + SET_VECTOR_ELT(r_result, 3, partition1); + SET_VECTOR_ELT(r_result, 4, partition2); + SET_VECTOR_ELT(r_result, 5, stats); + SET_STRING_ELT(r_names, 0, CREATE_STRING_VECTOR("value")); + SET_STRING_ELT(r_names, 1, CREATE_STRING_VECTOR("flow")); + SET_STRING_ELT(r_names, 2, CREATE_STRING_VECTOR("cut")); + SET_STRING_ELT(r_names, 3, CREATE_STRING_VECTOR("partition1")); + SET_STRING_ELT(r_names, 4, CREATE_STRING_VECTOR("partition2")); + SET_STRING_ELT(r_names, 5, CREATE_STRING_VECTOR("stats")); + SET_NAMES(r_result, r_names); + UNPROTECT(7); + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_st_mincut / +/-------------------------------------------*/ +SEXP R_igraph_st_mincut(SEXP graph, SEXP source, SEXP target, SEXP capacity) { + /* Declarations */ + igraph_t c_graph; + igraph_real_t c_value; + igraph_vector_t c_cut; + igraph_vector_t c_partition1; + igraph_vector_t c_partition2; + igraph_integer_t c_source; + igraph_integer_t c_target; + igraph_vector_t c_capacity; + SEXP value; + SEXP cut; + SEXP partition1; + SEXP partition2; + + SEXP r_result, r_names; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != igraph_vector_init(&c_cut, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_cut); + cut=R_GlobalEnv; /* hack to have a non-NULL value */ + if (0 != igraph_vector_init(&c_partition1, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_partition1); + if (0 != igraph_vector_init(&c_partition2, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_partition2); + c_source = (igraph_integer_t) REAL(source)[0]; + c_target = (igraph_integer_t) REAL(target)[0]; + if (!isNull(capacity)) { R_SEXP_to_vector(capacity, &c_capacity); } + /* Call igraph */ + igraph_st_mincut(&c_graph, &c_value, (isNull(cut) ? 0 : &c_cut), &c_partition1, &c_partition2, c_source, c_target, (isNull(capacity) ? 0 : &c_capacity)); + + /* Convert output */ + PROTECT(r_result=NEW_LIST(4)); + PROTECT(r_names=NEW_CHARACTER(4)); + PROTECT(value=NEW_NUMERIC(1)); + REAL(value)[0]=c_value; + PROTECT(cut=R_igraph_0orvector_to_SEXPp1(&c_cut)); + igraph_vector_destroy(&c_cut); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(partition1=R_igraph_vector_to_SEXPp1(&c_partition1)); + igraph_vector_destroy(&c_partition1); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(partition2=R_igraph_vector_to_SEXPp1(&c_partition2)); + igraph_vector_destroy(&c_partition2); + IGRAPH_FINALLY_CLEAN(1); + SET_VECTOR_ELT(r_result, 0, value); + SET_VECTOR_ELT(r_result, 1, cut); + SET_VECTOR_ELT(r_result, 2, partition1); + SET_VECTOR_ELT(r_result, 3, partition2); + SET_STRING_ELT(r_names, 0, CREATE_STRING_VECTOR("value")); + SET_STRING_ELT(r_names, 1, CREATE_STRING_VECTOR("cut")); + SET_STRING_ELT(r_names, 2, CREATE_STRING_VECTOR("partition1")); + SET_STRING_ELT(r_names, 3, CREATE_STRING_VECTOR("partition2")); + SET_NAMES(r_result, r_names); + UNPROTECT(5); + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_dominator_tree / +/-------------------------------------------*/ +SEXP R_igraph_dominator_tree(SEXP graph, SEXP root, SEXP mode) { + /* Declarations */ + igraph_t c_graph; + igraph_integer_t c_root; + igraph_vector_t c_dom; + igraph_t c_domtree; + igraph_vector_t c_leftout; + igraph_neimode_t c_mode; + SEXP dom; + SEXP domtree; + SEXP leftout; + + SEXP r_result, r_names; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + c_root = (igraph_integer_t) REAL(root)[0]; + if (0 != igraph_vector_init(&c_dom, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_dom); + domtree=R_GlobalEnv; /* hack to have a non-NULL value */ + if (0 != igraph_vector_init(&c_leftout, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_leftout); + c_mode = (igraph_neimode_t) Rf_asInteger(mode); + /* Call igraph */ + igraph_dominator_tree(&c_graph, c_root, &c_dom, (isNull(domtree) ? 0 : &c_domtree), &c_leftout, c_mode); + + /* Convert output */ + PROTECT(r_result=NEW_LIST(3)); + PROTECT(r_names=NEW_CHARACTER(3)); + PROTECT(dom=R_igraph_vector_to_SEXPp1(&c_dom)); + igraph_vector_destroy(&c_dom); + IGRAPH_FINALLY_CLEAN(1); + IGRAPH_FINALLY(igraph_destroy, &c_domtree); + PROTECT(domtree=R_igraph_to_SEXP(&c_domtree)); + igraph_destroy(&c_domtree); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(leftout=R_igraph_vector_to_SEXPp1(&c_leftout)); + igraph_vector_destroy(&c_leftout); + IGRAPH_FINALLY_CLEAN(1); + SET_VECTOR_ELT(r_result, 0, dom); + SET_VECTOR_ELT(r_result, 1, domtree); + SET_VECTOR_ELT(r_result, 2, leftout); + SET_STRING_ELT(r_names, 0, CREATE_STRING_VECTOR("dom")); + SET_STRING_ELT(r_names, 1, CREATE_STRING_VECTOR("domtree")); + SET_STRING_ELT(r_names, 2, CREATE_STRING_VECTOR("leftout")); + SET_NAMES(r_result, r_names); + UNPROTECT(4); + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_all_st_cuts / +/-------------------------------------------*/ +SEXP R_igraph_all_st_cuts(SEXP graph, SEXP source, SEXP target) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_ptr_t c_cuts; + igraph_vector_ptr_t c_partition1s; + igraph_integer_t c_source; + igraph_integer_t c_target; + SEXP cuts; + SEXP partition1s; + + SEXP r_result, r_names; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != igraph_vector_ptr_init(&c_cuts, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(R_igraph_vectorlist_destroy, &c_cuts); + if (0 != igraph_vector_ptr_init(&c_partition1s, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(R_igraph_vectorlist_destroy, &c_partition1s); + c_source = (igraph_integer_t) REAL(source)[0]; + c_target = (igraph_integer_t) REAL(target)[0]; + /* Call igraph */ + igraph_all_st_cuts(&c_graph, &c_cuts, &c_partition1s, c_source, c_target); + + /* Convert output */ + PROTECT(r_result=NEW_LIST(2)); + PROTECT(r_names=NEW_CHARACTER(2)); + PROTECT(cuts=R_igraph_vectorlist_to_SEXP_p1(&c_cuts)); + R_igraph_vectorlist_destroy(&c_cuts); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(partition1s=R_igraph_vectorlist_to_SEXP_p1(&c_partition1s)); + R_igraph_vectorlist_destroy(&c_partition1s); + IGRAPH_FINALLY_CLEAN(1); + SET_VECTOR_ELT(r_result, 0, cuts); + SET_VECTOR_ELT(r_result, 1, partition1s); + SET_STRING_ELT(r_names, 0, CREATE_STRING_VECTOR("cuts")); + SET_STRING_ELT(r_names, 1, CREATE_STRING_VECTOR("partition1s")); + SET_NAMES(r_result, r_names); + UNPROTECT(3); + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_all_st_mincuts / +/-------------------------------------------*/ +SEXP R_igraph_all_st_mincuts(SEXP graph, SEXP source, SEXP target, SEXP capacity) { + /* Declarations */ + igraph_t c_graph; + igraph_real_t c_value; + igraph_vector_ptr_t c_cuts; + igraph_vector_ptr_t c_partition1s; + igraph_integer_t c_source; + igraph_integer_t c_target; + igraph_vector_t c_capacity; + SEXP value; + SEXP cuts; + SEXP partition1s; + + SEXP r_result, r_names; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != igraph_vector_ptr_init(&c_cuts, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(R_igraph_vectorlist_destroy, &c_cuts); + if (0 != igraph_vector_ptr_init(&c_partition1s, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(R_igraph_vectorlist_destroy, &c_partition1s); + c_source = (igraph_integer_t) REAL(source)[0]; + c_target = (igraph_integer_t) REAL(target)[0]; + if (!isNull(capacity)) { R_SEXP_to_vector(capacity, &c_capacity); } + /* Call igraph */ + igraph_all_st_mincuts(&c_graph, &c_value, &c_cuts, &c_partition1s, c_source, c_target, (isNull(capacity) ? 0 : &c_capacity)); + + /* Convert output */ + PROTECT(r_result=NEW_LIST(3)); + PROTECT(r_names=NEW_CHARACTER(3)); + PROTECT(value=NEW_NUMERIC(1)); + REAL(value)[0]=c_value; + PROTECT(cuts=R_igraph_vectorlist_to_SEXP_p1(&c_cuts)); + R_igraph_vectorlist_destroy(&c_cuts); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(partition1s=R_igraph_vectorlist_to_SEXP_p1(&c_partition1s)); + R_igraph_vectorlist_destroy(&c_partition1s); + IGRAPH_FINALLY_CLEAN(1); + SET_VECTOR_ELT(r_result, 0, value); + SET_VECTOR_ELT(r_result, 1, cuts); + SET_VECTOR_ELT(r_result, 2, partition1s); + SET_STRING_ELT(r_names, 0, CREATE_STRING_VECTOR("value")); + SET_STRING_ELT(r_names, 1, CREATE_STRING_VECTOR("cuts")); + SET_STRING_ELT(r_names, 2, CREATE_STRING_VECTOR("partition1s")); + SET_NAMES(r_result, r_names); + UNPROTECT(4); + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_is_separator / +/-------------------------------------------*/ +SEXP R_igraph_is_separator(SEXP graph, SEXP candidate) { + /* Declarations */ + igraph_t c_graph; + igraph_vs_t c_candidate; + igraph_bool_t c_res; + SEXP res; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + R_SEXP_to_igraph_vs(candidate, &c_graph, &c_candidate); + /* Call igraph */ + igraph_is_separator(&c_graph, c_candidate, &c_res); + + /* Convert output */ + igraph_vs_destroy(&c_candidate); + PROTECT(res=NEW_LOGICAL(1)); + LOGICAL(res)[0]=c_res; + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_is_minimal_separator / +/-------------------------------------------*/ +SEXP R_igraph_is_minimal_separator(SEXP graph, SEXP candidate) { + /* Declarations */ + igraph_t c_graph; + igraph_vs_t c_candidate; + igraph_bool_t c_res; + SEXP res; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + R_SEXP_to_igraph_vs(candidate, &c_graph, &c_candidate); + /* Call igraph */ + igraph_is_minimal_separator(&c_graph, c_candidate, &c_res); + + /* Convert output */ + igraph_vs_destroy(&c_candidate); + PROTECT(res=NEW_LOGICAL(1)); + LOGICAL(res)[0]=c_res; + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_all_minimal_st_separators / +/-------------------------------------------*/ +SEXP R_igraph_all_minimal_st_separators(SEXP graph) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_ptr_t c_separators; + SEXP separators; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != igraph_vector_ptr_init(&c_separators, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(R_igraph_vectorlist_destroy, &c_separators); + /* Call igraph */ + igraph_all_minimal_st_separators(&c_graph, &c_separators); + + /* Convert output */ + PROTECT(separators=R_igraph_vectorlist_to_SEXP_p1(&c_separators)); + R_igraph_vectorlist_destroy(&c_separators); + IGRAPH_FINALLY_CLEAN(1); + r_result = separators; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_minimum_size_separators / +/-------------------------------------------*/ +SEXP R_igraph_minimum_size_separators(SEXP graph) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_ptr_t c_separators; + SEXP separators; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != igraph_vector_ptr_init(&c_separators, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(R_igraph_vectorlist_destroy, &c_separators); + /* Call igraph */ + igraph_minimum_size_separators(&c_graph, &c_separators); + + /* Convert output */ + PROTECT(separators=R_igraph_vectorlist_to_SEXP_p1(&c_separators)); + R_igraph_vectorlist_destroy(&c_separators); + IGRAPH_FINALLY_CLEAN(1); + r_result = separators; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_isoclass / +/-------------------------------------------*/ +SEXP R_igraph_isoclass(SEXP graph) { + /* Declarations */ + igraph_t c_graph; + igraph_integer_t c_isoclass; + SEXP isoclass; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + c_isoclass=0; + /* Call igraph */ + igraph_isoclass(&c_graph, &c_isoclass); + + /* Convert output */ + PROTECT(isoclass=NEW_INTEGER(1)); + INTEGER(isoclass)[0]=c_isoclass; + r_result = isoclass; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_isomorphic / +/-------------------------------------------*/ +SEXP R_igraph_isomorphic(SEXP graph1, SEXP graph2) { + /* Declarations */ + igraph_t c_graph1; + igraph_t c_graph2; + igraph_bool_t c_iso; + SEXP iso; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph1, &c_graph1); + R_SEXP_to_igraph(graph2, &c_graph2); + /* Call igraph */ + igraph_isomorphic(&c_graph1, &c_graph2, &c_iso); + + /* Convert output */ + PROTECT(iso=NEW_LOGICAL(1)); + LOGICAL(iso)[0]=c_iso; + r_result = iso; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_isoclass_subgraph / +/-------------------------------------------*/ +SEXP R_igraph_isoclass_subgraph(SEXP graph, SEXP vids) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_t c_vids; + igraph_integer_t c_isoclass; + SEXP isoclass; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + R_SEXP_to_vector(vids, &c_vids); + c_isoclass=0; + /* Call igraph */ + igraph_isoclass_subgraph(&c_graph, &c_vids, &c_isoclass); + + /* Convert output */ + PROTECT(isoclass=NEW_INTEGER(1)); + INTEGER(isoclass)[0]=c_isoclass; + r_result = isoclass; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_isoclass_create / +/-------------------------------------------*/ +SEXP R_igraph_isoclass_create(SEXP size, SEXP number, SEXP directed) { + /* Declarations */ + igraph_t c_graph; + igraph_integer_t c_size; + igraph_integer_t c_number; + igraph_bool_t c_directed; + SEXP graph; + + SEXP r_result; + /* Convert input */ + c_size=INTEGER(size)[0]; + c_number=INTEGER(number)[0]; + c_directed=LOGICAL(directed)[0]; + /* Call igraph */ + igraph_isoclass_create(&c_graph, c_size, c_number, c_directed); + + /* Convert output */ + IGRAPH_FINALLY(igraph_destroy, &c_graph); + PROTECT(graph=R_igraph_to_SEXP(&c_graph)); + igraph_destroy(&c_graph); + IGRAPH_FINALLY_CLEAN(1); + r_result = graph; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_isomorphic_vf2 / +/-------------------------------------------*/ +SEXP R_igraph_isomorphic_vf2(SEXP graph1, SEXP graph2, SEXP vertex_color1, SEXP vertex_color2, SEXP edge_color1, SEXP edge_color2) { + /* Declarations */ + igraph_t c_graph1; + igraph_t c_graph2; + igraph_vector_int_t c_vertex_color1; + igraph_vector_int_t c_vertex_color2; + igraph_vector_int_t c_edge_color1; + igraph_vector_int_t c_edge_color2; + igraph_bool_t c_iso; + igraph_vector_t c_map12; + igraph_vector_t c_map21; + + + + SEXP iso; + SEXP map12; + SEXP map21; + + SEXP r_result, r_names; + /* Convert input */ + R_SEXP_to_igraph(graph1, &c_graph1); + R_SEXP_to_igraph(graph2, &c_graph2); + if (!isNull(vertex_color1)) { R_SEXP_to_vector_int(vertex_color1, &c_vertex_color1); } + if (!isNull(vertex_color2)) { R_SEXP_to_vector_int(vertex_color2, &c_vertex_color2); } + if (!isNull(edge_color1)) { R_SEXP_to_vector_int(edge_color1, &c_edge_color1); } + if (!isNull(edge_color2)) { R_SEXP_to_vector_int(edge_color2, &c_edge_color2); } + if (0 != igraph_vector_init(&c_map12, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_map12); + map12=R_GlobalEnv; /* hack to have a non-NULL value */ + if (0 != igraph_vector_init(&c_map21, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_map21); + map21=R_GlobalEnv; /* hack to have a non-NULL value */ + /* Call igraph */ + igraph_isomorphic_vf2(&c_graph1, &c_graph2, (isNull(vertex_color1) ? 0 : &c_vertex_color1), (isNull(vertex_color2) ? 0 : &c_vertex_color2), (isNull(edge_color1) ? 0 : &c_edge_color1), (isNull(edge_color2) ? 0 : &c_edge_color2), &c_iso, (isNull(map12) ? 0 : &c_map12), (isNull(map21) ? 0 : &c_map21), 0, 0, 0); + + /* Convert output */ + PROTECT(r_result=NEW_LIST(3)); + PROTECT(r_names=NEW_CHARACTER(3)); + PROTECT(iso=NEW_LOGICAL(1)); + LOGICAL(iso)[0]=c_iso; + PROTECT(map12=R_igraph_0orvector_to_SEXPp1(&c_map12)); + igraph_vector_destroy(&c_map12); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(map21=R_igraph_0orvector_to_SEXPp1(&c_map21)); + igraph_vector_destroy(&c_map21); + IGRAPH_FINALLY_CLEAN(1); + SET_VECTOR_ELT(r_result, 0, iso); + SET_VECTOR_ELT(r_result, 1, map12); + SET_VECTOR_ELT(r_result, 2, map21); + SET_STRING_ELT(r_names, 0, CREATE_STRING_VECTOR("iso")); + SET_STRING_ELT(r_names, 1, CREATE_STRING_VECTOR("map12")); + SET_STRING_ELT(r_names, 2, CREATE_STRING_VECTOR("map21")); + SET_NAMES(r_result, r_names); + UNPROTECT(4); + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_count_isomorphisms_vf2 / +/-------------------------------------------*/ +SEXP R_igraph_count_isomorphisms_vf2(SEXP graph1, SEXP graph2, SEXP vertex_color1, SEXP vertex_color2, SEXP edge_color1, SEXP edge_color2) { + /* Declarations */ + igraph_t c_graph1; + igraph_t c_graph2; + igraph_vector_int_t c_vertex_color1; + igraph_vector_int_t c_vertex_color2; + igraph_vector_int_t c_edge_color1; + igraph_vector_int_t c_edge_color2; + igraph_integer_t c_count; + + + + SEXP count; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph1, &c_graph1); + R_SEXP_to_igraph(graph2, &c_graph2); + if (!isNull(vertex_color1)) { R_SEXP_to_vector_int(vertex_color1, &c_vertex_color1); } + if (!isNull(vertex_color2)) { R_SEXP_to_vector_int(vertex_color2, &c_vertex_color2); } + if (!isNull(edge_color1)) { R_SEXP_to_vector_int(edge_color1, &c_edge_color1); } + if (!isNull(edge_color2)) { R_SEXP_to_vector_int(edge_color2, &c_edge_color2); } + c_count=0; + /* Call igraph */ + igraph_count_isomorphisms_vf2(&c_graph1, &c_graph2, (isNull(vertex_color1) ? 0 : &c_vertex_color1), (isNull(vertex_color2) ? 0 : &c_vertex_color2), (isNull(edge_color1) ? 0 : &c_edge_color1), (isNull(edge_color2) ? 0 : &c_edge_color2), &c_count, 0, 0, 0); + + /* Convert output */ + PROTECT(count=NEW_INTEGER(1)); + INTEGER(count)[0]=c_count; + r_result = count; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_get_isomorphisms_vf2 / +/-------------------------------------------*/ +SEXP R_igraph_get_isomorphisms_vf2(SEXP graph1, SEXP graph2, SEXP vertex_color1, SEXP vertex_color2, SEXP edge_color1, SEXP edge_color2) { + /* Declarations */ + igraph_t c_graph1; + igraph_t c_graph2; + igraph_vector_int_t c_vertex_color1; + igraph_vector_int_t c_vertex_color2; + igraph_vector_int_t c_edge_color1; + igraph_vector_int_t c_edge_color2; + igraph_vector_ptr_t c_maps; + + + + SEXP maps; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph1, &c_graph1); + R_SEXP_to_igraph(graph2, &c_graph2); + if (!isNull(vertex_color1)) { R_SEXP_to_vector_int(vertex_color1, &c_vertex_color1); } + if (!isNull(vertex_color2)) { R_SEXP_to_vector_int(vertex_color2, &c_vertex_color2); } + if (!isNull(edge_color1)) { R_SEXP_to_vector_int(edge_color1, &c_edge_color1); } + if (!isNull(edge_color2)) { R_SEXP_to_vector_int(edge_color2, &c_edge_color2); } + if (0 != igraph_vector_ptr_init(&c_maps, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(R_igraph_vectorlist_destroy, &c_maps); + /* Call igraph */ + igraph_get_isomorphisms_vf2(&c_graph1, &c_graph2, (isNull(vertex_color1) ? 0 : &c_vertex_color1), (isNull(vertex_color2) ? 0 : &c_vertex_color2), (isNull(edge_color1) ? 0 : &c_edge_color1), (isNull(edge_color2) ? 0 : &c_edge_color2), &c_maps, 0, 0, 0); + + /* Convert output */ + PROTECT(maps=R_igraph_vectorlist_to_SEXP(&c_maps)); + R_igraph_vectorlist_destroy(&c_maps); + IGRAPH_FINALLY_CLEAN(1); + r_result = maps; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_subisomorphic_vf2 / +/-------------------------------------------*/ +SEXP R_igraph_subisomorphic_vf2(SEXP graph1, SEXP graph2, SEXP vertex_color1, SEXP vertex_color2, SEXP edge_color1, SEXP edge_color2) { + /* Declarations */ + igraph_t c_graph1; + igraph_t c_graph2; + igraph_vector_int_t c_vertex_color1; + igraph_vector_int_t c_vertex_color2; + igraph_vector_int_t c_edge_color1; + igraph_vector_int_t c_edge_color2; + igraph_bool_t c_iso; + igraph_vector_t c_map12; + igraph_vector_t c_map21; + + + + SEXP iso; + SEXP map12; + SEXP map21; + + SEXP r_result, r_names; + /* Convert input */ + R_SEXP_to_igraph(graph1, &c_graph1); + R_SEXP_to_igraph(graph2, &c_graph2); + if (!isNull(vertex_color1)) { R_SEXP_to_vector_int(vertex_color1, &c_vertex_color1); } + if (!isNull(vertex_color2)) { R_SEXP_to_vector_int(vertex_color2, &c_vertex_color2); } + if (!isNull(edge_color1)) { R_SEXP_to_vector_int(edge_color1, &c_edge_color1); } + if (!isNull(edge_color2)) { R_SEXP_to_vector_int(edge_color2, &c_edge_color2); } + if (0 != igraph_vector_init(&c_map12, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_map12); + map12=R_GlobalEnv; /* hack to have a non-NULL value */ + if (0 != igraph_vector_init(&c_map21, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_map21); + map21=R_GlobalEnv; /* hack to have a non-NULL value */ + /* Call igraph */ + igraph_subisomorphic_vf2(&c_graph1, &c_graph2, (isNull(vertex_color1) ? 0 : &c_vertex_color1), (isNull(vertex_color2) ? 0 : &c_vertex_color2), (isNull(edge_color1) ? 0 : &c_edge_color1), (isNull(edge_color2) ? 0 : &c_edge_color2), &c_iso, (isNull(map12) ? 0 : &c_map12), (isNull(map21) ? 0 : &c_map21), 0, 0, 0); + + /* Convert output */ + PROTECT(r_result=NEW_LIST(3)); + PROTECT(r_names=NEW_CHARACTER(3)); + PROTECT(iso=NEW_LOGICAL(1)); + LOGICAL(iso)[0]=c_iso; + PROTECT(map12=R_igraph_0orvector_to_SEXPp1(&c_map12)); + igraph_vector_destroy(&c_map12); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(map21=R_igraph_0orvector_to_SEXPp1(&c_map21)); + igraph_vector_destroy(&c_map21); + IGRAPH_FINALLY_CLEAN(1); + SET_VECTOR_ELT(r_result, 0, iso); + SET_VECTOR_ELT(r_result, 1, map12); + SET_VECTOR_ELT(r_result, 2, map21); + SET_STRING_ELT(r_names, 0, CREATE_STRING_VECTOR("iso")); + SET_STRING_ELT(r_names, 1, CREATE_STRING_VECTOR("map12")); + SET_STRING_ELT(r_names, 2, CREATE_STRING_VECTOR("map21")); + SET_NAMES(r_result, r_names); + UNPROTECT(4); + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_count_subisomorphisms_vf2 / +/-------------------------------------------*/ +SEXP R_igraph_count_subisomorphisms_vf2(SEXP graph1, SEXP graph2, SEXP vertex_color1, SEXP vertex_color2, SEXP edge_color1, SEXP edge_color2) { + /* Declarations */ + igraph_t c_graph1; + igraph_t c_graph2; + igraph_vector_int_t c_vertex_color1; + igraph_vector_int_t c_vertex_color2; + igraph_vector_int_t c_edge_color1; + igraph_vector_int_t c_edge_color2; + igraph_integer_t c_count; + + + + SEXP count; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph1, &c_graph1); + R_SEXP_to_igraph(graph2, &c_graph2); + if (!isNull(vertex_color1)) { R_SEXP_to_vector_int(vertex_color1, &c_vertex_color1); } + if (!isNull(vertex_color2)) { R_SEXP_to_vector_int(vertex_color2, &c_vertex_color2); } + if (!isNull(edge_color1)) { R_SEXP_to_vector_int(edge_color1, &c_edge_color1); } + if (!isNull(edge_color2)) { R_SEXP_to_vector_int(edge_color2, &c_edge_color2); } + c_count=0; + /* Call igraph */ + igraph_count_subisomorphisms_vf2(&c_graph1, &c_graph2, (isNull(vertex_color1) ? 0 : &c_vertex_color1), (isNull(vertex_color2) ? 0 : &c_vertex_color2), (isNull(edge_color1) ? 0 : &c_edge_color1), (isNull(edge_color2) ? 0 : &c_edge_color2), &c_count, 0, 0, 0); + + /* Convert output */ + PROTECT(count=NEW_INTEGER(1)); + INTEGER(count)[0]=c_count; + r_result = count; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_get_subisomorphisms_vf2 / +/-------------------------------------------*/ +SEXP R_igraph_get_subisomorphisms_vf2(SEXP graph1, SEXP graph2, SEXP vertex_color1, SEXP vertex_color2, SEXP edge_color1, SEXP edge_color2) { + /* Declarations */ + igraph_t c_graph1; + igraph_t c_graph2; + igraph_vector_int_t c_vertex_color1; + igraph_vector_int_t c_vertex_color2; + igraph_vector_int_t c_edge_color1; + igraph_vector_int_t c_edge_color2; + igraph_vector_ptr_t c_maps; + + + + SEXP maps; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph1, &c_graph1); + R_SEXP_to_igraph(graph2, &c_graph2); + if (!isNull(vertex_color1)) { R_SEXP_to_vector_int(vertex_color1, &c_vertex_color1); } + if (!isNull(vertex_color2)) { R_SEXP_to_vector_int(vertex_color2, &c_vertex_color2); } + if (!isNull(edge_color1)) { R_SEXP_to_vector_int(edge_color1, &c_edge_color1); } + if (!isNull(edge_color2)) { R_SEXP_to_vector_int(edge_color2, &c_edge_color2); } + if (0 != igraph_vector_ptr_init(&c_maps, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(R_igraph_vectorlist_destroy, &c_maps); + /* Call igraph */ + igraph_get_subisomorphisms_vf2(&c_graph1, &c_graph2, (isNull(vertex_color1) ? 0 : &c_vertex_color1), (isNull(vertex_color2) ? 0 : &c_vertex_color2), (isNull(edge_color1) ? 0 : &c_edge_color1), (isNull(edge_color2) ? 0 : &c_edge_color2), &c_maps, 0, 0, 0); + + /* Convert output */ + PROTECT(maps=R_igraph_vectorlist_to_SEXP(&c_maps)); + R_igraph_vectorlist_destroy(&c_maps); + IGRAPH_FINALLY_CLEAN(1); + r_result = maps; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_isomorphic_34 / +/-------------------------------------------*/ +SEXP R_igraph_isomorphic_34(SEXP graph1, SEXP graph2) { + /* Declarations */ + igraph_t c_graph1; + igraph_t c_graph2; + igraph_bool_t c_iso; + SEXP iso; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph1, &c_graph1); + R_SEXP_to_igraph(graph2, &c_graph2); + /* Call igraph */ + igraph_isomorphic_34(&c_graph1, &c_graph2, &c_iso); + + /* Convert output */ + PROTECT(iso=NEW_LOGICAL(1)); + LOGICAL(iso)[0]=c_iso; + r_result = iso; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_canonical_permutation / +/-------------------------------------------*/ +SEXP R_igraph_canonical_permutation(SEXP graph, SEXP colors, SEXP sh) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_int_t c_colors; + igraph_vector_t c_labeling; + igraph_bliss_sh_t c_sh; + igraph_bliss_info_t c_info; + SEXP labeling; + SEXP info; + + SEXP r_result, r_names; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (!isNull(colors)) { R_SEXP_to_vector_int(colors, &c_colors); } + if (0 != igraph_vector_init(&c_labeling, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_labeling); + c_sh = (igraph_bliss_sh_t) Rf_asInteger(sh); + /* Call igraph */ + igraph_canonical_permutation(&c_graph, (isNull(colors) ? 0 : &c_colors), &c_labeling, c_sh, &c_info); + + /* Convert output */ + PROTECT(r_result=NEW_LIST(2)); + PROTECT(r_names=NEW_CHARACTER(2)); + PROTECT(labeling=R_igraph_vector_to_SEXPp1(&c_labeling)); + igraph_vector_destroy(&c_labeling); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(info=R_igraph_bliss_info_to_SEXP(&c_info)); + if (c_info.group_size) { free(c_info.group_size); } + SET_VECTOR_ELT(r_result, 0, labeling); + SET_VECTOR_ELT(r_result, 1, info); + SET_STRING_ELT(r_names, 0, CREATE_STRING_VECTOR("labeling")); + SET_STRING_ELT(r_names, 1, CREATE_STRING_VECTOR("info")); + SET_NAMES(r_result, r_names); + UNPROTECT(3); + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_permute_vertices / +/-------------------------------------------*/ +SEXP R_igraph_permute_vertices(SEXP graph, SEXP permutation) { + /* Declarations */ + igraph_t c_graph; + igraph_t c_res; + igraph_vector_t c_permutation; + SEXP res; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + R_SEXP_to_vector(permutation, &c_permutation); + /* Call igraph */ + igraph_permute_vertices(&c_graph, &c_res, &c_permutation); + + /* Convert output */ + IGRAPH_FINALLY(igraph_destroy, &c_res); + PROTECT(res=R_igraph_to_SEXP(&c_res)); + igraph_destroy(&c_res); + IGRAPH_FINALLY_CLEAN(1); + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_isomorphic_bliss / +/-------------------------------------------*/ +SEXP R_igraph_isomorphic_bliss(SEXP graph1, SEXP graph2, SEXP colors1, SEXP colors2, SEXP sh) { + /* Declarations */ + igraph_t c_graph1; + igraph_t c_graph2; + igraph_vector_int_t c_colors1; + igraph_vector_int_t c_colors2; + igraph_bool_t c_iso; + igraph_vector_t c_map12; + igraph_vector_t c_map21; + igraph_bliss_sh_t c_sh; + igraph_bliss_info_t c_info1; + igraph_bliss_info_t c_info2; + SEXP iso; + SEXP map12; + SEXP map21; + SEXP info1; + SEXP info2; + + SEXP r_result, r_names; + /* Convert input */ + R_SEXP_to_igraph(graph1, &c_graph1); + R_SEXP_to_igraph(graph2, &c_graph2); + if (!isNull(colors1)) { R_SEXP_to_vector_int(colors1, &c_colors1); } + if (!isNull(colors2)) { R_SEXP_to_vector_int(colors2, &c_colors2); } + if (0 != igraph_vector_init(&c_map12, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_map12); + map12=R_GlobalEnv; /* hack to have a non-NULL value */ + if (0 != igraph_vector_init(&c_map21, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_map21); + map21=R_GlobalEnv; /* hack to have a non-NULL value */ + c_sh = (igraph_bliss_sh_t) Rf_asInteger(sh); + /* Call igraph */ + igraph_isomorphic_bliss(&c_graph1, &c_graph2, (isNull(colors1) ? 0 : &c_colors1), (isNull(colors2) ? 0 : &c_colors2), &c_iso, (isNull(map12) ? 0 : &c_map12), (isNull(map21) ? 0 : &c_map21), c_sh, &c_info1, &c_info2); + + /* Convert output */ + PROTECT(r_result=NEW_LIST(5)); + PROTECT(r_names=NEW_CHARACTER(5)); + PROTECT(iso=NEW_LOGICAL(1)); + LOGICAL(iso)[0]=c_iso; + PROTECT(map12=R_igraph_0orvector_to_SEXPp1(&c_map12)); + igraph_vector_destroy(&c_map12); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(map21=R_igraph_0orvector_to_SEXPp1(&c_map21)); + igraph_vector_destroy(&c_map21); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(info1=R_igraph_bliss_info_to_SEXP(&c_info1)); + if (c_info1.group_size) { free(c_info1.group_size); } + PROTECT(info2=R_igraph_bliss_info_to_SEXP(&c_info2)); + if (c_info2.group_size) { free(c_info2.group_size); } + SET_VECTOR_ELT(r_result, 0, iso); + SET_VECTOR_ELT(r_result, 1, map12); + SET_VECTOR_ELT(r_result, 2, map21); + SET_VECTOR_ELT(r_result, 3, info1); + SET_VECTOR_ELT(r_result, 4, info2); + SET_STRING_ELT(r_names, 0, CREATE_STRING_VECTOR("iso")); + SET_STRING_ELT(r_names, 1, CREATE_STRING_VECTOR("map12")); + SET_STRING_ELT(r_names, 2, CREATE_STRING_VECTOR("map21")); + SET_STRING_ELT(r_names, 3, CREATE_STRING_VECTOR("info1")); + SET_STRING_ELT(r_names, 4, CREATE_STRING_VECTOR("info2")); + SET_NAMES(r_result, r_names); + UNPROTECT(6); + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_automorphisms / +/-------------------------------------------*/ +SEXP R_igraph_automorphisms(SEXP graph, SEXP colors, SEXP sh) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_int_t c_colors; + igraph_bliss_sh_t c_sh; + igraph_bliss_info_t c_info; + SEXP info; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (!isNull(colors)) { R_SEXP_to_vector_int(colors, &c_colors); } + c_sh = (igraph_bliss_sh_t) Rf_asInteger(sh); + /* Call igraph */ + igraph_automorphisms(&c_graph, (isNull(colors) ? 0 : &c_colors), c_sh, &c_info); + + /* Convert output */ + PROTECT(info=R_igraph_bliss_info_to_SEXP(&c_info)); + if (c_info.group_size) { free(c_info.group_size); } + r_result = info; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_automorphism_group / +/-------------------------------------------*/ +SEXP R_igraph_automorphism_group(SEXP graph, SEXP colors, SEXP sh) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_int_t c_colors; + igraph_vector_ptr_t c_generators; + igraph_bliss_sh_t c_sh; + igraph_bliss_info_t c_info; + SEXP generators; + SEXP info; + + SEXP r_result, r_names; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (!isNull(colors)) { R_SEXP_to_vector_int(colors, &c_colors); } + if (0 != igraph_vector_ptr_init(&c_generators, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(R_igraph_vectorlist_destroy, &c_generators); + c_sh = (igraph_bliss_sh_t) Rf_asInteger(sh); + /* Call igraph */ + igraph_automorphism_group(&c_graph, (isNull(colors) ? 0 : &c_colors), &c_generators, c_sh, &c_info); + + /* Convert output */ + PROTECT(r_result=NEW_LIST(2)); + PROTECT(r_names=NEW_CHARACTER(2)); + PROTECT(generators=R_igraph_vectorlist_to_SEXP_p1(&c_generators)); + R_igraph_vectorlist_destroy(&c_generators); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(info=R_igraph_bliss_info_to_SEXP(&c_info)); + if (c_info.group_size) { free(c_info.group_size); } + SET_VECTOR_ELT(r_result, 0, generators); + SET_VECTOR_ELT(r_result, 1, info); + SET_STRING_ELT(r_names, 0, CREATE_STRING_VECTOR("generators")); + SET_STRING_ELT(r_names, 1, CREATE_STRING_VECTOR("info")); + SET_NAMES(r_result, r_names); + UNPROTECT(3); + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_simplify_and_colorize / +/-------------------------------------------*/ +SEXP R_igraph_simplify_and_colorize(SEXP graph) { + /* Declarations */ + igraph_t c_graph; + igraph_t c_res; + igraph_vector_int_t c_vertex_color; + igraph_vector_int_t c_edge_color; + SEXP res; + SEXP vertex_color; + SEXP edge_color; + + SEXP r_result, r_names; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != igraph_vector_int_init(&c_vertex_color, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_int_destroy, &c_vertex_color); + if (0 != igraph_vector_int_init(&c_edge_color, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_int_destroy, &c_edge_color); + /* Call igraph */ + igraph_simplify_and_colorize(&c_graph, &c_res, &c_vertex_color, &c_edge_color); + + /* Convert output */ + PROTECT(r_result=NEW_LIST(3)); + PROTECT(r_names=NEW_CHARACTER(3)); + IGRAPH_FINALLY(igraph_destroy, &c_res); + PROTECT(res=R_igraph_to_SEXP(&c_res)); + igraph_destroy(&c_res); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(vertex_color=R_igraph_vector_int_to_SEXP(&c_vertex_color)); + igraph_vector_int_destroy(&c_vertex_color); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(edge_color=R_igraph_vector_int_to_SEXP(&c_edge_color)); + igraph_vector_int_destroy(&c_edge_color); + IGRAPH_FINALLY_CLEAN(1); + SET_VECTOR_ELT(r_result, 0, res); + SET_VECTOR_ELT(r_result, 1, vertex_color); + SET_VECTOR_ELT(r_result, 2, edge_color); + SET_STRING_ELT(r_names, 0, CREATE_STRING_VECTOR("res")); + SET_STRING_ELT(r_names, 1, CREATE_STRING_VECTOR("vertex_color")); + SET_STRING_ELT(r_names, 2, CREATE_STRING_VECTOR("edge_color")); + SET_NAMES(r_result, r_names); + UNPROTECT(4); + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_scg_grouping / +/-------------------------------------------*/ +SEXP R_igraph_scg_grouping(SEXP V, SEXP nt, SEXP nt_vec, SEXP mtype, SEXP algo, SEXP p, SEXP maxiter) { + /* Declarations */ + igraph_matrix_t c_V; + igraph_vector_t c_groups; + igraph_integer_t c_nt; + igraph_vector_t c_nt_vec; + igraph_scg_matrix_t c_mtype; + igraph_scg_algorithm_t c_algo; + igraph_vector_t c_p; + igraph_integer_t c_maxiter; + SEXP groups; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_matrix(V, &c_V); + if (0 != igraph_vector_init(&c_groups, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_groups); + c_nt=INTEGER(nt)[0]; + if (!isNull(nt_vec)) { R_SEXP_to_vector(nt_vec, &c_nt_vec); } + c_mtype = (igraph_scg_matrix_t) Rf_asInteger(mtype); + c_algo = (igraph_scg_algorithm_t) Rf_asInteger(algo); + if (!isNull(p)) { R_SEXP_to_vector(p, &c_p); } + c_maxiter=INTEGER(maxiter)[0]; + /* Call igraph */ + igraph_scg_grouping(&c_V, &c_groups, c_nt, (isNull(nt_vec) ? 0 : &c_nt_vec), c_mtype, c_algo, (isNull(p) ? 0 : &c_p), c_maxiter); + + /* Convert output */ + PROTECT(groups=R_igraph_vector_to_SEXPp1(&c_groups)); + igraph_vector_destroy(&c_groups); + IGRAPH_FINALLY_CLEAN(1); + r_result = groups; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_scg_norm_eps / +/-------------------------------------------*/ +SEXP R_igraph_scg_norm_eps(SEXP V, SEXP groups, SEXP mtype, SEXP p, SEXP norm) { + /* Declarations */ + igraph_matrix_t c_V; + igraph_vector_t c_groups; + igraph_vector_t c_eps; + igraph_scg_matrix_t c_mtype; + igraph_vector_t c_p; + igraph_scg_norm_t c_norm; + SEXP eps; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_matrix(V, &c_V); + R_SEXP_to_vector(groups, &c_groups); + if (0 != igraph_vector_init(&c_eps, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_eps); + c_mtype = (igraph_scg_matrix_t) Rf_asInteger(mtype); + if (!isNull(p)) { R_SEXP_to_vector(p, &c_p); } + c_norm = (igraph_scg_norm_t) Rf_asInteger(norm); + /* Call igraph */ + igraph_scg_norm_eps(&c_V, &c_groups, &c_eps, c_mtype, (isNull(p) ? 0 : &c_p), c_norm); + + /* Convert output */ + PROTECT(eps=R_igraph_vector_to_SEXP(&c_eps)); + igraph_vector_destroy(&c_eps); + IGRAPH_FINALLY_CLEAN(1); + r_result = eps; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_is_matching / +/-------------------------------------------*/ +SEXP R_igraph_is_matching(SEXP graph, SEXP types, SEXP matching) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_bool_t c_types; + igraph_vector_long_t c_matching; + igraph_bool_t c_res; + SEXP res; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (!isNull(types)) { R_SEXP_to_vector_bool(types, &c_types); } + R_SEXP_to_vector_long_copy(matching, &c_matching); + /* Call igraph */ + igraph_is_matching(&c_graph, (isNull(types) ? 0 : &c_types), &c_matching, &c_res); + + /* Convert output */ + igraph_vector_long_destroy(&c_matching); + PROTECT(res=NEW_LOGICAL(1)); + LOGICAL(res)[0]=c_res; + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_is_maximal_matching / +/-------------------------------------------*/ +SEXP R_igraph_is_maximal_matching(SEXP graph, SEXP types, SEXP matching) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_bool_t c_types; + igraph_vector_long_t c_matching; + igraph_bool_t c_res; + SEXP res; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (!isNull(types)) { R_SEXP_to_vector_bool(types, &c_types); } + R_SEXP_to_vector_long_copy(matching, &c_matching); + /* Call igraph */ + igraph_is_maximal_matching(&c_graph, (isNull(types) ? 0 : &c_types), &c_matching, &c_res); + + /* Convert output */ + igraph_vector_long_destroy(&c_matching); + PROTECT(res=NEW_LOGICAL(1)); + LOGICAL(res)[0]=c_res; + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_maximum_bipartite_matching / +/-------------------------------------------*/ +SEXP R_igraph_maximum_bipartite_matching(SEXP graph, SEXP types, SEXP weights, SEXP eps) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_bool_t c_types; + igraph_integer_t c_matching_size; + igraph_real_t c_matching_weight; + igraph_vector_long_t c_matching; + igraph_vector_t c_weights; + igraph_real_t c_eps; + SEXP matching_size; + SEXP matching_weight; + SEXP matching; + + SEXP r_result, r_names; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (!isNull(types)) { R_SEXP_to_vector_bool(types, &c_types); } + c_matching_size=0; + if (0 != igraph_vector_long_init(&c_matching, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_long_destroy, &c_matching); + if (!isNull(weights)) { R_SEXP_to_vector(weights, &c_weights); } + c_eps=REAL(eps)[0]; + /* Call igraph */ + igraph_maximum_bipartite_matching(&c_graph, (isNull(types) ? 0 : &c_types), &c_matching_size, &c_matching_weight, &c_matching, (isNull(weights) ? 0 : &c_weights), c_eps); + + /* Convert output */ + PROTECT(r_result=NEW_LIST(3)); + PROTECT(r_names=NEW_CHARACTER(3)); + PROTECT(matching_size=NEW_INTEGER(1)); + INTEGER(matching_size)[0]=c_matching_size; + PROTECT(matching_weight=NEW_NUMERIC(1)); + REAL(matching_weight)[0]=c_matching_weight; + PROTECT(matching=R_igraph_vector_long_to_SEXPp1(&c_matching)); + igraph_vector_long_destroy(&c_matching); + IGRAPH_FINALLY_CLEAN(1); + SET_VECTOR_ELT(r_result, 0, matching_size); + SET_VECTOR_ELT(r_result, 1, matching_weight); + SET_VECTOR_ELT(r_result, 2, matching); + SET_STRING_ELT(r_names, 0, CREATE_STRING_VECTOR("matching_size")); + SET_STRING_ELT(r_names, 1, CREATE_STRING_VECTOR("matching_weight")); + SET_STRING_ELT(r_names, 2, CREATE_STRING_VECTOR("matching")); + SET_NAMES(r_result, r_names); + UNPROTECT(4); + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_eigen_adjacency / +/-------------------------------------------*/ +SEXP R_igraph_eigen_adjacency(SEXP graph, SEXP algorithm, SEXP which, SEXP options) { + /* Declarations */ + igraph_t c_graph; + igraph_eigen_algorithm_t c_algorithm; + igraph_eigen_which_t c_which; + igraph_arpack_options_t c_options; + + igraph_vector_t c_values; + igraph_matrix_t c_vectors; + igraph_vector_complex_t c_cmplxvalues; + igraph_matrix_complex_t c_cmplxvectors; + SEXP values; + SEXP vectors; + SEXP cmplxvalues; + SEXP cmplxvectors; + + SEXP r_result, r_names; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + c_algorithm=REAL(algorithm)[0]; + R_SEXP_to_igraph_eigen_which(which, &c_which); + R_SEXP_to_igraph_arpack_options(options, &c_options); + if (0 != igraph_vector_init(&c_values, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_values); + if (0 != igraph_matrix_init(&c_vectors, 0, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_matrix_destroy, &c_vectors); + if (0 != igraph_vector_complex_init(&c_cmplxvalues, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_complex_destroy, &c_cmplxvalues); + cmplxvalues=R_GlobalEnv; /* hack to have a non-NULL value */ + if (0 != igraph_matrix_complex_init(&c_cmplxvectors, 0, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_matrix_complex_destroy, &c_cmplxvectors); + cmplxvectors=R_GlobalEnv; /* hack to have a non-NULL value */ + /* Call igraph */ + igraph_eigen_adjacency(&c_graph, c_algorithm, &c_which, &c_options, 0, &c_values, &c_vectors, (isNull(cmplxvalues) ? 0 : &c_cmplxvalues), (isNull(cmplxvectors) ? 0 : &c_cmplxvectors)); + + /* Convert output */ + PROTECT(r_result=NEW_LIST(5)); + PROTECT(r_names=NEW_CHARACTER(5)); + PROTECT(options=R_igraph_arpack_options_to_SEXP(&c_options)); + PROTECT(values=R_igraph_vector_to_SEXP(&c_values)); + igraph_vector_destroy(&c_values); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(vectors=R_igraph_matrix_to_SEXP(&c_vectors)); + igraph_matrix_destroy(&c_vectors); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(cmplxvalues=R_igraph_0orvector_complex_to_SEXP(&c_cmplxvalues)); + igraph_vector_complex_destroy(&c_cmplxvalues); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(cmplxvectors=R_igraph_0ormatrix_complex_to_SEXP(&c_cmplxvectors)); + igraph_matrix_complex_destroy(&c_cmplxvectors); + IGRAPH_FINALLY_CLEAN(1); + SET_VECTOR_ELT(r_result, 0, options); + SET_VECTOR_ELT(r_result, 1, values); + SET_VECTOR_ELT(r_result, 2, vectors); + SET_VECTOR_ELT(r_result, 3, cmplxvalues); + SET_VECTOR_ELT(r_result, 4, cmplxvectors); + SET_STRING_ELT(r_names, 0, CREATE_STRING_VECTOR("options")); + SET_STRING_ELT(r_names, 1, CREATE_STRING_VECTOR("values")); + SET_STRING_ELT(r_names, 2, CREATE_STRING_VECTOR("vectors")); + SET_STRING_ELT(r_names, 3, CREATE_STRING_VECTOR("cmplxvalues")); + SET_STRING_ELT(r_names, 4, CREATE_STRING_VECTOR("cmplxvectors")); + SET_NAMES(r_result, r_names); + UNPROTECT(6); + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_power_law_fit / +/-------------------------------------------*/ +SEXP R_igraph_power_law_fit(SEXP data, SEXP xmin, SEXP force_continuous) { + /* Declarations */ + igraph_vector_t c_data; + igraph_plfit_result_t c_res; + igraph_real_t c_xmin; + igraph_bool_t c_force_continuous; + SEXP res; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_vector(data, &c_data); + c_xmin=REAL(xmin)[0]; + c_force_continuous=LOGICAL(force_continuous)[0]; + /* Call igraph */ + igraph_power_law_fit(&c_data, &c_res, c_xmin, c_force_continuous); + + /* Convert output */ + PROTECT(res=R_igraph_plfit_result_to_SEXP(&c_res)); + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_sir / +/-------------------------------------------*/ +SEXP R_igraph_sir(SEXP graph, SEXP beta, SEXP gamma, SEXP no_sim) { + /* Declarations */ + igraph_t c_graph; + igraph_real_t c_beta; + igraph_real_t c_gamma; + igraph_integer_t c_no_sim; + igraph_vector_ptr_t c_res; + SEXP res; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + c_beta=REAL(beta)[0]; + c_gamma=REAL(gamma)[0]; + c_no_sim=INTEGER(no_sim)[0]; + if (0 != igraph_vector_ptr_init(&c_res, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(R_igraph_sirlist_destroy, &c_res); + /* Call igraph */ + igraph_sir(&c_graph, c_beta, c_gamma, c_no_sim, &c_res); + + /* Convert output */ + PROTECT(res=R_igraph_sirlist_to_SEXP(&c_res)); + R_igraph_sirlist_destroy(&c_res); + IGRAPH_FINALLY_CLEAN(1); + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_convex_hull / +/-------------------------------------------*/ +SEXP R_igraph_convex_hull(SEXP data) { + /* Declarations */ + igraph_matrix_t c_data; + igraph_vector_t c_resverts; + igraph_matrix_t c_rescoords; + SEXP resverts; + SEXP rescoords; + + SEXP r_result, r_names; + /* Convert input */ + R_SEXP_to_matrix(data, &c_data); + if (0 != igraph_vector_init(&c_resverts, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_resverts); + if (0 != igraph_matrix_init(&c_rescoords, 0, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_matrix_destroy, &c_rescoords); + /* Call igraph */ + igraph_convex_hull(&c_data, &c_resverts, &c_rescoords); + + /* Convert output */ + PROTECT(r_result=NEW_LIST(2)); + PROTECT(r_names=NEW_CHARACTER(2)); + PROTECT(resverts=R_igraph_vector_to_SEXP(&c_resverts)); + igraph_vector_destroy(&c_resverts); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(rescoords=R_igraph_matrix_to_SEXP(&c_rescoords)); + igraph_matrix_destroy(&c_rescoords); + IGRAPH_FINALLY_CLEAN(1); + SET_VECTOR_ELT(r_result, 0, resverts); + SET_VECTOR_ELT(r_result, 1, rescoords); + SET_STRING_ELT(r_names, 0, CREATE_STRING_VECTOR("resverts")); + SET_STRING_ELT(r_names, 1, CREATE_STRING_VECTOR("rescoords")); + SET_NAMES(r_result, r_names); + UNPROTECT(3); + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_dim_select / +/-------------------------------------------*/ +SEXP R_igraph_dim_select(SEXP sv) { + /* Declarations */ + igraph_vector_t c_sv; + igraph_integer_t c_dim; + SEXP dim; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_vector(sv, &c_sv); + c_dim=0; + /* Call igraph */ + igraph_dim_select(&c_sv, &c_dim); + + /* Convert output */ + PROTECT(dim=NEW_INTEGER(1)); + INTEGER(dim)[0]=c_dim; + r_result = dim; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_is_eulerian / +/-------------------------------------------*/ +SEXP R_igraph_is_eulerian(SEXP graph) { + /* Declarations */ + igraph_t c_graph; + igraph_bool_t c_has_path; + igraph_bool_t c_has_cycle; + SEXP has_path; + SEXP has_cycle; + + SEXP r_result, r_names; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + /* Call igraph */ + igraph_is_eulerian(&c_graph, &c_has_path, &c_has_cycle); + + /* Convert output */ + PROTECT(r_result=NEW_LIST(2)); + PROTECT(r_names=NEW_CHARACTER(2)); + PROTECT(has_path=NEW_LOGICAL(1)); + LOGICAL(has_path)[0]=c_has_path; + PROTECT(has_cycle=NEW_LOGICAL(1)); + LOGICAL(has_cycle)[0]=c_has_cycle; + SET_VECTOR_ELT(r_result, 0, has_path); + SET_VECTOR_ELT(r_result, 1, has_cycle); + SET_STRING_ELT(r_names, 0, CREATE_STRING_VECTOR("has_path")); + SET_STRING_ELT(r_names, 1, CREATE_STRING_VECTOR("has_cycle")); + SET_NAMES(r_result, r_names); + UNPROTECT(3); + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_eulerian_path / +/-------------------------------------------*/ +SEXP R_igraph_eulerian_path(SEXP graph) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_t c_edge_res; + igraph_vector_t c_vertex_res; + SEXP edge_res; + SEXP vertex_res; + + SEXP r_result, r_names; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != igraph_vector_init(&c_edge_res, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_edge_res); + if (0 != igraph_vector_init(&c_vertex_res, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_vertex_res); + /* Call igraph */ + igraph_eulerian_path(&c_graph, &c_edge_res, &c_vertex_res); + + /* Convert output */ + PROTECT(r_result=NEW_LIST(2)); + PROTECT(r_names=NEW_CHARACTER(2)); + PROTECT(edge_res=R_igraph_vector_to_SEXPp1(&c_edge_res)); + igraph_vector_destroy(&c_edge_res); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(vertex_res=R_igraph_vector_to_SEXPp1(&c_vertex_res)); + igraph_vector_destroy(&c_vertex_res); + IGRAPH_FINALLY_CLEAN(1); + SET_VECTOR_ELT(r_result, 0, edge_res); + SET_VECTOR_ELT(r_result, 1, vertex_res); + SET_STRING_ELT(r_names, 0, CREATE_STRING_VECTOR("epath")); + SET_STRING_ELT(r_names, 1, CREATE_STRING_VECTOR("vpath")); + SET_NAMES(r_result, r_names); + UNPROTECT(3); + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_eulerian_cycle / +/-------------------------------------------*/ +SEXP R_igraph_eulerian_cycle(SEXP graph) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_t c_edge_res; + igraph_vector_t c_vertex_res; + SEXP edge_res; + SEXP vertex_res; + + SEXP r_result, r_names; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != igraph_vector_init(&c_edge_res, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_edge_res); + if (0 != igraph_vector_init(&c_vertex_res, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_vertex_res); + /* Call igraph */ + igraph_eulerian_cycle(&c_graph, &c_edge_res, &c_vertex_res); + + /* Convert output */ + PROTECT(r_result=NEW_LIST(2)); + PROTECT(r_names=NEW_CHARACTER(2)); + PROTECT(edge_res=R_igraph_vector_to_SEXPp1(&c_edge_res)); + igraph_vector_destroy(&c_edge_res); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(vertex_res=R_igraph_vector_to_SEXPp1(&c_vertex_res)); + igraph_vector_destroy(&c_vertex_res); + IGRAPH_FINALLY_CLEAN(1); + SET_VECTOR_ELT(r_result, 0, edge_res); + SET_VECTOR_ELT(r_result, 1, vertex_res); + SET_STRING_ELT(r_names, 0, CREATE_STRING_VECTOR("epath")); + SET_STRING_ELT(r_names, 1, CREATE_STRING_VECTOR("vpath")); + SET_NAMES(r_result, r_names); + UNPROTECT(3); + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_is_tree / +/-------------------------------------------*/ +SEXP R_igraph_is_tree(SEXP graph, SEXP mode) { + /* Declarations */ + igraph_t c_graph; + igraph_bool_t c_res; + igraph_integer_t c_root; + igraph_neimode_t c_mode; + SEXP res; + SEXP root; + + SEXP r_result, r_names; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + c_root = -1; + c_mode = (igraph_neimode_t) Rf_asInteger(mode); + /* Call igraph */ + igraph_is_tree(&c_graph, &c_res, &c_root, c_mode); + + /* Convert output */ + PROTECT(r_result=NEW_LIST(2)); + PROTECT(r_names=NEW_CHARACTER(2)); + PROTECT(res=NEW_LOGICAL(1)); + LOGICAL(res)[0]=c_res; + PROTECT(root = NEW_INTEGER(1)); + INTEGER(root)[0] = c_root + 1; + SET_VECTOR_ELT(r_result, 0, res); + SET_VECTOR_ELT(r_result, 1, root); + SET_STRING_ELT(r_names, 0, CREATE_STRING_VECTOR("res")); + SET_STRING_ELT(r_names, 1, CREATE_STRING_VECTOR("root")); + SET_NAMES(r_result, r_names); + UNPROTECT(3); + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_from_prufer / +/-------------------------------------------*/ +SEXP R_igraph_from_prufer(SEXP prufer) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_int_t c_prufer; + SEXP graph; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_vector_int(prufer, &c_prufer); + /* Call igraph */ + igraph_from_prufer(&c_graph, &c_prufer); + + /* Convert output */ + IGRAPH_FINALLY(igraph_destroy, &c_graph); + PROTECT(graph=R_igraph_to_SEXP(&c_graph)); + igraph_destroy(&c_graph); + IGRAPH_FINALLY_CLEAN(1); + r_result = graph; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_to_prufer / +/-------------------------------------------*/ +SEXP R_igraph_to_prufer(SEXP graph) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_int_t c_prufer; + SEXP prufer; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != igraph_vector_int_init(&c_prufer, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_int_destroy, &c_prufer); + /* Call igraph */ + igraph_to_prufer(&c_graph, &c_prufer); + + /* Convert output */ + PROTECT(prufer=R_igraph_vector_int_to_SEXP(&c_prufer)); + igraph_vector_int_destroy(&c_prufer); + IGRAPH_FINALLY_CLEAN(1); + r_result = prufer; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_random_spanning_tree / +/-------------------------------------------*/ +SEXP R_igraph_random_spanning_tree(SEXP graph, SEXP vid) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_t c_res; + igraph_integer_t c_vid; + SEXP res; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != igraph_vector_init(&c_res, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_res); + c_vid = (igraph_integer_t) REAL(vid)[0]; + /* Call igraph */ + igraph_random_spanning_tree(&c_graph, &c_res, c_vid); + + /* Convert output */ + PROTECT(res=R_igraph_vector_to_SEXPp1(&c_res)); + igraph_vector_destroy(&c_res); + IGRAPH_FINALLY_CLEAN(1); + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_tree_game / +/-------------------------------------------*/ +SEXP R_igraph_tree_game(SEXP n, SEXP directed, SEXP method) { + /* Declarations */ + igraph_t c_graph; + igraph_integer_t c_n; + igraph_bool_t c_directed; + igraph_random_tree_t c_method; + SEXP graph; + + SEXP r_result; + /* Convert input */ + c_n=INTEGER(n)[0]; + c_directed=LOGICAL(directed)[0]; + c_method = (igraph_random_tree_t) Rf_asInteger(method); + /* Call igraph */ + igraph_tree_game(&c_graph, c_n, c_directed, c_method); + + /* Convert output */ + IGRAPH_FINALLY(igraph_destroy, &c_graph); + PROTECT(graph=R_igraph_to_SEXP(&c_graph)); + igraph_destroy(&c_graph); + IGRAPH_FINALLY_CLEAN(1); + r_result = graph; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_vertex_coloring_greedy / +/-------------------------------------------*/ +SEXP R_igraph_vertex_coloring_greedy(SEXP graph, SEXP heuristic) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_int_t c_colors; + igraph_coloring_greedy_t c_heuristic; + SEXP colors; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != igraph_vector_int_init(&c_colors, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_int_destroy, &c_colors); + c_heuristic = (igraph_coloring_greedy_t) Rf_asInteger(heuristic); + /* Call igraph */ + igraph_vertex_coloring_greedy(&c_graph, &c_colors, c_heuristic); + + /* Convert output */ + PROTECT(colors=R_igraph_vector_int_to_SEXP(&c_colors)); + igraph_vector_int_destroy(&c_colors); + IGRAPH_FINALLY_CLEAN(1); + r_result = colors; + + UNPROTECT(1); + return(r_result); +} diff --git a/src/rigraph/rinterface.h b/src/rigraph/rinterface.h new file mode 100644 index 0000000..e890f09 --- /dev/null +++ b/src/rigraph/rinterface.h @@ -0,0 +1,29 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2010 Gabor Csardi + 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 "uuid/uuid.h" + +#define R_IGRAPH_TYPE_VERSION "0.8.0" +#define R_IGRAPH_VERSION_VAR ".__igraph_version__." + +SEXP R_igraph_add_env(SEXP graph); diff --git a/src/rigraph/rinterface_extra.c b/src/rigraph/rinterface_extra.c new file mode 100644 index 0000000..0b5e980 --- /dev/null +++ b/src/rigraph/rinterface_extra.c @@ -0,0 +1,334 @@ +/* -*- mode: C -*- */ +/* + IGraph library R interface. + Copyright (C) 2013 Gabor Csardi + 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.h" + +#include +#include +#include + +#include "rinterface.h" + +#include + +/* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++C */ +/* C */ +/* Given a HIERARCHIC CLUSTERING, described as a sequence of C */ +/* agglomerations, prepare the seq. of aggloms. and "horiz." C */ +/* order of objects for plotting the dendrogram using S routine C */ +/* 'plclust'. C */ +/* C */ +/* Parameters: C */ +/* C */ +/* IA, IB: vectors of dimension N defining the agglomer- C */ +/* ations. C */ +/* IIA, IIB: used to store IA and IB values differently C */ +/* (in form needed for S command 'plclust' C */ +/* IORDER: "horiz." order of objects for dendrogram C */ +/* C */ +/* F. Murtagh, ESA/ESO/STECF, Garching, June 1991 C */ +/* C */ +/* HISTORY C */ +/* C */ +/* Adapted from routine HCASS, which additionally determines C */ +/* cluster assignments at all levels, at extra comput. expense C */ +/* C */ +/* ---------------------------------------------------------------C */ + +int igraphhcass2(int *n, int *ia, int *ib, + int *iorder, int *iia, + int *iib) { + + /* System generated locals */ + int i__1, i__2, i__3; + + /* Local variables */ + static int i__, j, k, k1, k2, loc; + + /* Args */ + /* Var */ + + /* Following bit is to get seq. of merges into format acceptable to + plclust I coded clusters as lowest seq. no. of constituents; S's + 'hclust' codes singletons as -ve numbers, and non-singletons with + their seq. nos. */ + + /* Parameter adjustments */ + --iib; + --iia; + --iorder; + --ib; + --ia; + + /* Function Body */ + i__1 = *n; + for (i__ = 1; i__ <= i__1; ++i__) { + iia[i__] = ia[i__]; + iib[i__] = ib[i__]; + } + i__1 = *n - 2; + for (i__ = 1; i__ <= i__1; ++i__) { + /* In the following, smallest (+ve or -ve) seq. no. wanted */ + /* Computing MIN */ + i__2 = ia[i__], i__3 = ib[i__]; + k = i__2 < i__3 ? i__2 : i__3; + i__2 = *n - 1; + for (j = i__ + 1; j <= i__2; ++j) { + if (ia[j] == k) { + iia[j] = -i__; + } + if (ib[j] == k) { + iib[j] = -i__; + } + } + } + i__1 = *n - 1; + for (i__ = 1; i__ <= i__1; ++i__) { + iia[i__] = -iia[i__]; + iib[i__] = -iib[i__]; + } + i__1 = *n - 1; + for (i__ = 1; i__ <= i__1; ++i__) { + if (iia[i__] > 0 && iib[i__] < 0) { + k = iia[i__]; + iia[i__] = iib[i__]; + iib[i__] = k; + } + if (iia[i__] > 0 && iib[i__] > 0) { + /* Computing MIN */ + i__2 = iia[i__], i__3 = iib[i__]; + k1 = i__2 < i__3 ? i__2 : i__3; + /* Computing MAX */ + i__2 = iia[i__], i__3 = iib[i__]; + k2 = i__2 > i__3 ? i__2 : i__3; + iia[i__] = k1; + iib[i__] = k2; + } + } + + /* NEW PART FOR 'ORDER' */ + + iorder[1] = iia[*n - 1]; + iorder[2] = iib[*n - 1]; + loc = 2; + for (i__ = *n - 2; i__ >= 1; --i__) { + i__1 = loc; + for (j = 1; j <= i__1; ++j) { + if (iorder[j] == i__) { + /* REPLACE IORDER(J) WITH IIA(I) AND IIB(I) */ + iorder[j] = iia[i__]; + if (j == loc) { + ++loc; + iorder[loc] = iib[i__]; + } else { + ++loc; + i__2 = j + 2; + for (k = loc; k >= i__2; --k) { + iorder[k] = iorder[k - 1]; + } + iorder[j + 1] = iib[i__]; + } + goto L171; + } + } + /* SHOULD NEVER REACH HERE */ + L171: + ; + } + + i__1 = *n; + for (i__ = 1; i__ <= i__1; ++i__) { + iorder[i__] = -iorder[i__]; + } + + return 0; +} /* hcass2_ */ + +SEXP R_igraph_psumtree_draw(SEXP plength, SEXP howmany, SEXP prob) { + SEXP result; + int length=INTEGER(plength)[0]; + int i, n=INTEGER(howmany)[0]; + igraph_psumtree_t tree; + igraph_real_t sum; + + PROTECT(result=NEW_INTEGER(n)); + + igraph_psumtree_init(&tree, length); + if (isNull(prob)) { + for (i=0; i + +#include "igraph_random.h" + +#include "igraph_nongraph.h" +#include "igraph_error.h" +#include "igraph_types.h" +#include "igraph_vector.h" +#include "igraph_memory.h" + +#include "core/math.h" + +#include "config.h" +#include +#include + +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); + +static int igraph_rng_R_init(void **state) { + return IGRAPH_SUCCESS; +} + +static void igraph_rng_R_destroy(void *state) { +} + +static int igraph_rng_R_seed(void *state, unsigned long int seed) { + IGRAPH_ERROR("R RNG error, unsupported function called", + IGRAPH_EINTERNAL); + return IGRAPH_SUCCESS; +} + +static unsigned long int igraph_rng_R_get(void *state) { + return (unsigned long) (unif_rand() * 0x7FFFFFFFUL); +} + +static igraph_real_t igraph_rng_R_get_real(void *state) { + return unif_rand(); +} + +static igraph_real_t igraph_rng_R_get_norm(void *state) { + return norm_rand(); +} + +static igraph_real_t igraph_rng_R_get_geom(void *state, igraph_real_t p) { + return Rf_rgeom(p); +} + +static igraph_real_t igraph_rng_R_get_binom(void *state, long int n, + igraph_real_t p) { + return Rf_rbinom(n, p); +} + +static 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(); +} + +static igraph_rng_type_t igraph_rng_R_type = { + /* 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_rng_t igraph_rng_R_instance; + +void igraph_rng_R_install() { + igraph_rng_init(&igraph_rng_R_instance, &igraph_rng_R_type); + igraph_rng_set_default(&igraph_rng_R_instance); +} diff --git a/src/rigraph/rrandom.h b/src/rigraph/rrandom.h new file mode 100644 index 0000000..4d9be8e --- /dev/null +++ b/src/rigraph/rrandom.h @@ -0,0 +1,28 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2022 The igraph development 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., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#ifndef IGRAPH_RRANDOM_H +#define IGRAPH_RRANDOM_H + +void igraph_rng_R_install(); + +#endif diff --git a/src/rigraph/vendor/arpack/debug.h b/src/rigraph/vendor/arpack/debug.h new file mode 100644 index 0000000..5eb0bb1 --- /dev/null +++ b/src/rigraph/vendor/arpack/debug.h @@ -0,0 +1,16 @@ +c +c\SCCS Information: @(#) +c FILE: debug.h SID: 2.3 DATE OF SID: 11/16/95 RELEASE: 2 +c +c %---------------------------------% +c | See debug.doc for documentation | +c %---------------------------------% + integer logfil, ndigit, mgetv0, + & msaupd, msaup2, msaitr, mseigt, msapps, msgets, mseupd, + & mnaupd, mnaup2, mnaitr, mneigh, mnapps, mngets, mneupd, + & mcaupd, mcaup2, mcaitr, mceigh, mcapps, mcgets, mceupd + common /debug/ + & logfil, ndigit, mgetv0, + & msaupd, msaup2, msaitr, mseigt, msapps, msgets, mseupd, + & mnaupd, mnaup2, mnaitr, mneigh, mnapps, mngets, mneupd, + & mcaupd, mcaup2, mcaitr, mceigh, mcapps, mcgets, mceupd diff --git a/src/rigraph/vendor/arpack/dgetv0.f b/src/rigraph/vendor/arpack/dgetv0.f new file mode 100644 index 0000000..b64d47a --- /dev/null +++ b/src/rigraph/vendor/arpack/dgetv0.f @@ -0,0 +1,419 @@ +c----------------------------------------------------------------------- +c\BeginDoc +c +c\Name: igraphdgetv0 +c +c\Description: +c Generate a random initial residual vector for the Arnoldi process. +c Force the residual vector to be in the range of the operator OP. +c +c\Usage: +c call igraphdgetv0 +c ( IDO, BMAT, ITRY, INITV, N, J, V, LDV, RESID, RNORM, +c IPNTR, WORKD, IERR ) +c +c\Arguments +c IDO Integer. (INPUT/OUTPUT) +c Reverse communication flag. IDO must be zero on the first +c call to igraphdgetv0. +c ------------------------------------------------------------- +c IDO = 0: first call to the reverse communication interface +c IDO = -1: compute Y = OP * X where +c IPNTR(1) is the pointer into WORKD for X, +c IPNTR(2) is the pointer into WORKD for Y. +c This is for the initialization phase to force the +c starting vector into the range of OP. +c IDO = 2: compute Y = B * X where +c IPNTR(1) is the pointer into WORKD for X, +c IPNTR(2) is the pointer into WORKD for Y. +c IDO = 99: done +c ------------------------------------------------------------- +c +c BMAT Character*1. (INPUT) +c BMAT specifies the type of the matrix B in the (generalized) +c eigenvalue problem A*x = lambda*B*x. +c B = 'I' -> standard eigenvalue problem A*x = lambda*x +c B = 'G' -> generalized eigenvalue problem A*x = lambda*B*x +c +c ITRY Integer. (INPUT) +c ITRY counts the number of times that igraphdgetv0 is called. +c It should be set to 1 on the initial call to igraphdgetv0. +c +c INITV Logical variable. (INPUT) +c .TRUE. => the initial residual vector is given in RESID. +c .FALSE. => generate a random initial residual vector. +c +c N Integer. (INPUT) +c Dimension of the problem. +c +c J Integer. (INPUT) +c Index of the residual vector to be generated, with respect to +c the Arnoldi process. J > 1 in case of a "restart". +c +c V Double precision N by J array. (INPUT) +c The first J-1 columns of V contain the current Arnoldi basis +c if this is a "restart". +c +c LDV Integer. (INPUT) +c Leading dimension of V exactly as declared in the calling +c program. +c +c RESID Double precision array of length N. (INPUT/OUTPUT) +c Initial residual vector to be generated. If RESID is +c provided, force RESID into the range of the operator OP. +c +c RNORM Double precision scalar. (OUTPUT) +c B-norm of the generated residual. +c +c IPNTR Integer array of length 3. (OUTPUT) +c +c WORKD Double precision work array of length 2*N. (REVERSE COMMUNICATION). +c On exit, WORK(1:N) = B*RESID to be used in SSAITR. +c +c IERR Integer. (OUTPUT) +c = 0: Normal exit. +c = -1: Cannot generate a nontrivial restarted residual vector +c in the range of the operator OP. +c +c\EndDoc +c +c----------------------------------------------------------------------- +c +c\BeginLib +c +c\Local variables: +c xxxxxx real +c +c\References: +c 1. D.C. Sorensen, "Implicit Application of Polynomial Filters in +c a k-Step Arnoldi Method", SIAM J. Matr. Anal. Apps., 13 (1992), +c pp 357-385. +c 2. R.B. Lehoucq, "Analysis and Implementation of an Implicitly +c Restarted Arnoldi Iteration", Rice University Technical Report +c TR95-13, Department of Computational and Applied Mathematics. +c +c\Routines called: +c igraphsecond ARPACK utility routine for timing. +c igraphdvout ARPACK utility routine for vector output. +c dlarnv LAPACK routine for generating a random vector. +c dgemv Level 2 BLAS routine for matrix vector multiplication. +c dcopy Level 1 BLAS that copies one vector to another. +c ddot Level 1 BLAS that computes the scalar product of two vectors. +c dnrm2 Level 1 BLAS that computes the norm of a vector. +c +c\Author +c Danny Sorensen Phuong Vu +c Richard Lehoucq CRPC / Rice University +c Dept. of Computational & Houston, Texas +c Applied Mathematics +c Rice University +c Houston, Texas +c +c\SCCS Information: @(#) +c FILE: getv0.F SID: 2.6 DATE OF SID: 8/27/96 RELEASE: 2 +c +c\EndLib +c +c----------------------------------------------------------------------- +c + subroutine igraphdgetv0 + & ( ido, bmat, itry, initv, n, j, v, ldv, resid, rnorm, + & ipntr, workd, ierr ) +c +c %----------------------------------------------------% +c | Include files for debugging and timing information | +c %----------------------------------------------------% +c + include 'debug.h' + include 'stat.h' +c +c %------------------% +c | Scalar Arguments | +c %------------------% +c + character bmat*1 + logical initv + integer ido, ierr, itry, j, ldv, n + Double precision + & rnorm +c +c %-----------------% +c | Array Arguments | +c %-----------------% +c + integer ipntr(3) + Double precision + & resid(n), v(ldv,j), workd(2*n) +c +c %------------% +c | Parameters | +c %------------% +c + Double precision + & one, zero + parameter (one = 1.0D+0, zero = 0.0D+0) +c +c %------------------------% +c | Local Scalars & Arrays | +c %------------------------% +c + logical first, inits, orth + integer idist, iseed(4), iter, msglvl, jj + Double precision + & rnorm0 + save first, iseed, inits, iter, msglvl, orth, rnorm0 +c +c %----------------------% +c | External Subroutines | +c %----------------------% +c + external dlarnv, igraphdvout, dcopy, dgemv, igraphsecond +c +c %--------------------% +c | External Functions | +c %--------------------% +c + Double precision + & ddot, dnrm2 + external ddot, dnrm2 +c +c %---------------------% +c | Intrinsic Functions | +c %---------------------% +c + intrinsic abs, sqrt +c +c %-----------------% +c | Data Statements | +c %-----------------% +c + data inits /.true./ +c +c %-----------------------% +c | Executable Statements | +c %-----------------------% +c +c +c %-----------------------------------% +c | Initialize the seed of the LAPACK | +c | random number generator | +c %-----------------------------------% +c + if (inits) then + iseed(1) = 1 + iseed(2) = 3 + iseed(3) = 5 + iseed(4) = 7 + inits = .false. + end if +c + if (ido .eq. 0) then +c +c %-------------------------------% +c | Initialize timing statistics | +c | & message level for debugging | +c %-------------------------------% +c + call igraphsecond (t0) + msglvl = mgetv0 +c + ierr = 0 + iter = 0 + first = .FALSE. + orth = .FALSE. +c +c %-----------------------------------------------------% +c | Possibly generate a random starting vector in RESID | +c | Use a LAPACK random number generator used by the | +c | matrix generation routines. | +c | idist = 1: uniform (0,1) distribution; | +c | idist = 2: uniform (-1,1) distribution; | +c | idist = 3: normal (0,1) distribution; | +c %-----------------------------------------------------% +c + if (.not.initv) then + idist = 2 + call dlarnv (idist, iseed, n, resid) + end if +c +c %----------------------------------------------------------% +c | Force the starting vector into the range of OP to handle | +c | the generalized problem when B is possibly (singular). | +c %----------------------------------------------------------% +c + call igraphsecond (t2) + if (bmat .eq. 'G') then + nopx = nopx + 1 + ipntr(1) = 1 + ipntr(2) = n + 1 + call dcopy (n, resid, 1, workd, 1) + ido = -1 + go to 9000 + end if + end if +c +c %-----------------------------------------% +c | Back from computing OP*(initial-vector) | +c %-----------------------------------------% +c + if (first) go to 20 +c +c %-----------------------------------------------% +c | Back from computing B*(orthogonalized-vector) | +c %-----------------------------------------------% +c + if (orth) go to 40 +c + if (bmat .eq. 'G') then + call igraphsecond (t3) + tmvopx = tmvopx + (t3 - t2) + end if +c +c %------------------------------------------------------% +c | Starting vector is now in the range of OP; r = OP*r; | +c | Compute B-norm of starting vector. | +c %------------------------------------------------------% +c + call igraphsecond (t2) + first = .TRUE. + if (bmat .eq. 'G') then + nbx = nbx + 1 + call dcopy (n, workd(n+1), 1, resid, 1) + ipntr(1) = n + 1 + ipntr(2) = 1 + ido = 2 + go to 9000 + else if (bmat .eq. 'I') then + call dcopy (n, resid, 1, workd, 1) + end if +c + 20 continue +c + if (bmat .eq. 'G') then + call igraphsecond (t3) + tmvbx = tmvbx + (t3 - t2) + end if +c + first = .FALSE. + if (bmat .eq. 'G') then + rnorm0 = ddot (n, resid, 1, workd, 1) + rnorm0 = sqrt(abs(rnorm0)) + else if (bmat .eq. 'I') then + rnorm0 = dnrm2(n, resid, 1) + end if + rnorm = rnorm0 +c +c %---------------------------------------------% +c | Exit if this is the very first Arnoldi step | +c %---------------------------------------------% +c + if (j .eq. 1) go to 50 +c +c %---------------------------------------------------------------- +c | Otherwise need to B-orthogonalize the starting vector against | +c | the current Arnoldi basis using Gram-Schmidt with iter. ref. | +c | This is the case where an invariant subspace is encountered | +c | in the middle of the Arnoldi factorization. | +c | | +c | s = V^{T}*B*r; r = r - V*s; | +c | | +c | Stopping criteria used for iter. ref. is discussed in | +c | Parlett's book, page 107 and in Gragg & Reichel TOMS paper. | +c %---------------------------------------------------------------% +c + orth = .TRUE. + 30 continue +c + call dgemv ('T', n, j-1, one, v, ldv, workd, 1, + & zero, workd(n+1), 1) + call dgemv ('N', n, j-1, -one, v, ldv, workd(n+1), 1, + & one, resid, 1) +c +c %----------------------------------------------------------% +c | Compute the B-norm of the orthogonalized starting vector | +c %----------------------------------------------------------% +c + call igraphsecond (t2) + if (bmat .eq. 'G') then + nbx = nbx + 1 + call dcopy (n, resid, 1, workd(n+1), 1) + ipntr(1) = n + 1 + ipntr(2) = 1 + ido = 2 + go to 9000 + else if (bmat .eq. 'I') then + call dcopy (n, resid, 1, workd, 1) + end if +c + 40 continue +c + if (bmat .eq. 'G') then + call igraphsecond (t3) + tmvbx = tmvbx + (t3 - t2) + end if +c + if (bmat .eq. 'G') then + rnorm = ddot (n, resid, 1, workd, 1) + rnorm = sqrt(abs(rnorm)) + else if (bmat .eq. 'I') then + rnorm = dnrm2(n, resid, 1) + end if +c +c %--------------------------------------% +c | Check for further orthogonalization. | +c %--------------------------------------% +c + if (msglvl .gt. 2) then + call igraphdvout (logfil, 1, [rnorm0], ndigit, + & '_getv0: re-orthonalization ; rnorm0 is') + call igraphdvout (logfil, 1, [rnorm], ndigit, + & '_getv0: re-orthonalization ; rnorm is') + end if +c + if (rnorm .gt. 0.717*rnorm0) go to 50 +c + iter = iter + 1 + if (iter .le. 1) then +c +c %-----------------------------------% +c | Perform iterative refinement step | +c %-----------------------------------% +c + rnorm0 = rnorm + go to 30 + else +c +c %------------------------------------% +c | Iterative refinement step "failed" | +c %------------------------------------% +c + do 45 jj = 1, n + resid(jj) = zero + 45 continue + rnorm = zero + ierr = -1 + end if +c + 50 continue +c + if (msglvl .gt. 0) then + call igraphdvout (logfil, 1, [rnorm], ndigit, + & '_getv0: B-norm of initial / restarted starting vector') + end if + if (msglvl .gt. 2) then + call igraphdvout (logfil, n, resid, ndigit, + & '_getv0: initial / restarted starting vector') + end if + ido = 99 +c + call igraphsecond (t1) + tgetv0 = tgetv0 + (t1 - t0) +c + 9000 continue + return +c +c %---------------% +c | End of igraphdgetv0 | +c %---------------% +c + end diff --git a/src/rigraph/vendor/arpack/dlaqrb.f b/src/rigraph/vendor/arpack/dlaqrb.f new file mode 100644 index 0000000..5fcefec --- /dev/null +++ b/src/rigraph/vendor/arpack/dlaqrb.f @@ -0,0 +1,521 @@ +c----------------------------------------------------------------------- +c\BeginDoc +c +c\Name: igraphdlaqrb +c +c\Description: +c Compute the eigenvalues and the Schur decomposition of an upper +c Hessenberg submatrix in rows and columns ILO to IHI. Only the +c last component of the Schur vectors are computed. +c +c This is mostly a modification of the LAPACK routine dlahqr. +c +c\Usage: +c call igraphdlaqrb +c ( WANTT, N, ILO, IHI, H, LDH, WR, WI, Z, INFO ) +c +c\Arguments +c WANTT Logical variable. (INPUT) +c = .TRUE. : the full Schur form T is required; +c = .FALSE.: only eigenvalues are required. +c +c N Integer. (INPUT) +c The order of the matrix H. N >= 0. +c +c ILO Integer. (INPUT) +c IHI Integer. (INPUT) +c It is assumed that H is already upper quasi-triangular in +c rows and columns IHI+1:N, and that H(ILO,ILO-1) = 0 (unless +c ILO = 1). SLAQRB works primarily with the Hessenberg +c submatrix in rows and columns ILO to IHI, but applies +c transformations to all of H if WANTT is .TRUE.. +c 1 <= ILO <= max(1,IHI); IHI <= N. +c +c H Double precision array, dimension (LDH,N). (INPUT/OUTPUT) +c On entry, the upper Hessenberg matrix H. +c On exit, if WANTT is .TRUE., H is upper quasi-triangular in +c rows and columns ILO:IHI, with any 2-by-2 diagonal blocks in +c standard form. If WANTT is .FALSE., the contents of H are +c unspecified on exit. +c +c LDH Integer. (INPUT) +c The leading dimension of the array H. LDH >= max(1,N). +c +c WR Double precision array, dimension (N). (OUTPUT) +c WI Double precision array, dimension (N). (OUTPUT) +c The real and imaginary parts, respectively, of the computed +c eigenvalues ILO to IHI are stored in the corresponding +c elements of WR and WI. If two eigenvalues are computed as a +c complex conjugate pair, they are stored in consecutive +c elements of WR and WI, say the i-th and (i+1)th, with +c WI(i) > 0 and WI(i+1) < 0. If WANTT is .TRUE., the +c eigenvalues are stored in the same order as on the diagonal +c of the Schur form returned in H, with WR(i) = H(i,i), and, if +c H(i:i+1,i:i+1) is a 2-by-2 diagonal block, +c WI(i) = sqrt(H(i+1,i)*H(i,i+1)) and WI(i+1) = -WI(i). +c +c Z Double precision array, dimension (N). (OUTPUT) +c On exit Z contains the last components of the Schur vectors. +c +c INFO Integer. (OUPUT) +c = 0: successful exit +c > 0: SLAQRB failed to compute all the eigenvalues ILO to IHI +c in a total of 30*(IHI-ILO+1) iterations; if INFO = i, +c elements i+1:ihi of WR and WI contain those eigenvalues +c which have been successfully computed. +c +c\Remarks +c 1. None. +c +c----------------------------------------------------------------------- +c +c\BeginLib +c +c\Local variables: +c xxxxxx real +c +c\Routines called: +c dlabad LAPACK routine that computes machine constants. +c dlamch LAPACK routine that determines machine constants. +c dlanhs LAPACK routine that computes various norms of a matrix. +c dlanv2 LAPACK routine that computes the Schur factorization of +c 2 by 2 nonsymmetric matrix in standard form. +c dlarfg LAPACK Householder reflection construction routine. +c dcopy Level 1 BLAS that copies one vector to another. +c drot Level 1 BLAS that applies a rotation to a 2 by 2 matrix. + +c +c\Author +c Danny Sorensen Phuong Vu +c Richard Lehoucq CRPC / Rice University +c Dept. of Computational & Houston, Texas +c Applied Mathematics +c Rice University +c Houston, Texas +c +c\Revision history: +c xx/xx/92: Version ' 2.4' +c Modified from the LAPACK routine dlahqr so that only the +c last component of the Schur vectors are computed. +c +c\SCCS Information: @(#) +c FILE: laqrb.F SID: 2.2 DATE OF SID: 8/27/96 RELEASE: 2 +c +c\Remarks +c 1. None +c +c\EndLib +c +c----------------------------------------------------------------------- +c + subroutine igraphdlaqrb ( wantt, n, ilo, ihi, h, ldh, wr, wi, + & z, info ) +c +c %------------------% +c | Scalar Arguments | +c %------------------% +c + logical wantt + integer ihi, ilo, info, ldh, n +c +c %-----------------% +c | Array Arguments | +c %-----------------% +c + Double precision + & h( ldh, * ), wi( * ), wr( * ), z( * ) +c +c %------------% +c | Parameters | +c %------------% +c + Double precision + & zero, one, dat1, dat2 + parameter (zero = 0.0D+0, one = 1.0D+0, dat1 = 7.5D-1, + & dat2 = -4.375D-1) +c +c %------------------------% +c | Local Scalars & Arrays | +c %------------------------% +c + integer i, i1, i2, itn, its, j, k, l, m, nh, nr + Double precision + & cs, h00, h10, h11, h12, h21, h22, h33, h33s, + & h43h34, h44, h44s, ovfl, s, smlnum, sn, sum, + & t1, t2, t3, tst1, ulp, unfl, v1, v2, v3 + Double precision + & v( 3 ), work( 1 ) +c +c %--------------------% +c | External Functions | +c %--------------------% +c + Double precision + & dlamch, dlanhs + external dlamch, dlanhs +c +c %----------------------% +c | External Subroutines | +c %----------------------% +c + external dcopy, dlabad, dlanv2, dlarfg, drot +c +c %-----------------------% +c | Executable Statements | +c %-----------------------% +c + info = 0 +c +c %--------------------------% +c | Quick return if possible | +c %--------------------------% +c + if( n.eq.0 ) + & return + if( ilo.eq.ihi ) then + wr( ilo ) = h( ilo, ilo ) + wi( ilo ) = zero + return + end if +c +c %---------------------------------------------% +c | Initialize the vector of last components of | +c | the Schur vectors for accumulation. | +c %---------------------------------------------% +c + do 5 j = 1, n-1 + z(j) = zero + 5 continue + z(n) = one +c + nh = ihi - ilo + 1 +c +c %-------------------------------------------------------------% +c | Set machine-dependent constants for the stopping criterion. | +c | If norm(H) <= sqrt(OVFL), overflow should not occur. | +c %-------------------------------------------------------------% +c + unfl = dlamch( 'safe minimum' ) + ovfl = one / unfl + call dlabad( unfl, ovfl ) + ulp = dlamch( 'precision' ) + smlnum = unfl*( nh / ulp ) +c +c %---------------------------------------------------------------% +c | I1 and I2 are the indices of the first row and last column | +c | of H to which transformations must be applied. If eigenvalues | +c | only are computed, I1 and I2 are set inside the main loop. | +c | Zero out H(J+2,J) = ZERO for J=1:N if WANTT = .TRUE. | +c | else H(J+2,J) for J=ILO:IHI-ILO-1 if WANTT = .FALSE. | +c %---------------------------------------------------------------% +c + if( wantt ) then + i1 = 1 + i2 = n + do 8 i=1,i2-2 + h(i1+i+1,i) = zero + 8 continue + else + do 9 i=1, ihi-ilo-1 + h(ilo+i+1,ilo+i-1) = zero + 9 continue + end if +c +c %---------------------------------------------------% +c | ITN is the total number of QR iterations allowed. | +c %---------------------------------------------------% +c + itn = 30*nh +c +c ------------------------------------------------------------------ +c The main loop begins here. I is the loop index and decreases from +c IHI to ILO in steps of 1 or 2. Each iteration of the loop works +c with the active submatrix in rows and columns L to I. +c Eigenvalues I+1 to IHI have already converged. Either L = ILO or +c H(L,L-1) is negligible so that the matrix splits. +c ------------------------------------------------------------------ +c + i = ihi + 10 continue + l = ilo + if( i.lt.ilo ) + & go to 150 + +c %--------------------------------------------------------------% +c | Perform QR iterations on rows and columns ILO to I until a | +c | submatrix of order 1 or 2 splits off at the bottom because a | +c | subdiagonal element has become negligible. | +c %--------------------------------------------------------------% + + do 130 its = 0, itn +c +c %----------------------------------------------% +c | Look for a single small subdiagonal element. | +c %----------------------------------------------% +c + do 20 k = i, l + 1, -1 + tst1 = abs( h( k-1, k-1 ) ) + abs( h( k, k ) ) + if( tst1.eq.zero ) + & tst1 = dlanhs( '1', i-l+1, h( l, l ), ldh, work ) + if( abs( h( k, k-1 ) ).le.max( ulp*tst1, smlnum ) ) + & go to 30 + 20 continue + 30 continue + l = k + if( l.gt.ilo ) then +c +c %------------------------% +c | H(L,L-1) is negligible | +c %------------------------% +c + h( l, l-1 ) = zero + end if +c +c %-------------------------------------------------------------% +c | Exit from loop if a submatrix of order 1 or 2 has split off | +c %-------------------------------------------------------------% +c + if( l.ge.i-1 ) + & go to 140 +c +c %---------------------------------------------------------% +c | Now the active submatrix is in rows and columns L to I. | +c | If eigenvalues only are being computed, only the active | +c | submatrix need be transformed. | +c %---------------------------------------------------------% +c + if( .not.wantt ) then + i1 = l + i2 = i + end if +c + if( its.eq.10 .or. its.eq.20 ) then +c +c %-------------------% +c | Exceptional shift | +c %-------------------% +c + s = abs( h( i, i-1 ) ) + abs( h( i-1, i-2 ) ) + h44 = dat1*s + h33 = h44 + h43h34 = dat2*s*s +c + else +c +c %-----------------------------------------% +c | Prepare to use Wilkinson's double shift | +c %-----------------------------------------% +c + h44 = h( i, i ) + h33 = h( i-1, i-1 ) + h43h34 = h( i, i-1 )*h( i-1, i ) + end if +c +c %-----------------------------------------------------% +c | Look for two consecutive small subdiagonal elements | +c %-----------------------------------------------------% +c + do 40 m = i - 2, l, -1 +c +c %---------------------------------------------------------% +c | Determine the effect of starting the double-shift QR | +c | iteration at row M, and see if this would make H(M,M-1) | +c | negligible. | +c %---------------------------------------------------------% +c + h11 = h( m, m ) + h22 = h( m+1, m+1 ) + h21 = h( m+1, m ) + h12 = h( m, m+1 ) + h44s = h44 - h11 + h33s = h33 - h11 + v1 = ( h33s*h44s-h43h34 ) / h21 + h12 + v2 = h22 - h11 - h33s - h44s + v3 = h( m+2, m+1 ) + s = abs( v1 ) + abs( v2 ) + abs( v3 ) + v1 = v1 / s + v2 = v2 / s + v3 = v3 / s + v( 1 ) = v1 + v( 2 ) = v2 + v( 3 ) = v3 + if( m.eq.l ) + & go to 50 + h00 = h( m-1, m-1 ) + h10 = h( m, m-1 ) + tst1 = abs( v1 )*( abs( h00 )+abs( h11 )+abs( h22 ) ) + if( abs( h10 )*( abs( v2 )+abs( v3 ) ).le.ulp*tst1 ) + & go to 50 + 40 continue + 50 continue +c +c %----------------------% +c | Double-shift QR step | +c %----------------------% +c + do 120 k = m, i - 1 +c +c ------------------------------------------------------------ +c The first iteration of this loop determines a reflection G +c from the vector V and applies it from left and right to H, +c thus creating a nonzero bulge below the subdiagonal. +c +c Each subsequent iteration determines a reflection G to +c restore the Hessenberg form in the (K-1)th column, and thus +c chases the bulge one step toward the bottom of the active +c submatrix. NR is the order of G. +c ------------------------------------------------------------ +c + nr = min( 3, i-k+1 ) + if( k.gt.m ) + & call dcopy( nr, h( k, k-1 ), 1, v, 1 ) + call dlarfg( nr, v( 1 ), v( 2 ), 1, t1 ) + if( k.gt.m ) then + h( k, k-1 ) = v( 1 ) + h( k+1, k-1 ) = zero + if( k.lt.i-1 ) + & h( k+2, k-1 ) = zero + else if( m.gt.l ) then + h( k, k-1 ) = -h( k, k-1 ) + end if + v2 = v( 2 ) + t2 = t1*v2 + if( nr.eq.3 ) then + v3 = v( 3 ) + t3 = t1*v3 +c +c %------------------------------------------------% +c | Apply G from the left to transform the rows of | +c | the matrix in columns K to I2. | +c %------------------------------------------------% +c + do 60 j = k, i2 + sum = h( k, j ) + v2*h( k+1, j ) + v3*h( k+2, j ) + h( k, j ) = h( k, j ) - sum*t1 + h( k+1, j ) = h( k+1, j ) - sum*t2 + h( k+2, j ) = h( k+2, j ) - sum*t3 + 60 continue +c +c %----------------------------------------------------% +c | Apply G from the right to transform the columns of | +c | the matrix in rows I1 to min(K+3,I). | +c %----------------------------------------------------% +c + do 70 j = i1, min( k+3, i ) + sum = h( j, k ) + v2*h( j, k+1 ) + v3*h( j, k+2 ) + h( j, k ) = h( j, k ) - sum*t1 + h( j, k+1 ) = h( j, k+1 ) - sum*t2 + h( j, k+2 ) = h( j, k+2 ) - sum*t3 + 70 continue +c +c %----------------------------------% +c | Accumulate transformations for Z | +c %----------------------------------% +c + sum = z( k ) + v2*z( k+1 ) + v3*z( k+2 ) + z( k ) = z( k ) - sum*t1 + z( k+1 ) = z( k+1 ) - sum*t2 + z( k+2 ) = z( k+2 ) - sum*t3 + + else if( nr.eq.2 ) then +c +c %------------------------------------------------% +c | Apply G from the left to transform the rows of | +c | the matrix in columns K to I2. | +c %------------------------------------------------% +c + do 90 j = k, i2 + sum = h( k, j ) + v2*h( k+1, j ) + h( k, j ) = h( k, j ) - sum*t1 + h( k+1, j ) = h( k+1, j ) - sum*t2 + 90 continue +c +c %----------------------------------------------------% +c | Apply G from the right to transform the columns of | +c | the matrix in rows I1 to min(K+3,I). | +c %----------------------------------------------------% +c + do 100 j = i1, i + sum = h( j, k ) + v2*h( j, k+1 ) + h( j, k ) = h( j, k ) - sum*t1 + h( j, k+1 ) = h( j, k+1 ) - sum*t2 + 100 continue +c +c %----------------------------------% +c | Accumulate transformations for Z | +c %----------------------------------% +c + sum = z( k ) + v2*z( k+1 ) + z( k ) = z( k ) - sum*t1 + z( k+1 ) = z( k+1 ) - sum*t2 + end if + 120 continue + + 130 continue +c +c %-------------------------------------------------------% +c | Failure to converge in remaining number of iterations | +c %-------------------------------------------------------% +c + info = i + return + + 140 continue + + if( l.eq.i ) then +c +c %------------------------------------------------------% +c | H(I,I-1) is negligible: one eigenvalue has converged | +c %------------------------------------------------------% +c + wr( i ) = h( i, i ) + wi( i ) = zero + + else if( l.eq.i-1 ) then +c +c %--------------------------------------------------------% +c | H(I-1,I-2) is negligible; | +c | a pair of eigenvalues have converged. | +c | | +c | Transform the 2-by-2 submatrix to standard Schur form, | +c | and compute and store the eigenvalues. | +c %--------------------------------------------------------% +c + call dlanv2( h( i-1, i-1 ), h( i-1, i ), h( i, i-1 ), + & h( i, i ), wr( i-1 ), wi( i-1 ), wr( i ), wi( i ), + & cs, sn ) + + if( wantt ) then +c +c %-----------------------------------------------------% +c | Apply the transformation to the rest of H and to Z, | +c | as required. | +c %-----------------------------------------------------% +c + if( i2.gt.i ) + & call drot( i2-i, h( i-1, i+1 ), ldh, h( i, i+1 ), ldh, + & cs, sn ) + call drot( i-i1-1, h( i1, i-1 ), 1, h( i1, i ), 1, cs, sn ) + sum = cs*z( i-1 ) + sn*z( i ) + z( i ) = cs*z( i ) - sn*z( i-1 ) + z( i-1 ) = sum + end if + end if +c +c %---------------------------------------------------------% +c | Decrement number of remaining iterations, and return to | +c | start of the main loop with new value of I. | +c %---------------------------------------------------------% +c + itn = itn - its + i = l - 1 + go to 10 + + 150 continue + return +c +c %---------------% +c | End of igraphdlaqrb | +c %---------------% +c + end diff --git a/src/rigraph/vendor/arpack/dmout.f b/src/rigraph/vendor/arpack/dmout.f new file mode 100644 index 0000000..6204d64 --- /dev/null +++ b/src/rigraph/vendor/arpack/dmout.f @@ -0,0 +1,167 @@ +*----------------------------------------------------------------------- +* 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 IGRAPHDMOUT( LOUT, M, N, A, LDA, IDIGIT, IFMT ) +* ... +* ... SPECIFICATIONS FOR ARGUMENTS +* ... +* ... SPECIFICATIONS FOR LOCAL VARIABLES +* .. Scalar Arguments .. + CHARACTER*( * ) IFMT + INTEGER IDIGIT, LDA, LOUT, M, N +* .. +* .. Array Arguments .. + DOUBLE PRECISION A( LDA, * ) +* .. +* .. Local Scalars .. + CHARACTER*80 LINE + INTEGER I, J, K1, K2, LLL, NDIGIT +* .. +* .. Local Arrays .. + CHARACTER ICOL( 3 ) +* .. +* .. Intrinsic Functions .. + INTRINSIC LEN, MIN, MIN0 +* .. +* .. Data statements .. + DATA ICOL( 1 ), ICOL( 2 ), ICOL( 3 ) / 'C', 'o', + $ 'l' / +* .. +* .. Executable Statements .. +* ... +* ... FIRST EXECUTABLE STATEMENT +* +c$$$ LLL = MIN( LEN( IFMT ), 80 ) +c$$$ DO 10 I = 1, LLL +c$$$ LINE( I: I ) = '-' +c$$$ 10 CONTINUE +c$$$* +c$$$ DO 20 I = LLL + 1, 80 +c$$$ LINE( I: I ) = ' ' +c$$$ 20 CONTINUE +c$$$* +c$$$ WRITE( LOUT, FMT = 9999 )IFMT, LINE( 1: LLL ) +c$$$ 9999 FORMAT( / 1X, A, / 1X, A ) +c$$$* +c$$$ IF( M.LE.0 .OR. N.LE.0 .OR. LDA.LE.0 ) +c$$$ $ RETURN +c$$$ NDIGIT = IDIGIT +c$$$ IF( IDIGIT.EQ.0 ) +c$$$ $ NDIGIT = 4 +c$$$* +c$$$*======================================================================= +c$$$* CODE FOR OUTPUT USING 72 COLUMNS FORMAT +c$$$*======================================================================= +c$$$* +c$$$ IF( IDIGIT.LT.0 ) THEN +c$$$ NDIGIT = -IDIGIT +c$$$ IF( NDIGIT.LE.4 ) THEN +c$$$ DO 40 K1 = 1, N, 5 +c$$$ K2 = MIN0( N, K1+4 ) +c$$$ WRITE( LOUT, FMT = 9998 )( ICOL, I, I = K1, K2 ) +c$$$ DO 30 I = 1, M +c$$$ WRITE( LOUT, FMT = 9994 )I, ( A( I, J ), J = K1, K2 ) +c$$$ 30 CONTINUE +c$$$ 40 CONTINUE +c$$$* +c$$$ ELSE IF( NDIGIT.LE.6 ) THEN +c$$$ DO 60 K1 = 1, N, 4 +c$$$ K2 = MIN0( N, K1+3 ) +c$$$ WRITE( LOUT, FMT = 9997 )( ICOL, I, I = K1, K2 ) +c$$$ DO 50 I = 1, M +c$$$ WRITE( LOUT, FMT = 9993 )I, ( A( I, J ), J = K1, K2 ) +c$$$ 50 CONTINUE +c$$$ 60 CONTINUE +c$$$* +c$$$ ELSE IF( NDIGIT.LE.10 ) THEN +c$$$ DO 80 K1 = 1, N, 3 +c$$$ K2 = MIN0( N, K1+2 ) +c$$$ WRITE( LOUT, FMT = 9996 )( ICOL, I, I = K1, K2 ) +c$$$ DO 70 I = 1, M +c$$$ WRITE( LOUT, FMT = 9992 )I, ( A( I, J ), J = K1, K2 ) +c$$$ 70 CONTINUE +c$$$ 80 CONTINUE +c$$$* +c$$$ ELSE +c$$$ DO 100 K1 = 1, N, 2 +c$$$ K2 = MIN0( N, K1+1 ) +c$$$ WRITE( LOUT, FMT = 9995 )( ICOL, I, I = K1, K2 ) +c$$$ DO 90 I = 1, M +c$$$ WRITE( LOUT, FMT = 9991 )I, ( A( I, J ), J = K1, K2 ) +c$$$ 90 CONTINUE +c$$$ 100 CONTINUE +c$$$ END IF +c$$$* +c$$$*======================================================================= +c$$$* CODE FOR OUTPUT USING 132 COLUMNS FORMAT +c$$$*======================================================================= +c$$$* +c$$$ ELSE +c$$$ IF( NDIGIT.LE.4 ) THEN +c$$$ DO 120 K1 = 1, N, 10 +c$$$ K2 = MIN0( N, K1+9 ) +c$$$ WRITE( LOUT, FMT = 9998 )( ICOL, I, I = K1, K2 ) +c$$$ DO 110 I = 1, M +c$$$ WRITE( LOUT, FMT = 9994 )I, ( A( I, J ), J = K1, K2 ) +c$$$ 110 CONTINUE +c$$$ 120 CONTINUE +c$$$* +c$$$ ELSE IF( NDIGIT.LE.6 ) THEN +c$$$ DO 140 K1 = 1, N, 8 +c$$$ K2 = MIN0( N, K1+7 ) +c$$$ WRITE( LOUT, FMT = 9997 )( ICOL, I, I = K1, K2 ) +c$$$ DO 130 I = 1, M +c$$$ WRITE( LOUT, FMT = 9993 )I, ( A( I, J ), J = K1, K2 ) +c$$$ 130 CONTINUE +c$$$ 140 CONTINUE +c$$$* +c$$$ ELSE IF( NDIGIT.LE.10 ) THEN +c$$$ DO 160 K1 = 1, N, 6 +c$$$ K2 = MIN0( N, K1+5 ) +c$$$ WRITE( LOUT, FMT = 9996 )( ICOL, I, I = K1, K2 ) +c$$$ DO 150 I = 1, M +c$$$ WRITE( LOUT, FMT = 9992 )I, ( A( I, J ), J = K1, K2 ) +c$$$ 150 CONTINUE +c$$$ 160 CONTINUE +c$$$* +c$$$ ELSE +c$$$ DO 180 K1 = 1, N, 5 +c$$$ K2 = MIN0( N, K1+4 ) +c$$$ WRITE( LOUT, FMT = 9995 )( ICOL, I, I = K1, K2 ) +c$$$ DO 170 I = 1, M +c$$$ WRITE( LOUT, FMT = 9991 )I, ( A( I, J ), J = K1, K2 ) +c$$$ 170 CONTINUE +c$$$ 180 CONTINUE +c$$$ END IF +c$$$ END IF +c$$$ WRITE( LOUT, FMT = 9990 ) +c$$$* +c$$$ 9998 FORMAT( 10X, 10( 4X, 3A1, I4, 1X ) ) +c$$$ 9997 FORMAT( 10X, 8( 5X, 3A1, I4, 2X ) ) +c$$$ 9996 FORMAT( 10X, 6( 7X, 3A1, I4, 4X ) ) +c$$$ 9995 FORMAT( 10X, 5( 9X, 3A1, I4, 6X ) ) +c$$$ 9994 FORMAT( 1X, ' Row', I4, ':', 1X, 1P, 10D12.3 ) +c$$$ 9993 FORMAT( 1X, ' Row', I4, ':', 1X, 1P, 8D14.5 ) +c$$$ 9992 FORMAT( 1X, ' Row', I4, ':', 1X, 1P, 6D18.9 ) +c$$$ 9991 FORMAT( 1X, ' Row', I4, ':', 1X, 1P, 5D22.13 ) +c$$$ 9990 FORMAT( 1X, ' ' ) +* + RETURN + END diff --git a/src/rigraph/vendor/arpack/dnaitr.f b/src/rigraph/vendor/arpack/dnaitr.f new file mode 100644 index 0000000..8ec2569 --- /dev/null +++ b/src/rigraph/vendor/arpack/dnaitr.f @@ -0,0 +1,840 @@ +c----------------------------------------------------------------------- +c\BeginDoc +c +c\Name: igraphdnaitr +c +c\Description: +c Reverse communication interface for applying NP additional steps to +c a K step nonsymmetric Arnoldi factorization. +c +c Input: OP*V_{k} - V_{k}*H = r_{k}*e_{k}^T +c +c with (V_{k}^T)*B*V_{k} = I, (V_{k}^T)*B*r_{k} = 0. +c +c Output: OP*V_{k+p} - V_{k+p}*H = r_{k+p}*e_{k+p}^T +c +c with (V_{k+p}^T)*B*V_{k+p} = I, (V_{k+p}^T)*B*r_{k+p} = 0. +c +c where OP and B are as in igraphdnaupd. The B-norm of r_{k+p} is also +c computed and returned. +c +c\Usage: +c call igraphdnaitr +c ( IDO, BMAT, N, K, NP, NB, RESID, RNORM, V, LDV, H, LDH, +c IPNTR, WORKD, INFO ) +c +c\Arguments +c IDO Integer. (INPUT/OUTPUT) +c Reverse communication flag. +c ------------------------------------------------------------- +c IDO = 0: first call to the reverse communication interface +c IDO = -1: compute Y = OP * X where +c IPNTR(1) is the pointer into WORK for X, +c IPNTR(2) is the pointer into WORK for Y. +c This is for the restart phase to force the new +c starting vector into the range of OP. +c IDO = 1: compute Y = OP * X where +c IPNTR(1) is the pointer into WORK for X, +c IPNTR(2) is the pointer into WORK for Y, +c IPNTR(3) is the pointer into WORK for B * X. +c IDO = 2: compute Y = B * X where +c IPNTR(1) is the pointer into WORK for X, +c IPNTR(2) is the pointer into WORK for Y. +c IDO = 99: done +c ------------------------------------------------------------- +c When the routine is used in the "shift-and-invert" mode, the +c vector B * Q is already available and do not need to be +c recompute in forming OP * Q. +c +c BMAT Character*1. (INPUT) +c BMAT specifies the type of the matrix B that defines the +c semi-inner product for the operator OP. See igraphdnaupd. +c B = 'I' -> standard eigenvalue problem A*x = lambda*x +c B = 'G' -> generalized eigenvalue problem A*x = lambda*M**x +c +c N Integer. (INPUT) +c Dimension of the eigenproblem. +c +c K Integer. (INPUT) +c Current size of V and H. +c +c NP Integer. (INPUT) +c Number of additional Arnoldi steps to take. +c +c NB Integer. (INPUT) +c Blocksize to be used in the recurrence. +c Only work for NB = 1 right now. The goal is to have a +c program that implement both the block and non-block method. +c +c RESID Double precision array of length N. (INPUT/OUTPUT) +c On INPUT: RESID contains the residual vector r_{k}. +c On OUTPUT: RESID contains the residual vector r_{k+p}. +c +c RNORM Double precision scalar. (INPUT/OUTPUT) +c B-norm of the starting residual on input. +c B-norm of the updated residual r_{k+p} on output. +c +c V Double precision N by K+NP array. (INPUT/OUTPUT) +c On INPUT: V contains the Arnoldi vectors in the first K +c columns. +c On OUTPUT: V contains the new NP Arnoldi vectors in the next +c NP columns. The first K columns are unchanged. +c +c LDV Integer. (INPUT) +c Leading dimension of V exactly as declared in the calling +c program. +c +c H Double precision (K+NP) by (K+NP) array. (INPUT/OUTPUT) +c H is used to store the generated upper Hessenberg matrix. +c +c LDH Integer. (INPUT) +c Leading dimension of H exactly as declared in the calling +c program. +c +c IPNTR Integer array of length 3. (OUTPUT) +c Pointer to mark the starting locations in the WORK for +c vectors used by the Arnoldi iteration. +c ------------------------------------------------------------- +c IPNTR(1): pointer to the current operand vector X. +c IPNTR(2): pointer to the current result vector Y. +c IPNTR(3): pointer to the vector B * X when used in the +c shift-and-invert mode. X is the current operand. +c ------------------------------------------------------------- +c +c WORKD Double precision work array of length 3*N. (REVERSE COMMUNICATION) +c Distributed array to be used in the basic Arnoldi iteration +c for reverse communication. The calling program should not +c use WORKD as temporary workspace during the iteration !!!!!! +c On input, WORKD(1:N) = B*RESID and is used to save some +c computation at the first step. +c +c INFO Integer. (OUTPUT) +c = 0: Normal exit. +c > 0: Size of the spanning invariant subspace of OP found. +c +c\EndDoc +c +c----------------------------------------------------------------------- +c +c\BeginLib +c +c\Local variables: +c xxxxxx real +c +c\References: +c 1. D.C. Sorensen, "Implicit Application of Polynomial Filters in +c a k-Step Arnoldi Method", SIAM J. Matr. Anal. Apps., 13 (1992), +c pp 357-385. +c 2. R.B. Lehoucq, "Analysis and Implementation of an Implicitly +c Restarted Arnoldi Iteration", Rice University Technical Report +c TR95-13, Department of Computational and Applied Mathematics. +c +c\Routines called: +c igraphdgetv0 ARPACK routine to generate the initial vector. +c igraphivout ARPACK utility routine that prints integers. +c igraphsecond ARPACK utility routine for timing. +c igraphdmout ARPACK utility routine that prints matrices +c igraphdvout ARPACK utility routine that prints vectors. +c dlabad LAPACK routine that computes machine constants. +c dlamch LAPACK routine that determines machine constants. +c dlascl LAPACK routine for careful scaling of a matrix. +c dlanhs LAPACK routine that computes various norms of a matrix. +c dgemv Level 2 BLAS routine for matrix vector multiplication. +c daxpy Level 1 BLAS that computes a vector triad. +c dscal Level 1 BLAS that scales a vector. +c dcopy Level 1 BLAS that copies one vector to another . +c ddot Level 1 BLAS that computes the scalar product of two vectors. +c dnrm2 Level 1 BLAS that computes the norm of a vector. +c +c\Author +c Danny Sorensen Phuong Vu +c Richard Lehoucq CRPC / Rice University +c Dept. of Computational & Houston, Texas +c Applied Mathematics +c Rice University +c Houston, Texas +c +c\Revision history: +c xx/xx/92: Version ' 2.4' +c +c\SCCS Information: @(#) +c FILE: naitr.F SID: 2.4 DATE OF SID: 8/27/96 RELEASE: 2 +c +c\Remarks +c The algorithm implemented is: +c +c restart = .false. +c Given V_{k} = [v_{1}, ..., v_{k}], r_{k}; +c r_{k} contains the initial residual vector even for k = 0; +c Also assume that rnorm = || B*r_{k} || and B*r_{k} are already +c computed by the calling program. +c +c betaj = rnorm ; p_{k+1} = B*r_{k} ; +c For j = k+1, ..., k+np Do +c 1) if ( betaj < tol ) stop or restart depending on j. +c ( At present tol is zero ) +c if ( restart ) generate a new starting vector. +c 2) v_{j} = r(j-1)/betaj; V_{j} = [V_{j-1}, v_{j}]; +c p_{j} = p_{j}/betaj +c 3) r_{j} = OP*v_{j} where OP is defined as in igraphdnaupd +c For shift-invert mode p_{j} = B*v_{j} is already available. +c wnorm = || OP*v_{j} || +c 4) Compute the j-th step residual vector. +c w_{j} = V_{j}^T * B * OP * v_{j} +c r_{j} = OP*v_{j} - V_{j} * w_{j} +c H(:,j) = w_{j}; +c H(j,j-1) = rnorm +c rnorm = || r_(j) || +c If (rnorm > 0.717*wnorm) accept step and go back to 1) +c 5) Re-orthogonalization step: +c s = V_{j}'*B*r_{j} +c r_{j} = r_{j} - V_{j}*s; rnorm1 = || r_{j} || +c alphaj = alphaj + s_{j}; +c 6) Iterative refinement step: +c If (rnorm1 > 0.717*rnorm) then +c rnorm = rnorm1 +c accept step and go back to 1) +c Else +c rnorm = rnorm1 +c If this is the first time in step 6), go to 5) +c Else r_{j} lies in the span of V_{j} numerically. +c Set r_{j} = 0 and rnorm = 0; go to 1) +c EndIf +c End Do +c +c\EndLib +c +c----------------------------------------------------------------------- +c + subroutine igraphdnaitr + & (ido, bmat, n, k, np, nb, resid, rnorm, v, ldv, h, ldh, + & ipntr, workd, info) +c +c %----------------------------------------------------% +c | Include files for debugging and timing information | +c %----------------------------------------------------% +c + include 'debug.h' + include 'stat.h' +c +c %------------------% +c | Scalar Arguments | +c %------------------% +c + character bmat*1 + integer ido, info, k, ldh, ldv, n, nb, np + Double precision + & rnorm +c +c %-----------------% +c | Array Arguments | +c %-----------------% +c + integer ipntr(3) + Double precision + & h(ldh,k+np), resid(n), v(ldv,k+np), workd(3*n) +c +c %------------% +c | Parameters | +c %------------% +c + Double precision + & one, zero + parameter (one = 1.0D+0, zero = 0.0D+0) +c +c %---------------% +c | Local Scalars | +c %---------------% +c + logical first, orth1, orth2, rstart, step3, step4 + integer ierr, i, infol, ipj, irj, ivj, iter, itry, j, msglvl, + & jj + Double precision + & betaj, ovfl, temp1, rnorm1, smlnum, tst1, ulp, unfl, + & wnorm + save first, orth1, orth2, rstart, step3, step4, + & ierr, ipj, irj, ivj, iter, itry, j, msglvl, ovfl, + & betaj, rnorm1, smlnum, ulp, unfl, wnorm +c +c %-----------------------% +c | Local Array Arguments | +c %-----------------------% +c + Double precision + & xtemp(2) +c +c %----------------------% +c | External Subroutines | +c %----------------------% +c + external daxpy, dcopy, dscal, dgemv, igraphdgetv0, dlabad, + & igraphdvout, igraphdmout, igraphivout, igraphsecond +c +c %--------------------% +c | External Functions | +c %--------------------% +c + Double precision + & ddot, dnrm2, dlanhs, dlamch + external ddot, dnrm2, dlanhs, dlamch +c +c %---------------------% +c | Intrinsic Functions | +c %---------------------% +c + intrinsic abs, sqrt +c +c %-----------------% +c | Data statements | +c %-----------------% +c + data first / .true. / +c +c %-----------------------% +c | Executable Statements | +c %-----------------------% +c + if (first) then +c +c %-----------------------------------------% +c | Set machine-dependent constants for the | +c | the splitting and deflation criterion. | +c | If norm(H) <= sqrt(OVFL), | +c | overflow should not occur. | +c | REFERENCE: LAPACK subroutine dlahqr | +c %-----------------------------------------% +c + unfl = dlamch( 'safe minimum' ) + ovfl = one / unfl + call dlabad( unfl, ovfl ) + ulp = dlamch( 'precision' ) + smlnum = unfl*( n / ulp ) + first = .false. + end if +c + if (ido .eq. 0) then +c +c %-------------------------------% +c | Initialize timing statistics | +c | & message level for debugging | +c %-------------------------------% +c + call igraphsecond (t0) + msglvl = mnaitr +c +c %------------------------------% +c | Initial call to this routine | +c %------------------------------% +c + info = 0 + step3 = .false. + step4 = .false. + rstart = .false. + orth1 = .false. + orth2 = .false. + j = k + 1 + ipj = 1 + irj = ipj + n + ivj = irj + n + end if +c +c %-------------------------------------------------% +c | When in reverse communication mode one of: | +c | STEP3, STEP4, ORTH1, ORTH2, RSTART | +c | will be .true. when .... | +c | STEP3: return from computing OP*v_{j}. | +c | STEP4: return from computing B-norm of OP*v_{j} | +c | ORTH1: return from computing B-norm of r_{j+1} | +c | ORTH2: return from computing B-norm of | +c | correction to the residual vector. | +c | RSTART: return from OP computations needed by | +c | igraphdgetv0. | +c %-------------------------------------------------% +c + if (step3) go to 50 + if (step4) go to 60 + if (orth1) go to 70 + if (orth2) go to 90 + if (rstart) go to 30 +c +c %-----------------------------% +c | Else this is the first step | +c %-----------------------------% +c +c %--------------------------------------------------------------% +c | | +c | A R N O L D I I T E R A T I O N L O O P | +c | | +c | Note: B*r_{j-1} is already in WORKD(1:N)=WORKD(IPJ:IPJ+N-1) | +c %--------------------------------------------------------------% + + 1000 continue +c + if (msglvl .gt. 1) then + call igraphivout (logfil, 1, [j], ndigit, + & '_naitr: generating Arnoldi vector number') + call igraphdvout (logfil, 1, [rnorm], ndigit, + & '_naitr: B-norm of the current residual is') + end if +c +c %---------------------------------------------------% +c | STEP 1: Check if the B norm of j-th residual | +c | vector is zero. Equivalent to determing whether | +c | an exact j-step Arnoldi factorization is present. | +c %---------------------------------------------------% +c + betaj = rnorm + if (rnorm .gt. zero) go to 40 +c +c %---------------------------------------------------% +c | Invariant subspace found, generate a new starting | +c | vector which is orthogonal to the current Arnoldi | +c | basis and continue the iteration. | +c %---------------------------------------------------% +c + if (msglvl .gt. 0) then + call igraphivout (logfil, 1, [j], ndigit, + & '_naitr: ****** RESTART AT STEP ******') + end if +c +c %---------------------------------------------% +c | ITRY is the loop variable that controls the | +c | maximum amount of times that a restart is | +c | attempted. NRSTRT is used by stat.h | +c %---------------------------------------------% +c + betaj = zero + nrstrt = nrstrt + 1 + itry = 1 + 20 continue + rstart = .true. + ido = 0 + 30 continue +c +c %--------------------------------------% +c | If in reverse communication mode and | +c | RSTART = .true. flow returns here. | +c %--------------------------------------% +c + call igraphdgetv0 (ido, bmat, itry, .false., n, j, v, ldv, + & resid, rnorm, ipntr, workd, ierr) + if (ido .ne. 99) go to 9000 + if (ierr .lt. 0) then + itry = itry + 1 + if (itry .le. 3) go to 20 +c +c %------------------------------------------------% +c | Give up after several restart attempts. | +c | Set INFO to the size of the invariant subspace | +c | which spans OP and exit. | +c %------------------------------------------------% +c + info = j - 1 + call igraphsecond (t1) + tnaitr = tnaitr + (t1 - t0) + ido = 99 + go to 9000 + end if +c + 40 continue +c +c %---------------------------------------------------------% +c | STEP 2: v_{j} = r_{j-1}/rnorm and p_{j} = p_{j}/rnorm | +c | Note that p_{j} = B*r_{j-1}. In order to avoid overflow | +c | when reciprocating a small RNORM, test against lower | +c | machine bound. | +c %---------------------------------------------------------% +c + call dcopy (n, resid, 1, v(1,j), 1) + if (rnorm .ge. unfl) then + temp1 = one / rnorm + call dscal (n, temp1, v(1,j), 1) + call dscal (n, temp1, workd(ipj), 1) + else +c +c %-----------------------------------------% +c | To scale both v_{j} and p_{j} carefully | +c | use LAPACK routine SLASCL | +c %-----------------------------------------% +c + call dlascl ('General', i, i, rnorm, one, n, 1, + & v(1,j), n, infol) + call dlascl ('General', i, i, rnorm, one, n, 1, + & workd(ipj), n, infol) + end if +c +c %------------------------------------------------------% +c | STEP 3: r_{j} = OP*v_{j}; Note that p_{j} = B*v_{j} | +c | Note that this is not quite yet r_{j}. See STEP 4 | +c %------------------------------------------------------% +c + step3 = .true. + nopx = nopx + 1 + call igraphsecond (t2) + call dcopy (n, v(1,j), 1, workd(ivj), 1) + ipntr(1) = ivj + ipntr(2) = irj + ipntr(3) = ipj + ido = 1 +c +c %-----------------------------------% +c | Exit in order to compute OP*v_{j} | +c %-----------------------------------% +c + go to 9000 + 50 continue +c +c %----------------------------------% +c | Back from reverse communication; | +c | WORKD(IRJ:IRJ+N-1) := OP*v_{j} | +c | if step3 = .true. | +c %----------------------------------% +c + call igraphsecond (t3) + tmvopx = tmvopx + (t3 - t2) + + step3 = .false. +c +c %------------------------------------------% +c | Put another copy of OP*v_{j} into RESID. | +c %------------------------------------------% +c + call dcopy (n, workd(irj), 1, resid, 1) +c +c %---------------------------------------% +c | STEP 4: Finish extending the Arnoldi | +c | factorization to length j. | +c %---------------------------------------% +c + call igraphsecond (t2) + if (bmat .eq. 'G') then + nbx = nbx + 1 + step4 = .true. + ipntr(1) = irj + ipntr(2) = ipj + ido = 2 +c +c %-------------------------------------% +c | Exit in order to compute B*OP*v_{j} | +c %-------------------------------------% +c + go to 9000 + else if (bmat .eq. 'I') then + call dcopy (n, resid, 1, workd(ipj), 1) + end if + 60 continue +c +c %----------------------------------% +c | Back from reverse communication; | +c | WORKD(IPJ:IPJ+N-1) := B*OP*v_{j} | +c | if step4 = .true. | +c %----------------------------------% +c + if (bmat .eq. 'G') then + call igraphsecond (t3) + tmvbx = tmvbx + (t3 - t2) + end if +c + step4 = .false. +c +c %-------------------------------------% +c | The following is needed for STEP 5. | +c | Compute the B-norm of OP*v_{j}. | +c %-------------------------------------% +c + if (bmat .eq. 'G') then + wnorm = ddot (n, resid, 1, workd(ipj), 1) + wnorm = sqrt(abs(wnorm)) + else if (bmat .eq. 'I') then + wnorm = dnrm2(n, resid, 1) + end if +c +c %-----------------------------------------% +c | Compute the j-th residual corresponding | +c | to the j step factorization. | +c | Use Classical Gram Schmidt and compute: | +c | w_{j} <- V_{j}^T * B * OP * v_{j} | +c | r_{j} <- OP*v_{j} - V_{j} * w_{j} | +c %-----------------------------------------% +c +c +c %------------------------------------------% +c | Compute the j Fourier coefficients w_{j} | +c | WORKD(IPJ:IPJ+N-1) contains B*OP*v_{j}. | +c %------------------------------------------% +c + call dgemv ('T', n, j, one, v, ldv, workd(ipj), 1, + & zero, h(1,j), 1) +c +c %--------------------------------------% +c | Orthogonalize r_{j} against V_{j}. | +c | RESID contains OP*v_{j}. See STEP 3. | +c %--------------------------------------% +c + call dgemv ('N', n, j, -one, v, ldv, h(1,j), 1, + & one, resid, 1) +c + if (j .gt. 1) h(j,j-1) = betaj +c + call igraphsecond (t4) +c + orth1 = .true. +c + call igraphsecond (t2) + if (bmat .eq. 'G') then + nbx = nbx + 1 + call dcopy (n, resid, 1, workd(irj), 1) + ipntr(1) = irj + ipntr(2) = ipj + ido = 2 +c +c %----------------------------------% +c | Exit in order to compute B*r_{j} | +c %----------------------------------% +c + go to 9000 + else if (bmat .eq. 'I') then + call dcopy (n, resid, 1, workd(ipj), 1) + end if + 70 continue +c +c %---------------------------------------------------% +c | Back from reverse communication if ORTH1 = .true. | +c | WORKD(IPJ:IPJ+N-1) := B*r_{j}. | +c %---------------------------------------------------% +c + if (bmat .eq. 'G') then + call igraphsecond (t3) + tmvbx = tmvbx + (t3 - t2) + end if +c + orth1 = .false. +c +c %------------------------------% +c | Compute the B-norm of r_{j}. | +c %------------------------------% +c + if (bmat .eq. 'G') then + rnorm = ddot (n, resid, 1, workd(ipj), 1) + rnorm = sqrt(abs(rnorm)) + else if (bmat .eq. 'I') then + rnorm = dnrm2(n, resid, 1) + end if +c +c %-----------------------------------------------------------% +c | STEP 5: Re-orthogonalization / Iterative refinement phase | +c | Maximum NITER_ITREF tries. | +c | | +c | s = V_{j}^T * B * r_{j} | +c | r_{j} = r_{j} - V_{j}*s | +c | alphaj = alphaj + s_{j} | +c | | +c | The stopping criteria used for iterative refinement is | +c | discussed in Parlett's book SEP, page 107 and in Gragg & | +c | Reichel ACM TOMS paper; Algorithm 686, Dec. 1990. | +c | Determine if we need to correct the residual. The goal is | +c | to enforce ||v(:,1:j)^T * r_{j}|| .le. eps * || r_{j} || | +c | The following test determines whether the sine of the | +c | angle between OP*x and the computed residual is less | +c | than or equal to 0.717. | +c %-----------------------------------------------------------% +c + if (rnorm .gt. 0.717*wnorm) go to 100 + iter = 0 + nrorth = nrorth + 1 +c +c %---------------------------------------------------% +c | Enter the Iterative refinement phase. If further | +c | refinement is necessary, loop back here. The loop | +c | variable is ITER. Perform a step of Classical | +c | Gram-Schmidt using all the Arnoldi vectors V_{j} | +c %---------------------------------------------------% +c + 80 continue +c + if (msglvl .gt. 2) then + xtemp(1) = wnorm + xtemp(2) = rnorm + call igraphdvout (logfil, 2, xtemp, ndigit, + & '_naitr: re-orthonalization; wnorm and rnorm are') + call igraphdvout (logfil, j, h(1,j), ndigit, + & '_naitr: j-th column of H') + end if +c +c %----------------------------------------------------% +c | Compute V_{j}^T * B * r_{j}. | +c | WORKD(IRJ:IRJ+J-1) = v(:,1:J)'*WORKD(IPJ:IPJ+N-1). | +c %----------------------------------------------------% +c + call dgemv ('T', n, j, one, v, ldv, workd(ipj), 1, + & zero, workd(irj), 1) +c +c %---------------------------------------------% +c | Compute the correction to the residual: | +c | r_{j} = r_{j} - V_{j} * WORKD(IRJ:IRJ+J-1). | +c | The correction to H is v(:,1:J)*H(1:J,1:J) | +c | + v(:,1:J)*WORKD(IRJ:IRJ+J-1)*e'_j. | +c %---------------------------------------------% +c + call dgemv ('N', n, j, -one, v, ldv, workd(irj), 1, + & one, resid, 1) + call daxpy (j, one, workd(irj), 1, h(1,j), 1) +c + orth2 = .true. + call igraphsecond (t2) + if (bmat .eq. 'G') then + nbx = nbx + 1 + call dcopy (n, resid, 1, workd(irj), 1) + ipntr(1) = irj + ipntr(2) = ipj + ido = 2 +c +c %-----------------------------------% +c | Exit in order to compute B*r_{j}. | +c | r_{j} is the corrected residual. | +c %-----------------------------------% +c + go to 9000 + else if (bmat .eq. 'I') then + call dcopy (n, resid, 1, workd(ipj), 1) + end if + 90 continue +c +c %---------------------------------------------------% +c | Back from reverse communication if ORTH2 = .true. | +c %---------------------------------------------------% +c + if (bmat .eq. 'G') then + call igraphsecond (t3) + tmvbx = tmvbx + (t3 - t2) + end if +c +c %-----------------------------------------------------% +c | Compute the B-norm of the corrected residual r_{j}. | +c %-----------------------------------------------------% +c + if (bmat .eq. 'G') then + rnorm1 = ddot (n, resid, 1, workd(ipj), 1) + rnorm1 = sqrt(abs(rnorm1)) + else if (bmat .eq. 'I') then + rnorm1 = dnrm2(n, resid, 1) + end if +c + if (msglvl .gt. 0 .and. iter .gt. 0) then + call igraphivout (logfil, 1, [j], ndigit, + & '_naitr: Iterative refinement for Arnoldi residual') + if (msglvl .gt. 2) then + xtemp(1) = rnorm + xtemp(2) = rnorm1 + call igraphdvout (logfil, 2, xtemp, ndigit, + & '_naitr: iterative refinement ; rnorm and rnorm1 are') + end if + end if +c +c %-----------------------------------------% +c | Determine if we need to perform another | +c | step of re-orthogonalization. | +c %-----------------------------------------% +c + if (rnorm1 .gt. 0.717*rnorm) then +c +c %---------------------------------------% +c | No need for further refinement. | +c | The cosine of the angle between the | +c | corrected residual vector and the old | +c | residual vector is greater than 0.717 | +c | In other words the corrected residual | +c | and the old residual vector share an | +c | angle of less than arcCOS(0.717) | +c %---------------------------------------% +c + rnorm = rnorm1 +c + else +c +c %-------------------------------------------% +c | Another step of iterative refinement step | +c | is required. NITREF is used by stat.h | +c %-------------------------------------------% +c + nitref = nitref + 1 + rnorm = rnorm1 + iter = iter + 1 + if (iter .le. 1) go to 80 +c +c %-------------------------------------------------% +c | Otherwise RESID is numerically in the span of V | +c %-------------------------------------------------% +c + do 95 jj = 1, n + resid(jj) = zero + 95 continue + rnorm = zero + end if +c +c %----------------------------------------------% +c | Branch here directly if iterative refinement | +c | wasn't necessary or after at most NITER_REF | +c | steps of iterative refinement. | +c %----------------------------------------------% +c + 100 continue +c + rstart = .false. + orth2 = .false. +c + call igraphsecond (t5) + titref = titref + (t5 - t4) +c +c %------------------------------------% +c | STEP 6: Update j = j+1; Continue | +c %------------------------------------% +c + j = j + 1 + if (j .gt. k+np) then + call igraphsecond (t1) + tnaitr = tnaitr + (t1 - t0) + ido = 99 + do 110 i = max(1,k), k+np-1 +c +c %--------------------------------------------% +c | Check for splitting and deflation. | +c | Use a standard test as in the QR algorithm | +c | REFERENCE: LAPACK subroutine dlahqr | +c %--------------------------------------------% +c + tst1 = abs( h( i, i ) ) + abs( h( i+1, i+1 ) ) + if( tst1.eq.zero ) + & tst1 = dlanhs( '1', k+np, h, ldh, workd(n+1) ) + if( abs( h( i+1,i ) ).le.max( ulp*tst1, smlnum ) ) + & h(i+1,i) = zero + 110 continue +c + if (msglvl .gt. 2) then + call igraphdmout (logfil, k+np, k+np, h, ldh, ndigit, + & '_naitr: Final upper Hessenberg matrix H of order K+NP') + end if +c + go to 9000 + end if +c +c %--------------------------------------------------------% +c | Loop back to extend the factorization by another step. | +c %--------------------------------------------------------% +c + go to 1000 +c +c %---------------------------------------------------------------% +c | | +c | E N D O F M A I N I T E R A T I O N L O O P | +c | | +c %---------------------------------------------------------------% +c + 9000 continue + return +c +c %---------------% +c | End of igraphdnaitr | +c %---------------% +c + end diff --git a/src/rigraph/vendor/arpack/dnapps.f b/src/rigraph/vendor/arpack/dnapps.f new file mode 100644 index 0000000..247e66b --- /dev/null +++ b/src/rigraph/vendor/arpack/dnapps.f @@ -0,0 +1,647 @@ +c----------------------------------------------------------------------- +c\BeginDoc +c +c\Name: igraphdnapps +c +c\Description: +c Given the Arnoldi factorization +c +c A*V_{k} - V_{k}*H_{k} = r_{k+p}*e_{k+p}^T, +c +c apply NP implicit shifts resulting in +c +c A*(V_{k}*Q) - (V_{k}*Q)*(Q^T* H_{k}*Q) = r_{k+p}*e_{k+p}^T * Q +c +c where Q is an orthogonal matrix which is the product of rotations +c and reflections resulting from the NP bulge chage sweeps. +c The updated Arnoldi factorization becomes: +c +c A*VNEW_{k} - VNEW_{k}*HNEW_{k} = rnew_{k}*e_{k}^T. +c +c\Usage: +c call igraphdnapps +c ( N, KEV, NP, SHIFTR, SHIFTI, V, LDV, H, LDH, RESID, Q, LDQ, +c WORKL, WORKD ) +c +c\Arguments +c N Integer. (INPUT) +c Problem size, i.e. size of matrix A. +c +c KEV Integer. (INPUT/OUTPUT) +c KEV+NP is the size of the input matrix H. +c KEV is the size of the updated matrix HNEW. KEV is only +c updated on ouput when fewer than NP shifts are applied in +c order to keep the conjugate pair together. +c +c NP Integer. (INPUT) +c Number of implicit shifts to be applied. +c +c SHIFTR, Double precision array of length NP. (INPUT) +c SHIFTI Real and imaginary part of the shifts to be applied. +c Upon, entry to igraphdnapps, the shifts must be sorted so that the +c conjugate pairs are in consecutive locations. +c +c V Double precision N by (KEV+NP) array. (INPUT/OUTPUT) +c On INPUT, V contains the current KEV+NP Arnoldi vectors. +c On OUTPUT, V contains the updated KEV Arnoldi vectors +c in the first KEV columns of V. +c +c LDV Integer. (INPUT) +c Leading dimension of V exactly as declared in the calling +c program. +c +c H Double precision (KEV+NP) by (KEV+NP) array. (INPUT/OUTPUT) +c On INPUT, H contains the current KEV+NP by KEV+NP upper +c Hessenber matrix of the Arnoldi factorization. +c On OUTPUT, H contains the updated KEV by KEV upper Hessenberg +c matrix in the KEV leading submatrix. +c +c LDH Integer. (INPUT) +c Leading dimension of H exactly as declared in the calling +c program. +c +c RESID Double precision array of length N. (INPUT/OUTPUT) +c On INPUT, RESID contains the the residual vector r_{k+p}. +c On OUTPUT, RESID is the update residual vector rnew_{k} +c in the first KEV locations. +c +c Q Double precision KEV+NP by KEV+NP work array. (WORKSPACE) +c Work array used to accumulate the rotations and reflections +c during the bulge chase sweep. +c +c LDQ Integer. (INPUT) +c Leading dimension of Q exactly as declared in the calling +c program. +c +c WORKL Double precision work array of length (KEV+NP). (WORKSPACE) +c Private (replicated) array on each PE or array allocated on +c the front end. +c +c WORKD Double precision work array of length 2*N. (WORKSPACE) +c Distributed array used in the application of the accumulated +c orthogonal matrix Q. +c +c\EndDoc +c +c----------------------------------------------------------------------- +c +c\BeginLib +c +c\Local variables: +c xxxxxx real +c +c\References: +c 1. D.C. Sorensen, "Implicit Application of Polynomial Filters in +c a k-Step Arnoldi Method", SIAM J. Matr. Anal. Apps., 13 (1992), +c pp 357-385. +c +c\Routines called: +c igraphivout ARPACK utility routine that prints integers. +c igraphsecond ARPACK utility routine for timing. +c igraphdmout ARPACK utility routine that prints matrices. +c igraphdvout ARPACK utility routine that prints vectors. +c dlabad LAPACK routine that computes machine constants. +c dlacpy LAPACK matrix copy routine. +c dlamch LAPACK routine that determines machine constants. +c dlanhs LAPACK routine that computes various norms of a matrix. +c dlapy2 LAPACK routine to compute sqrt(x**2+y**2) carefully. +c dlarf LAPACK routine that applies Householder reflection to +c a matrix. +c dlarfg LAPACK Householder reflection construction routine. +c dlartg LAPACK Givens rotation construction routine. +c dlaset LAPACK matrix initialization routine. +c dgemv Level 2 BLAS routine for matrix vector multiplication. +c daxpy Level 1 BLAS that computes a vector triad. +c dcopy Level 1 BLAS that copies one vector to another . +c dscal Level 1 BLAS that scales a vector. +c +c\Author +c Danny Sorensen Phuong Vu +c Richard Lehoucq CRPC / Rice University +c Dept. of Computational & Houston, Texas +c Applied Mathematics +c Rice University +c Houston, Texas +c +c\Revision history: +c xx/xx/92: Version ' 2.1' +c +c\SCCS Information: @(#) +c FILE: napps.F SID: 2.3 DATE OF SID: 4/20/96 RELEASE: 2 +c +c\Remarks +c 1. In this version, each shift is applied to all the sublocks of +c the Hessenberg matrix H and not just to the submatrix that it +c comes from. Deflation as in LAPACK routine dlahqr (QR algorithm +c for upper Hessenberg matrices ) is used. +c The subdiagonals of H are enforced to be non-negative. +c +c\EndLib +c +c----------------------------------------------------------------------- +c + subroutine igraphdnapps + & ( n, kev, np, shiftr, shifti, v, ldv, h, ldh, resid, q, ldq, + & workl, workd ) +c +c %----------------------------------------------------% +c | Include files for debugging and timing information | +c %----------------------------------------------------% +c + include 'debug.h' + include 'stat.h' +c +c %------------------% +c | Scalar Arguments | +c %------------------% +c + integer kev, ldh, ldq, ldv, n, np +c +c %-----------------% +c | Array Arguments | +c %-----------------% +c + Double precision + & h(ldh,kev+np), resid(n), shifti(np), shiftr(np), + & v(ldv,kev+np), q(ldq,kev+np), workd(2*n), workl(kev+np) +c +c %------------% +c | Parameters | +c %------------% +c + Double precision + & one, zero + parameter (one = 1.0D+0, zero = 0.0D+0) +c +c %------------------------% +c | Local Scalars & Arrays | +c %------------------------% +c + integer i, iend, ir, istart, j, jj, kplusp, msglvl, nr + logical cconj, first + Double precision + & c, f, g, h11, h12, h21, h22, h32, ovfl, r, s, sigmai, + & sigmar, smlnum, ulp, unfl, u(3), t, tau, tst1 + save first, ovfl, smlnum, ulp, unfl +c +c %----------------------% +c | External Subroutines | +c %----------------------% +c + external daxpy, dcopy, dscal, dlacpy, dlarfg, dlarf, + & dlaset, dlabad, igraphsecond, dlartg +c +c %--------------------% +c | External Functions | +c %--------------------% +c + Double precision + & dlamch, dlanhs, dlapy2 + external dlamch, dlanhs, dlapy2 +c +c %----------------------% +c | Intrinsics Functions | +c %----------------------% +c + intrinsic abs, max, min +c +c %----------------% +c | Data statments | +c %----------------% +c + data first / .true. / +c +c %-----------------------% +c | Executable Statements | +c %-----------------------% +c + if (first) then +c +c %-----------------------------------------------% +c | Set machine-dependent constants for the | +c | stopping criterion. If norm(H) <= sqrt(OVFL), | +c | overflow should not occur. | +c | REFERENCE: LAPACK subroutine dlahqr | +c %-----------------------------------------------% +c + unfl = dlamch( 'safe minimum' ) + ovfl = one / unfl + call dlabad( unfl, ovfl ) + ulp = dlamch( 'precision' ) + smlnum = unfl*( n / ulp ) + first = .false. + end if +c +c %-------------------------------% +c | Initialize timing statistics | +c | & message level for debugging | +c %-------------------------------% +c + call igraphsecond (t0) + msglvl = mnapps + kplusp = kev + np +c +c %--------------------------------------------% +c | Initialize Q to the identity to accumulate | +c | the rotations and reflections | +c %--------------------------------------------% +c + call dlaset ('All', kplusp, kplusp, zero, one, q, ldq) +c +c %----------------------------------------------% +c | Quick return if there are no shifts to apply | +c %----------------------------------------------% +c + if (np .eq. 0) go to 9000 +c +c %----------------------------------------------% +c | Chase the bulge with the application of each | +c | implicit shift. Each shift is applied to the | +c | whole matrix including each block. | +c %----------------------------------------------% +c + cconj = .false. + do 110 jj = 1, np + sigmar = shiftr(jj) + sigmai = shifti(jj) +c + if (msglvl .gt. 2 ) then + call igraphivout (logfil, 1, [jj], ndigit, + & '_napps: shift number.') + call igraphdvout (logfil, 1, [sigmar], ndigit, + & '_napps: The real part of the shift ') + call igraphdvout (logfil, 1, [sigmai], ndigit, + & '_napps: The imaginary part of the shift ') + end if +c +c %-------------------------------------------------% +c | The following set of conditionals is necessary | +c | in order that complex conjugate pairs of shifts | +c | are applied together or not at all. | +c %-------------------------------------------------% +c + if ( cconj ) then +c +c %-----------------------------------------% +c | cconj = .true. means the previous shift | +c | had non-zero imaginary part. | +c %-----------------------------------------% +c + cconj = .false. + go to 110 + else if ( jj .lt. np .and. abs( sigmai ) .gt. zero ) then +c +c %------------------------------------% +c | Start of a complex conjugate pair. | +c %------------------------------------% +c + cconj = .true. + else if ( jj .eq. np .and. abs( sigmai ) .gt. zero ) then +c +c %----------------------------------------------% +c | The last shift has a nonzero imaginary part. | +c | Don't apply it; thus the order of the | +c | compressed H is order KEV+1 since only np-1 | +c | were applied. | +c %----------------------------------------------% +c + kev = kev + 1 + go to 110 + end if + istart = 1 + 20 continue +c +c %--------------------------------------------------% +c | if sigmai = 0 then | +c | Apply the jj-th shift ... | +c | else | +c | Apply the jj-th and (jj+1)-th together ... | +c | (Note that jj < np at this point in the code) | +c | end | +c | to the current block of H. The next do loop | +c | determines the current block ; | +c %--------------------------------------------------% +c + do 30 i = istart, kplusp-1 +c +c %----------------------------------------% +c | Check for splitting and deflation. Use | +c | a standard test as in the QR algorithm | +c | REFERENCE: LAPACK subroutine dlahqr | +c %----------------------------------------% +c + tst1 = abs( h( i, i ) ) + abs( h( i+1, i+1 ) ) + if( tst1.eq.zero ) + & tst1 = dlanhs( '1', kplusp-jj+1, h, ldh, workl ) + if( abs( h( i+1,i ) ).le.max( ulp*tst1, smlnum ) ) then + if (msglvl .gt. 0) then + call igraphivout (logfil, 1, [i], ndigit, + & '_napps: matrix splitting at row/column no.') + call igraphivout (logfil, 1, [jj], ndigit, + & '_napps: matrix splitting with shift number.') + call igraphdvout (logfil, 1, h(i+1,i), ndigit, + & '_napps: off diagonal element.') + end if + iend = i + h(i+1,i) = zero + go to 40 + end if + 30 continue + iend = kplusp + 40 continue +c + if (msglvl .gt. 2) then + call igraphivout (logfil, 1, [istart], ndigit, + & '_napps: Start of current block ') + call igraphivout (logfil, 1, [iend], ndigit, + & '_napps: End of current block ') + end if +c +c %------------------------------------------------% +c | No reason to apply a shift to block of order 1 | +c %------------------------------------------------% +c + if ( istart .eq. iend ) go to 100 +c +c %------------------------------------------------------% +c | If istart + 1 = iend then no reason to apply a | +c | complex conjugate pair of shifts on a 2 by 2 matrix. | +c %------------------------------------------------------% +c + if ( istart + 1 .eq. iend .and. abs( sigmai ) .gt. zero ) + & go to 100 +c + h11 = h(istart,istart) + h21 = h(istart+1,istart) + if ( abs( sigmai ) .le. zero ) then +c +c %---------------------------------------------% +c | Real-valued shift ==> apply single shift QR | +c %---------------------------------------------% +c + f = h11 - sigmar + g = h21 +c + do 80 i = istart, iend-1 +c +c %-----------------------------------------------------% +c | Contruct the plane rotation G to zero out the bulge | +c %-----------------------------------------------------% +c + call dlartg (f, g, c, s, r) + if (i .gt. istart) then +c +c %-------------------------------------------% +c | The following ensures that h(1:iend-1,1), | +c | the first iend-2 off diagonal of elements | +c | H, remain non negative. | +c %-------------------------------------------% +c + if (r .lt. zero) then + r = -r + c = -c + s = -s + end if + h(i,i-1) = r + h(i+1,i-1) = zero + end if +c +c %---------------------------------------------% +c | Apply rotation to the left of H; H <- G'*H | +c %---------------------------------------------% +c + do 50 j = i, kplusp + t = c*h(i,j) + s*h(i+1,j) + h(i+1,j) = -s*h(i,j) + c*h(i+1,j) + h(i,j) = t + 50 continue +c +c %---------------------------------------------% +c | Apply rotation to the right of H; H <- H*G | +c %---------------------------------------------% +c + do 60 j = 1, min(i+2,iend) + t = c*h(j,i) + s*h(j,i+1) + h(j,i+1) = -s*h(j,i) + c*h(j,i+1) + h(j,i) = t + 60 continue +c +c %----------------------------------------------------% +c | Accumulate the rotation in the matrix Q; Q <- Q*G | +c %----------------------------------------------------% +c + do 70 j = 1, min( j+jj, kplusp ) + t = c*q(j,i) + s*q(j,i+1) + q(j,i+1) = - s*q(j,i) + c*q(j,i+1) + q(j,i) = t + 70 continue +c +c %---------------------------% +c | Prepare for next rotation | +c %---------------------------% +c + if (i .lt. iend-1) then + f = h(i+1,i) + g = h(i+2,i) + end if + 80 continue +c +c %-----------------------------------% +c | Finished applying the real shift. | +c %-----------------------------------% +c + else +c +c %----------------------------------------------------% +c | Complex conjugate shifts ==> apply double shift QR | +c %----------------------------------------------------% +c + h12 = h(istart,istart+1) + h22 = h(istart+1,istart+1) + h32 = h(istart+2,istart+1) +c +c %---------------------------------------------------------% +c | Compute 1st column of (H - shift*I)*(H - conj(shift)*I) | +c %---------------------------------------------------------% +c + s = 2.0*sigmar + t = dlapy2 ( sigmar, sigmai ) + u(1) = ( h11 * (h11 - s) + t * t ) / h21 + h12 + u(2) = h11 + h22 - s + u(3) = h32 +c + do 90 i = istart, iend-1 +c + nr = min ( 3, iend-i+1 ) +c +c %-----------------------------------------------------% +c | Construct Householder reflector G to zero out u(1). | +c | G is of the form I - tau*( 1 u )' * ( 1 u' ). | +c %-----------------------------------------------------% +c + call dlarfg ( nr, u(1), u(2), 1, tau ) +c + if (i .gt. istart) then + h(i,i-1) = u(1) + h(i+1,i-1) = zero + if (i .lt. iend-1) h(i+2,i-1) = zero + end if + u(1) = one +c +c %--------------------------------------% +c | Apply the reflector to the left of H | +c %--------------------------------------% +c + call dlarf ('Left', nr, kplusp-i+1, u, 1, tau, + & h(i,i), ldh, workl) +c +c %---------------------------------------% +c | Apply the reflector to the right of H | +c %---------------------------------------% +c + ir = min ( i+3, iend ) + call dlarf ('Right', ir, nr, u, 1, tau, + & h(1,i), ldh, workl) +c +c %-----------------------------------------------------% +c | Accumulate the reflector in the matrix Q; Q <- Q*G | +c %-----------------------------------------------------% +c + call dlarf ('Right', kplusp, nr, u, 1, tau, + & q(1,i), ldq, workl) +c +c %----------------------------% +c | Prepare for next reflector | +c %----------------------------% +c + if (i .lt. iend-1) then + u(1) = h(i+1,i) + u(2) = h(i+2,i) + if (i .lt. iend-2) u(3) = h(i+3,i) + end if +c + 90 continue +c +c %--------------------------------------------% +c | Finished applying a complex pair of shifts | +c | to the current block | +c %--------------------------------------------% +c + end if +c + 100 continue +c +c %---------------------------------------------------------% +c | Apply the same shift to the next block if there is any. | +c %---------------------------------------------------------% +c + istart = iend + 1 + if (iend .lt. kplusp) go to 20 +c +c %---------------------------------------------% +c | Loop back to the top to get the next shift. | +c %---------------------------------------------% +c + 110 continue +c +c %--------------------------------------------------% +c | Perform a similarity transformation that makes | +c | sure that H will have non negative sub diagonals | +c %--------------------------------------------------% +c + do 120 j=1,kev + if ( h(j+1,j) .lt. zero ) then + call dscal( kplusp-j+1, -one, h(j+1,j), ldh ) + call dscal( min(j+2, kplusp), -one, h(1,j+1), 1 ) + call dscal( min(j+np+1,kplusp), -one, q(1,j+1), 1 ) + end if + 120 continue +c + do 130 i = 1, kev +c +c %--------------------------------------------% +c | Final check for splitting and deflation. | +c | Use a standard test as in the QR algorithm | +c | REFERENCE: LAPACK subroutine dlahqr | +c %--------------------------------------------% +c + tst1 = abs( h( i, i ) ) + abs( h( i+1, i+1 ) ) + if( tst1.eq.zero ) + & tst1 = dlanhs( '1', kev, h, ldh, workl ) + if( h( i+1,i ) .le. max( ulp*tst1, smlnum ) ) + & h(i+1,i) = zero + 130 continue +c +c %-------------------------------------------------% +c | Compute the (kev+1)-st column of (V*Q) and | +c | temporarily store the result in WORKD(N+1:2*N). | +c | This is needed in the residual update since we | +c | cannot GUARANTEE that the corresponding entry | +c | of H would be zero as in exact arithmetic. | +c %-------------------------------------------------% +c + if (h(kev+1,kev) .gt. zero) + & call dgemv ('N', n, kplusp, one, v, ldv, q(1,kev+1), 1, zero, + & workd(n+1), 1) +c +c %----------------------------------------------------------% +c | Compute column 1 to kev of (V*Q) in backward order | +c | taking advantage of the upper Hessenberg structure of Q. | +c %----------------------------------------------------------% +c + do 140 i = 1, kev + call dgemv ('N', n, kplusp-i+1, one, v, ldv, + & q(1,kev-i+1), 1, zero, workd, 1) + call dcopy (n, workd, 1, v(1,kplusp-i+1), 1) + 140 continue +c +c %-------------------------------------------------% +c | Move v(:,kplusp-kev+1:kplusp) into v(:,1:kev). | +c %-------------------------------------------------% +c + call dlacpy ('A', n, kev, v(1,kplusp-kev+1), ldv, v, ldv) +c +c %--------------------------------------------------------------% +c | Copy the (kev+1)-st column of (V*Q) in the appropriate place | +c %--------------------------------------------------------------% +c + if (h(kev+1,kev) .gt. zero) + & call dcopy (n, workd(n+1), 1, v(1,kev+1), 1) +c +c %-------------------------------------% +c | Update the residual vector: | +c | r <- sigmak*r + betak*v(:,kev+1) | +c | where | +c | sigmak = (e_{kplusp}'*Q)*e_{kev} | +c | betak = e_{kev+1}'*H*e_{kev} | +c %-------------------------------------% +c + call dscal (n, q(kplusp,kev), resid, 1) + if (h(kev+1,kev) .gt. zero) + & call daxpy (n, h(kev+1,kev), v(1,kev+1), 1, resid, 1) +c + if (msglvl .gt. 1) then + call igraphdvout (logfil, 1, q(kplusp,kev), ndigit, + & '_napps: sigmak = (e_{kev+p}^T*Q)*e_{kev}') + call igraphdvout (logfil, 1, h(kev+1,kev), ndigit, + & '_napps: betak = e_{kev+1}^T*H*e_{kev}') + call igraphivout (logfil, 1, [kev], ndigit, + & '_napps: Order of the final Hessenberg matrix ') + if (msglvl .gt. 2) then + call igraphdmout (logfil, kev, kev, h, ldh, ndigit, + & '_napps: updated Hessenberg matrix H for next iteration') + end if +c + end if +c + 9000 continue + call igraphsecond (t1) + tnapps = tnapps + (t1 - t0) +c + return +c +c %---------------% +c | End of igraphdnapps | +c %---------------% +c + end diff --git a/src/rigraph/vendor/arpack/dnaup2.f b/src/rigraph/vendor/arpack/dnaup2.f new file mode 100644 index 0000000..e060689 --- /dev/null +++ b/src/rigraph/vendor/arpack/dnaup2.f @@ -0,0 +1,838 @@ +c\BeginDoc +c +c\Name: igraphdnaup2 +c +c\Description: +c Intermediate level interface called by igraphdnaupd. +c +c\Usage: +c call igraphdnaup2 +c ( IDO, BMAT, N, WHICH, NEV, NP, TOL, RESID, MODE, IUPD, +c ISHIFT, MXITER, V, LDV, H, LDH, RITZR, RITZI, BOUNDS, +c Q, LDQ, WORKL, IPNTR, WORKD, INFO ) +c +c\Arguments +c +c IDO, BMAT, N, WHICH, NEV, TOL, RESID: same as defined in igraphdnaupd. +c MODE, ISHIFT, MXITER: see the definition of IPARAM in igraphdnaupd. +c +c NP Integer. (INPUT/OUTPUT) +c Contains the number of implicit shifts to apply during +c each Arnoldi iteration. +c If ISHIFT=1, NP is adjusted dynamically at each iteration +c to accelerate convergence and prevent stagnation. +c This is also roughly equal to the number of matrix-vector +c products (involving the operator OP) per Arnoldi iteration. +c The logic for adjusting is contained within the current +c subroutine. +c If ISHIFT=0, NP is the number of shifts the user needs +c to provide via reverse comunication. 0 < NP < NCV-NEV. +c NP may be less than NCV-NEV for two reasons. The first, is +c to keep complex conjugate pairs of "wanted" Ritz values +c together. The igraphsecond, is that a leading block of the current +c upper Hessenberg matrix has split off and contains "unwanted" +c Ritz values. +c Upon termination of the IRA iteration, NP contains the number +c of "converged" wanted Ritz values. +c +c IUPD Integer. (INPUT) +c IUPD .EQ. 0: use explicit restart instead implicit update. +c IUPD .NE. 0: use implicit update. +c +c V Double precision N by (NEV+NP) array. (INPUT/OUTPUT) +c The Arnoldi basis vectors are returned in the first NEV +c columns of V. +c +c LDV Integer. (INPUT) +c Leading dimension of V exactly as declared in the calling +c program. +c +c H Double precision (NEV+NP) by (NEV+NP) array. (OUTPUT) +c H is used to store the generated upper Hessenberg matrix +c +c LDH Integer. (INPUT) +c Leading dimension of H exactly as declared in the calling +c program. +c +c RITZR, Double precision arrays of length NEV+NP. (OUTPUT) +c RITZI RITZR(1:NEV) (resp. RITZI(1:NEV)) contains the real (resp. +c imaginary) part of the computed Ritz values of OP. +c +c BOUNDS Double precision array of length NEV+NP. (OUTPUT) +c BOUNDS(1:NEV) contain the error bounds corresponding to +c the computed Ritz values. +c +c Q Double precision (NEV+NP) by (NEV+NP) array. (WORKSPACE) +c Private (replicated) work array used to accumulate the +c rotation in the shift application step. +c +c LDQ Integer. (INPUT) +c Leading dimension of Q exactly as declared in the calling +c program. +c +c WORKL Double precision work array of length at least +c (NEV+NP)**2 + 3*(NEV+NP). (INPUT/WORKSPACE) +c Private (replicated) array on each PE or array allocated on +c the front end. It is used in shifts calculation, shifts +c application and convergence checking. +c +c On exit, the last 3*(NEV+NP) locations of WORKL contain +c the Ritz values (real,imaginary) and associated Ritz +c estimates of the current Hessenberg matrix. They are +c listed in the same order as returned from igraphdneigh. +c +c If ISHIFT .EQ. O and IDO .EQ. 3, the first 2*NP locations +c of WORKL are used in reverse communication to hold the user +c supplied shifts. +c +c IPNTR Integer array of length 3. (OUTPUT) +c Pointer to mark the starting locations in the WORKD for +c vectors used by the Arnoldi iteration. +c ------------------------------------------------------------- +c IPNTR(1): pointer to the current operand vector X. +c IPNTR(2): pointer to the current result vector Y. +c IPNTR(3): pointer to the vector B * X when used in the +c shift-and-invert mode. X is the current operand. +c ------------------------------------------------------------- +c +c WORKD Double precision work array of length 3*N. (WORKSPACE) +c Distributed array to be used in the basic Arnoldi iteration +c for reverse communication. The user should not use WORKD +c as temporary workspace during the iteration !!!!!!!!!! +c See Data Distribution Note in DNAUPD. +c +c INFO Integer. (INPUT/OUTPUT) +c If INFO .EQ. 0, a randomly initial residual vector is used. +c If INFO .NE. 0, RESID contains the initial residual vector, +c possibly from a previous run. +c Error flag on output. +c = 0: Normal return. +c = 1: Maximum number of iterations taken. +c All possible eigenvalues of OP has been found. +c NP returns the number of converged Ritz values. +c = 2: No shifts could be applied. +c = -8: Error return from LAPACK eigenvalue calculation; +c This should never happen. +c = -9: Starting vector is zero. +c = -9999: Could not build an Arnoldi factorization. +c Size that was built in returned in NP. +c +c\EndDoc +c +c----------------------------------------------------------------------- +c +c\BeginLib +c +c\Local variables: +c xxxxxx real +c +c\References: +c 1. D.C. Sorensen, "Implicit Application of Polynomial Filters in +c a k-Step Arnoldi Method", SIAM J. Matr. Anal. Apps., 13 (1992), +c pp 357-385. +c 2. R.B. Lehoucq, "Analysis and Implementation of an Implicitly +c Restarted Arnoldi Iteration", Rice University Technical Report +c TR95-13, Department of Computational and Applied Mathematics. +c +c\Routines called: +c igraphdgetv0 ARPACK initial vector generation routine. +c igraphdnaitr ARPACK Arnoldi factorization routine. +c igraphdnapps ARPACK application of implicit shifts routine. +c igraphdnconv ARPACK convergence of Ritz values routine. +c igraphdneigh ARPACK compute Ritz values and error bounds routine. +c igraphdngets ARPACK reorder Ritz values and error bounds routine. +c igraphdsortc ARPACK sorting routine. +c igraphivout ARPACK utility routine that prints integers. +c igraphsecond ARPACK utility routine for timing. +c igraphdmout ARPACK utility routine that prints matrices +c igraphdvout ARPACK utility routine that prints vectors. +c dlamch LAPACK routine that determines machine constants. +c dlapy2 LAPACK routine to compute sqrt(x**2+y**2) carefully. +c dcopy Level 1 BLAS that copies one vector to another . +c ddot Level 1 BLAS that computes the scalar product of two vectors. +c dnrm2 Level 1 BLAS that computes the norm of a vector. +c dswap Level 1 BLAS that swaps two vectors. +c +c\Author +c Danny Sorensen Phuong Vu +c Richard Lehoucq CRPC / Rice University +c Dept. of Computational & Houston, Texas +c Applied Mathematics +c Rice University +c Houston, Texas +c +c\SCCS Information: @(#) +c FILE: naup2.F SID: 2.4 DATE OF SID: 7/30/96 RELEASE: 2 +c +c\Remarks +c 1. None +c +c\EndLib +c +c----------------------------------------------------------------------- +c + subroutine igraphdnaup2 + & ( 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 ) +c +c %----------------------------------------------------% +c | Include files for debugging and timing information | +c %----------------------------------------------------% +c + include 'debug.h' + include 'stat.h' +c +c %------------------% +c | Scalar Arguments | +c %------------------% +c + character bmat*1, which*2 + integer ido, info, ishift, iupd, mode, ldh, ldq, ldv, mxiter, + & n, nev, np + Double precision + & tol +c +c %-----------------% +c | Array Arguments | +c %-----------------% +c + integer ipntr(13) + Double precision + & bounds(nev+np), h(ldh,nev+np), q(ldq,nev+np), resid(n), + & ritzi(nev+np), ritzr(nev+np), v(ldv,nev+np), + & workd(3*n), workl( (nev+np)*(nev+np+3) ) +c +c %------------% +c | Parameters | +c %------------% +c + Double precision + & one, zero + parameter (one = 1.0D+0, zero = 0.0D+0) +c +c %---------------% +c | Local Scalars | +c %---------------% +c + character wprime*2 + logical cnorm, getv0, initv, update, ushift + integer ierr, iter, j, kplusp, msglvl, nconv, nevbef, nev0, + & np0, nptemp, numcnv + Double precision + & rnorm, temp, eps23 +c +c %-----------------------% +c | Local array arguments | +c %-----------------------% +c + integer kp(4) + save +c +c %----------------------% +c | External Subroutines | +c %----------------------% +c + external dcopy, igraphdgetv0, igraphdnaitr, igraphdnconv, + & igraphdneigh, igraphdngets, igraphdnapps, + & igraphdvout, igraphivout, igraphsecond +c +c %--------------------% +c | External Functions | +c %--------------------% +c + Double precision + & ddot, dnrm2, dlapy2, dlamch + external ddot, dnrm2, dlapy2, dlamch +c +c %---------------------% +c | Intrinsic Functions | +c %---------------------% +c + intrinsic min, max, abs, sqrt +c +c %-----------------------% +c | Executable Statements | +c %-----------------------% +c + if (ido .eq. 0) then +c + call igraphsecond (t0) +c + msglvl = mnaup2 +c +c %-------------------------------------% +c | Get the machine dependent constant. | +c %-------------------------------------% +c + eps23 = dlamch('Epsilon-Machine') + eps23 = eps23**(2.0D+0 / 3.0D+0) +c + nev0 = nev + np0 = np +c +c %-------------------------------------% +c | kplusp is the bound on the largest | +c | Lanczos factorization built. | +c | nconv is the current number of | +c | "converged" eigenvlues. | +c | iter is the counter on the current | +c | iteration step. | +c %-------------------------------------% +c + kplusp = nev + np + nconv = 0 + iter = 0 +c +c %---------------------------------------% +c | Set flags for computing the first NEV | +c | steps of the Arnoldi factorization. | +c %---------------------------------------% +c + getv0 = .true. + update = .false. + ushift = .false. + cnorm = .false. +c + if (info .ne. 0) then +c +c %--------------------------------------------% +c | User provides the initial residual vector. | +c %--------------------------------------------% +c + initv = .true. + info = 0 + else + initv = .false. + end if + end if +c +c %---------------------------------------------% +c | Get a possibly random starting vector and | +c | force it into the range of the operator OP. | +c %---------------------------------------------% +c + 10 continue +c + if (getv0) then + call igraphdgetv0 (ido, bmat, 1, initv, n, 1, v, ldv, resid, + & rnorm, ipntr, workd, info) +c + if (ido .ne. 99) go to 9000 +c + if (rnorm .eq. zero) then +c +c %-----------------------------------------% +c | The initial vector is zero. Error exit. | +c %-----------------------------------------% +c + info = -9 + go to 1100 + end if + getv0 = .false. + ido = 0 + end if +c +c %-----------------------------------% +c | Back from reverse communication : | +c | continue with update step | +c %-----------------------------------% +c + if (update) go to 20 +c +c %-------------------------------------------% +c | Back from computing user specified shifts | +c %-------------------------------------------% +c + if (ushift) go to 50 +c +c %-------------------------------------% +c | Back from computing residual norm | +c | at the end of the current iteration | +c %-------------------------------------% +c + if (cnorm) go to 100 +c +c %----------------------------------------------------------% +c | Compute the first NEV steps of the Arnoldi factorization | +c %----------------------------------------------------------% +c + call igraphdnaitr (ido, bmat, n, 0, nev, mode, resid, rnorm, v, + & ldv, h, ldh, ipntr, workd, info) +c +c %---------------------------------------------------% +c | ido .ne. 99 implies use of reverse communication | +c | to compute operations involving OP and possibly B | +c %---------------------------------------------------% +c + if (ido .ne. 99) go to 9000 +c + if (info .gt. 0) then + np = info + mxiter = iter + info = -9999 + go to 1200 + end if +c +c %--------------------------------------------------------------% +c | | +c | M A I N ARNOLDI I T E R A T I O N L O O P | +c | Each iteration implicitly restarts the Arnoldi | +c | factorization in place. | +c | | +c %--------------------------------------------------------------% +c + 1000 continue +c + iter = iter + 1 +c + if (msglvl .gt. 0) then + call igraphivout (logfil, 1, [iter], ndigit, + & '_naup2: **** Start of major iteration number ****') + end if +c +c %-----------------------------------------------------------% +c | Compute NP additional steps of the Arnoldi factorization. | +c | Adjust NP since NEV might have been updated by last call | +c | to the shift application routine igraphdnapps. | +c %-----------------------------------------------------------% +c + np = kplusp - nev +c + if (msglvl .gt. 1) then + call igraphivout (logfil, 1, [nev], ndigit, + & '_naup2: The length of the current Arnoldi factorization') + call igraphivout (logfil, 1, [np], ndigit, + & '_naup2: Extend the Arnoldi factorization by') + end if +c +c %-----------------------------------------------------------% +c | Compute NP additional steps of the Arnoldi factorization. | +c %-----------------------------------------------------------% +c + ido = 0 + 20 continue + update = .true. +c + call igraphdnaitr (ido, bmat, n, nev, np, mode, resid, rnorm, + & v, ldv, h, ldh, ipntr, workd, info) +c +c %---------------------------------------------------% +c | ido .ne. 99 implies use of reverse communication | +c | to compute operations involving OP and possibly B | +c %---------------------------------------------------% +c + if (ido .ne. 99) go to 9000 +c + if (info .gt. 0) then + np = info + mxiter = iter + info = -9999 + go to 1200 + end if + update = .false. +c + if (msglvl .gt. 1) then + call igraphdvout (logfil, 1, [rnorm], ndigit, + & '_naup2: Corresponding B-norm of the residual') + end if +c +c %--------------------------------------------------------% +c | Compute the eigenvalues and corresponding error bounds | +c | of the current upper Hessenberg matrix. | +c %--------------------------------------------------------% +c + call igraphdneigh (rnorm, kplusp, h, ldh, ritzr, ritzi, bounds, + & q, ldq, workl, ierr) +c + if (ierr .ne. 0) then + info = -8 + go to 1200 + end if +c +c %----------------------------------------------------% +c | Make a copy of eigenvalues and corresponding error | +c | bounds obtained from igraphdneigh. | +c %----------------------------------------------------% +c + call dcopy(kplusp, ritzr, 1, workl(kplusp**2+1), 1) + call dcopy(kplusp, ritzi, 1, workl(kplusp**2+kplusp+1), 1) + call dcopy(kplusp, bounds, 1, workl(kplusp**2+2*kplusp+1), 1) +c +c %---------------------------------------------------% +c | Select the wanted Ritz values and their bounds | +c | to be used in the convergence test. | +c | The wanted part of the spectrum and corresponding | +c | error bounds are in the last NEV loc. of RITZR, | +c | RITZI and BOUNDS respectively. The variables NEV | +c | and NP may be updated if the NEV-th wanted Ritz | +c | value has a non zero imaginary part. In this case | +c | NEV is increased by one and NP decreased by one. | +c | NOTE: The last two arguments of igraphdngets are no | +c | longer used as of version 2.1. | +c %---------------------------------------------------% +c + nev = nev0 + np = np0 + numcnv = nev + call igraphdngets (ishift, which, nev, np, ritzr, ritzi, + & bounds, workl, workl(np+1)) + if (nev .eq. nev0+1) numcnv = nev0+1 +c +c %-------------------% +c | Convergence test. | +c %-------------------% +c + call dcopy (nev, bounds(np+1), 1, workl(2*np+1), 1) + call igraphdnconv (nev, ritzr(np+1), ritzi(np+1), + & workl(2*np+1), tol, nconv) +c + if (msglvl .gt. 2) then + kp(1) = nev + kp(2) = np + kp(3) = numcnv + kp(4) = nconv + call igraphivout (logfil, 4, kp, ndigit, + & '_naup2: NEV, NP, NUMCNV, NCONV are') + call igraphdvout (logfil, kplusp, ritzr, ndigit, + & '_naup2: Real part of the eigenvalues of H') + call igraphdvout (logfil, kplusp, ritzi, ndigit, + & '_naup2: Imaginary part of the eigenvalues of H') + call igraphdvout (logfil, kplusp, bounds, ndigit, + & '_naup2: Ritz estimates of the current NCV Ritz values') + end if +c +c %---------------------------------------------------------% +c | Count the number of unwanted Ritz values that have zero | +c | Ritz estimates. If any Ritz estimates are equal to zero | +c | then a leading block of H of order equal to at least | +c | the number of Ritz values with zero Ritz estimates has | +c | split off. None of these Ritz values may be removed by | +c | shifting. Decrease NP the number of shifts to apply. If | +c | no shifts may be applied, then prepare to exit | +c %---------------------------------------------------------% +c + nptemp = np + do 30 j=1, nptemp + if (bounds(j) .eq. zero) then + np = np - 1 + nev = nev + 1 + end if + 30 continue +c + if ( (nconv .ge. numcnv) .or. + & (iter .gt. mxiter) .or. + & (np .eq. 0) ) then +c + if (msglvl .gt. 4) then + call igraphdvout(logfil, kplusp, workl(kplusp**2+1), + & ndigit, + & '_naup2: Real part of the eig computed by _neigh:') + call igraphdvout(logfil, kplusp, + & workl(kplusp**2+kplusp+1), ndigit, + & '_naup2: Imag part of the eig computed by _neigh:') + call igraphdvout(logfil, kplusp, + & workl(kplusp**2+kplusp*2+1), ndigit, + & '_naup2: Ritz eistmates computed by _neigh:') + end if +c +c %------------------------------------------------% +c | Prepare to exit. Put the converged Ritz values | +c | and corresponding bounds in RITZ(1:NCONV) and | +c | BOUNDS(1:NCONV) respectively. Then sort. Be | +c | careful when NCONV > NP | +c %------------------------------------------------% +c +c %------------------------------------------% +c | Use h( 3,1 ) as storage to communicate | +c | rnorm to _neupd if needed | +c %------------------------------------------% + + h(3,1) = rnorm +c +c %----------------------------------------------% +c | To be consistent with igraphdngets, we first do a | +c | pre-processing sort in order to keep complex | +c | conjugate pairs together. This is similar | +c | to the pre-processing sort used in igraphdngets | +c | except that the sort is done in the opposite | +c | order. | +c %----------------------------------------------% +c + if (which .eq. 'LM') wprime = 'SR' + if (which .eq. 'SM') wprime = 'LR' + if (which .eq. 'LR') wprime = 'SM' + if (which .eq. 'SR') wprime = 'LM' + if (which .eq. 'LI') wprime = 'SM' + if (which .eq. 'SI') wprime = 'LM' +c + call igraphdsortc (wprime, .true., kplusp, ritzr, ritzi, + & bounds) +c +c %----------------------------------------------% +c | Now sort Ritz values so that converged Ritz | +c | values appear within the first NEV locations | +c | of ritzr, ritzi and bounds, and the most | +c | desired one appears at the front. | +c %----------------------------------------------% +c + if (which .eq. 'LM') wprime = 'SM' + if (which .eq. 'SM') wprime = 'LM' + if (which .eq. 'LR') wprime = 'SR' + if (which .eq. 'SR') wprime = 'LR' + if (which .eq. 'LI') wprime = 'SI' + if (which .eq. 'SI') wprime = 'LI' +c + call igraphdsortc(wprime, .true., kplusp, ritzr, ritzi, + & bounds) +c +c %--------------------------------------------------% +c | Scale the Ritz estimate of each Ritz value | +c | by 1 / max(eps23,magnitude of the Ritz value). | +c %--------------------------------------------------% +c + do 35 j = 1, nev0 + temp = max(eps23,dlapy2(ritzr(j), + & ritzi(j))) + bounds(j) = bounds(j)/temp + 35 continue +c +c %----------------------------------------------------% +c | Sort the Ritz values according to the scaled Ritz | +c | esitmates. This will push all the converged ones | +c | towards the front of ritzr, ritzi, bounds | +c | (in the case when NCONV < NEV.) | +c %----------------------------------------------------% +c + wprime = 'LR' + call igraphdsortc(wprime, .true., nev0, bounds, ritzr, + & ritzi) +c +c %----------------------------------------------% +c | Scale the Ritz estimate back to its original | +c | value. | +c %----------------------------------------------% +c + do 40 j = 1, nev0 + temp = max(eps23, dlapy2(ritzr(j), + & ritzi(j))) + bounds(j) = bounds(j)*temp + 40 continue +c +c %------------------------------------------------% +c | Sort the converged Ritz values again so that | +c | the "threshold" value appears at the front of | +c | ritzr, ritzi and bound. | +c %------------------------------------------------% +c + call igraphdsortc(which, .true., nconv, ritzr, ritzi, + & bounds) +c + if (msglvl .gt. 1) then + call igraphdvout (logfil, kplusp, ritzr, ndigit, + & '_naup2: Sorted real part of the eigenvalues') + call igraphdvout (logfil, kplusp, ritzi, ndigit, + & '_naup2: Sorted imaginary part of the eigenvalues') + call igraphdvout (logfil, kplusp, bounds, ndigit, + & '_naup2: Sorted ritz estimates.') + end if +c +c %------------------------------------% +c | Max iterations have been exceeded. | +c %------------------------------------% +c + if (iter .gt. mxiter .and. nconv .lt. numcnv) info = 1 +c +c %---------------------% +c | No shifts to apply. | +c %---------------------% +c + if (np .eq. 0 .and. nconv .lt. numcnv) info = 2 +c + np = nconv + go to 1100 +c + else if ( (nconv .lt. numcnv) .and. (ishift .eq. 1) ) then +c +c %-------------------------------------------------% +c | Do not have all the requested eigenvalues yet. | +c | To prevent possible stagnation, adjust the size | +c | of NEV. | +c %-------------------------------------------------% +c + nevbef = nev + nev = nev + min(nconv, np/2) + if (nev .eq. 1 .and. kplusp .ge. 6) then + nev = kplusp / 2 + else if (nev .eq. 1 .and. kplusp .gt. 3) then + nev = 2 + end if + np = kplusp - nev +c +c %---------------------------------------% +c | If the size of NEV was just increased | +c | resort the eigenvalues. | +c %---------------------------------------% +c + if (nevbef .lt. nev) + & call igraphdngets (ishift, which, nev, np, ritzr, ritzi, + & bounds, workl, workl(np+1)) +c + end if +c + if (msglvl .gt. 0) then + call igraphivout (logfil, 1, [nconv], ndigit, + & '_naup2: no. of "converged" Ritz values at this iter.') + if (msglvl .gt. 1) then + kp(1) = nev + kp(2) = np + call igraphivout (logfil, 2, kp, ndigit, + & '_naup2: NEV and NP are') + call igraphdvout (logfil, nev, ritzr(np+1), ndigit, + & '_naup2: "wanted" Ritz values -- real part') + call igraphdvout (logfil, nev, ritzi(np+1), ndigit, + & '_naup2: "wanted" Ritz values -- imag part') + call igraphdvout (logfil, nev, bounds(np+1), ndigit, + & '_naup2: Ritz estimates of the "wanted" values ') + end if + end if +c + if (ishift .eq. 0) then +c +c %-------------------------------------------------------% +c | User specified shifts: reverse comminucation to | +c | compute the shifts. They are returned in the first | +c | 2*NP locations of WORKL. | +c %-------------------------------------------------------% +c + ushift = .true. + ido = 3 + go to 9000 + end if +c + 50 continue +c +c %------------------------------------% +c | Back from reverse communication; | +c | User specified shifts are returned | +c | in WORKL(1:2*NP) | +c %------------------------------------% +c + ushift = .false. +c + if ( ishift .eq. 0 ) then +c +c %----------------------------------% +c | Move the NP shifts from WORKL to | +c | RITZR, RITZI to free up WORKL | +c | for non-exact shift case. | +c %----------------------------------% +c + call dcopy (np, workl, 1, ritzr, 1) + call dcopy (np, workl(np+1), 1, ritzi, 1) + end if +c + if (msglvl .gt. 2) then + call igraphivout (logfil, 1, [np], ndigit, + & '_naup2: The number of shifts to apply ') + call igraphdvout (logfil, np, ritzr, ndigit, + & '_naup2: Real part of the shifts') + call igraphdvout (logfil, np, ritzi, ndigit, + & '_naup2: Imaginary part of the shifts') + if ( ishift .eq. 1 ) + & call igraphdvout (logfil, np, bounds, ndigit, + & '_naup2: Ritz estimates of the shifts') + end if +c +c %---------------------------------------------------------% +c | Apply the NP implicit shifts by QR bulge chasing. | +c | Each shift is applied to the whole upper Hessenberg | +c | matrix H. | +c | The first 2*N locations of WORKD are used as workspace. | +c %---------------------------------------------------------% +c + call igraphdnapps (n, nev, np, ritzr, ritzi, v, ldv, + & h, ldh, resid, q, ldq, workl, workd) +c +c %---------------------------------------------% +c | Compute the B-norm of the updated residual. | +c | Keep B*RESID in WORKD(1:N) to be used in | +c | the first step of the next call to igraphdnaitr. | +c %---------------------------------------------% +c + cnorm = .true. + call igraphsecond (t2) + if (bmat .eq. 'G') then + nbx = nbx + 1 + call dcopy (n, resid, 1, workd(n+1), 1) + ipntr(1) = n + 1 + ipntr(2) = 1 + ido = 2 +c +c %----------------------------------% +c | Exit in order to compute B*RESID | +c %----------------------------------% +c + go to 9000 + else if (bmat .eq. 'I') then + call dcopy (n, resid, 1, workd, 1) + end if +c + 100 continue +c +c %----------------------------------% +c | Back from reverse communication; | +c | WORKD(1:N) := B*RESID | +c %----------------------------------% +c + if (bmat .eq. 'G') then + call igraphsecond (t3) + tmvbx = tmvbx + (t3 - t2) + end if +c + if (bmat .eq. 'G') then + rnorm = ddot (n, resid, 1, workd, 1) + rnorm = sqrt(abs(rnorm)) + else if (bmat .eq. 'I') then + rnorm = dnrm2(n, resid, 1) + end if + cnorm = .false. +c + if (msglvl .gt. 2) then + call igraphdvout (logfil, 1, [rnorm], ndigit, + & '_naup2: B-norm of residual for compressed factorization') + call igraphdmout (logfil, nev, nev, h, ldh, ndigit, + & '_naup2: Compressed upper Hessenberg matrix H') + end if +c + go to 1000 +c +c %---------------------------------------------------------------% +c | | +c | E N D O F M A I N I T E R A T I O N L O O P | +c | | +c %---------------------------------------------------------------% +c + 1100 continue +c + mxiter = iter + nev = numcnv +c + 1200 continue + ido = 99 +c +c %------------% +c | Error Exit | +c %------------% +c + call igraphsecond (t1) + tnaup2 = t1 - t0 +c + 9000 continue +c +c %---------------% +c | End of igraphdnaup2 | +c %---------------% +c + return + end diff --git a/src/rigraph/vendor/arpack/dnaupd.f b/src/rigraph/vendor/arpack/dnaupd.f new file mode 100644 index 0000000..4133022 --- /dev/null +++ b/src/rigraph/vendor/arpack/dnaupd.f @@ -0,0 +1,655 @@ +c\BeginDoc +c +c\Name: igraphdnaupd +c +c\Description: +c Reverse communication interface for the Implicitly Restarted Arnoldi +c iteration. This subroutine computes approximations to a few eigenpairs +c of a linear operator "OP" with respect to a semi-inner product defined by +c a symmetric positive semi-definite real matrix B. B may be the identity +c matrix. NOTE: If the linear operator "OP" is real and symmetric +c with respect to the real positive semi-definite symmetric matrix B, +c i.e. B*OP = (OP')*B, then subroutine ssaupd should be used instead. +c +c The computed approximate eigenvalues are called Ritz values and +c the corresponding approximate eigenvectors are called Ritz vectors. +c +c igraphdnaupd is usually called iteratively to solve one of the +c following problems: +c +c Mode 1: A*x = lambda*x. +c ===> OP = A and B = I. +c +c Mode 2: A*x = lambda*M*x, M symmetric positive definite +c ===> OP = inv[M]*A and B = M. +c ===> (If M can be factored see remark 3 below) +c +c Mode 3: A*x = lambda*M*x, M symmetric semi-definite +c ===> OP = Real_Part{ inv[A - sigma*M]*M } and B = M. +c ===> shift-and-invert mode (in real arithmetic) +c If OP*x = amu*x, then +c amu = 1/2 * [ 1/(lambda-sigma) + 1/(lambda-conjg(sigma)) ]. +c Note: If sigma is real, i.e. imaginary part of sigma is zero; +c Real_Part{ inv[A - sigma*M]*M } == inv[A - sigma*M]*M +c amu == 1/(lambda-sigma). +c +c Mode 4: A*x = lambda*M*x, M symmetric semi-definite +c ===> OP = Imaginary_Part{ inv[A - sigma*M]*M } and B = M. +c ===> shift-and-invert mode (in real arithmetic) +c If OP*x = amu*x, then +c amu = 1/2i * [ 1/(lambda-sigma) - 1/(lambda-conjg(sigma)) ]. +c +c Both mode 3 and 4 give the same enhancement to eigenvalues close to +c the (complex) shift sigma. However, as lambda goes to infinity, +c the operator OP in mode 4 dampens the eigenvalues more strongly than +c does OP defined in mode 3. +c +c NOTE: The action of w <- inv[A - sigma*M]*v or w <- inv[M]*v +c should be accomplished either by a direct method +c using a sparse matrix factorization and solving +c +c [A - sigma*M]*w = v or M*w = v, +c +c or through an iterative method for solving these +c systems. If an iterative method is used, the +c convergence test must be more stringent than +c the accuracy requirements for the eigenvalue +c approximations. +c +c\Usage: +c call igraphdnaupd +c ( IDO, BMAT, N, WHICH, NEV, TOL, RESID, NCV, V, LDV, IPARAM, +c IPNTR, WORKD, WORKL, LWORKL, INFO ) +c +c\Arguments +c IDO Integer. (INPUT/OUTPUT) +c Reverse communication flag. IDO must be zero on the first +c call to igraphdnaupd. IDO will be set internally to +c indicate the type of operation to be performed. Control is +c then given back to the calling routine which has the +c responsibility to carry out the requested operation and call +c igraphdnaupd with the result. The operand is given in +c WORKD(IPNTR(1)), the result must be put in WORKD(IPNTR(2)). +c ------------------------------------------------------------- +c IDO = 0: first call to the reverse communication interface +c IDO = -1: compute Y = OP * X where +c IPNTR(1) is the pointer into WORKD for X, +c IPNTR(2) is the pointer into WORKD for Y. +c This is for the initialization phase to force the +c starting vector into the range of OP. +c IDO = 1: compute Y = OP * X where +c IPNTR(1) is the pointer into WORKD for X, +c IPNTR(2) is the pointer into WORKD for Y. +c In mode 3 and 4, the vector B * X is already +c available in WORKD(ipntr(3)). It does not +c need to be recomputed in forming OP * X. +c IDO = 2: compute Y = B * X where +c IPNTR(1) is the pointer into WORKD for X, +c IPNTR(2) is the pointer into WORKD for Y. +c IDO = 3: compute the IPARAM(8) real and imaginary parts +c of the shifts where INPTR(14) is the pointer +c into WORKL for placing the shifts. See Remark +c 5 below. +c IDO = 99: done +c ------------------------------------------------------------- +c +c BMAT Character*1. (INPUT) +c BMAT specifies the type of the matrix B that defines the +c semi-inner product for the operator OP. +c BMAT = 'I' -> standard eigenvalue problem A*x = lambda*x +c BMAT = 'G' -> generalized eigenvalue problem A*x = lambda*B*x +c +c N Integer. (INPUT) +c Dimension of the eigenproblem. +c +c WHICH Character*2. (INPUT) +c 'LM' -> want the NEV eigenvalues of largest magnitude. +c 'SM' -> want the NEV eigenvalues of smallest magnitude. +c 'LR' -> want the NEV eigenvalues of largest real part. +c 'SR' -> want the NEV eigenvalues of smallest real part. +c 'LI' -> want the NEV eigenvalues of largest imaginary part. +c 'SI' -> want the NEV eigenvalues of smallest imaginary part. +c +c NEV Integer. (INPUT) +c Number of eigenvalues of OP to be computed. 0 < NEV < N-1. +c +c TOL Double precision scalar. (INPUT) +c Stopping criterion: the relative accuracy of the Ritz value +c is considered acceptable if BOUNDS(I) .LE. TOL*ABS(RITZ(I)) +c where ABS(RITZ(I)) is the magnitude when RITZ(I) is complex. +c DEFAULT = DLAMCH('EPS') (machine precision as computed +c by the LAPACK auxiliary subroutine DLAMCH). +c +c RESID Double precision array of length N. (INPUT/OUTPUT) +c On INPUT: +c If INFO .EQ. 0, a random initial residual vector is used. +c If INFO .NE. 0, RESID contains the initial residual vector, +c possibly from a previous run. +c On OUTPUT: +c RESID contains the final residual vector. +c +c NCV Integer. (INPUT) +c Number of columns of the matrix V. NCV must satisfy the two +c inequalities 2 <= NCV-NEV and NCV <= N. +c This will indicate how many Arnoldi vectors are generated +c at each iteration. After the startup phase in which NEV +c Arnoldi vectors are generated, the algorithm generates +c approximately NCV-NEV Arnoldi vectors at each subsequent update +c iteration. Most of the cost in generating each Arnoldi vector is +c in the matrix-vector operation OP*x. +c NOTE: 2 <= NCV-NEV in order that complex conjugate pairs of Ritz +c values are kept together. (See remark 4 below) +c +c V Double precision array N by NCV. (OUTPUT) +c Contains the final set of Arnoldi basis vectors. +c +c LDV Integer. (INPUT) +c Leading dimension of V exactly as declared in the calling program. +c +c IPARAM Integer array of length 11. (INPUT/OUTPUT) +c IPARAM(1) = ISHIFT: method for selecting the implicit shifts. +c The shifts selected at each iteration are used to restart +c the Arnoldi iteration in an implicit fashion. +c ------------------------------------------------------------- +c ISHIFT = 0: the shifts are provided by the user via +c reverse communication. The real and imaginary +c parts of the NCV eigenvalues of the Hessenberg +c matrix H are returned in the part of the WORKL +c array corresponding to RITZR and RITZI. See remark +c 5 below. +c ISHIFT = 1: exact shifts with respect to the current +c Hessenberg matrix H. This is equivalent to +c restarting the iteration with a starting vector +c that is a linear combination of approximate Schur +c vectors associated with the "wanted" Ritz values. +c ------------------------------------------------------------- +c +c IPARAM(2) = No longer referenced. +c +c IPARAM(3) = MXITER +c On INPUT: maximum number of Arnoldi update iterations allowed. +c On OUTPUT: actual number of Arnoldi update iterations taken. +c +c IPARAM(4) = NB: blocksize to be used in the recurrence. +c The code currently works only for NB = 1. +c +c IPARAM(5) = NCONV: number of "converged" Ritz values. +c This represents the number of Ritz values that satisfy +c the convergence criterion. +c +c IPARAM(6) = IUPD +c No longer referenced. Implicit restarting is ALWAYS used. +c +c IPARAM(7) = MODE +c On INPUT determines what type of eigenproblem is being solved. +c Must be 1,2,3,4; See under \Description of igraphdnaupd for the +c four modes available. +c +c IPARAM(8) = NP +c When ido = 3 and the user provides shifts through reverse +c communication (IPARAM(1)=0), igraphdnaupd returns NP, the number +c of shifts the user is to provide. 0 < NP <=NCV-NEV. See Remark +c 5 below. +c +c IPARAM(9) = NUMOP, IPARAM(10) = NUMOPB, IPARAM(11) = NUMREO, +c OUTPUT: NUMOP = total number of OP*x operations, +c NUMOPB = total number of B*x operations if BMAT='G', +c NUMREO = total number of steps of re-orthogonalization. +c +c IPNTR Integer array of length 14. (OUTPUT) +c Pointer to mark the starting locations in the WORKD and WORKL +c arrays for matrices/vectors used by the Arnoldi iteration. +c ------------------------------------------------------------- +c IPNTR(1): pointer to the current operand vector X in WORKD. +c IPNTR(2): pointer to the current result vector Y in WORKD. +c IPNTR(3): pointer to the vector B * X in WORKD when used in +c the shift-and-invert mode. +c IPNTR(4): pointer to the next available location in WORKL +c that is untouched by the program. +c IPNTR(5): pointer to the NCV by NCV upper Hessenberg matrix +c H in WORKL. +c IPNTR(6): pointer to the real part of the ritz value array +c RITZR in WORKL. +c IPNTR(7): pointer to the imaginary part of the ritz value array +c RITZI in WORKL. +c IPNTR(8): pointer to the Ritz estimates in array WORKL associated +c with the Ritz values located in RITZR and RITZI in WORKL. +c +c IPNTR(14): pointer to the NP shifts in WORKL. See Remark 5 below. +c +c Note: IPNTR(9:13) is only referenced by igraphdneupd. See Remark 2 below. +c +c IPNTR(9): pointer to the real part of the NCV RITZ values of the +c original system. +c IPNTR(10): pointer to the imaginary part of the NCV RITZ values of +c the original system. +c IPNTR(11): pointer to the NCV corresponding error bounds. +c IPNTR(12): pointer to the NCV by NCV upper quasi-triangular +c Schur matrix for H. +c IPNTR(13): pointer to the NCV by NCV matrix of eigenvectors +c of the upper Hessenberg matrix H. Only referenced by +c igraphdneupd if RVEC = .TRUE. See Remark 2 below. +c ------------------------------------------------------------- +c +c WORKD Double precision work array of length 3*N. (REVERSE COMMUNICATION) +c Distributed array to be used in the basic Arnoldi iteration +c for reverse communication. The user should not use WORKD +c as temporary workspace during the iteration. Upon termination +c WORKD(1:N) contains B*RESID(1:N). If an invariant subspace +c associated with the converged Ritz values is desired, see remark +c 2 below, subroutine igraphdneupd uses this output. +c See Data Distribution Note below. +c +c WORKL Double precision work array of length LWORKL. (OUTPUT/WORKSPACE) +c Private (replicated) array on each PE or array allocated on +c the front end. See Data Distribution Note below. +c +c LWORKL Integer. (INPUT) +c LWORKL must be at least 3*NCV**2 + 6*NCV. +c +c INFO Integer. (INPUT/OUTPUT) +c If INFO .EQ. 0, a randomly initial residual vector is used. +c If INFO .NE. 0, RESID contains the initial residual vector, +c possibly from a previous run. +c Error flag on output. +c = 0: Normal exit. +c = 1: Maximum number of iterations taken. +c All possible eigenvalues of OP has been found. IPARAM(5) +c returns the number of wanted converged Ritz values. +c = 2: No longer an informational error. Deprecated starting +c with release 2 of ARPACK. +c = 3: No shifts could be applied during a cycle of the +c Implicitly restarted Arnoldi iteration. One possibility +c is to increase the size of NCV relative to NEV. +c See remark 4 below. +c = -1: N must be positive. +c = -2: NEV must be positive. +c = -3: NCV-NEV >= 2 and less than or equal to N. +c = -4: The maximum number of Arnoldi update iteration +c must be greater than zero. +c = -5: WHICH must be one of 'LM', 'SM', 'LR', 'SR', 'LI', 'SI' +c = -6: BMAT must be one of 'I' or 'G'. +c = -7: Length of private work array is not sufficient. +c = -8: Error return from LAPACK eigenvalue calculation; +c = -9: Starting vector is zero. +c = -10: IPARAM(7) must be 1,2,3,4. +c = -11: IPARAM(7) = 1 and BMAT = 'G' are incompatable. +c = -12: IPARAM(1) must be equal to 0 or 1. +c = -9999: Could not build an Arnoldi factorization. +c IPARAM(5) returns the size of the current Arnoldi +c factorization. +c +c\Remarks +c 1. The computed Ritz values are approximate eigenvalues of OP. The +c selection of WHICH should be made with this in mind when +c Mode = 3 and 4. After convergence, approximate eigenvalues of the +c original problem may be obtained with the ARPACK subroutine igraphdneupd. +c +c 2. If a basis for the invariant subspace corresponding to the converged Ritz +c values is needed, the user must call igraphdneupd immediately following +c completion of igraphdnaupd. This is new starting with release 2 of ARPACK. +c +c 3. If M can be factored into a Cholesky factorization M = LL' +c then Mode = 2 should not be selected. Instead one should use +c Mode = 1 with OP = inv(L)*A*inv(L'). Appropriate triangular +c linear systems should be solved with L and L' rather +c than computing inverses. After convergence, an approximate +c eigenvector z of the original problem is recovered by solving +c L'z = x where x is a Ritz vector of OP. +c +c 4. At present there is no a-priori analysis to guide the selection +c of NCV relative to NEV. The only formal requrement is that NCV > NEV + 2. +c However, it is recommended that NCV .ge. 2*NEV+1. If many problems of +c the same type are to be solved, one should experiment with increasing +c NCV while keeping NEV fixed for a given test problem. This will +c usually decrease the required number of OP*x operations but it +c also increases the work and storage required to maintain the orthogonal +c basis vectors. The optimal "cross-over" with respect to CPU time +c is problem dependent and must be determined empirically. +c See Chapter 8 of Reference 2 for further information. +c +c 5. When IPARAM(1) = 0, and IDO = 3, the user needs to provide the +c NP = IPARAM(8) real and imaginary parts of the shifts in locations +c real part imaginary part +c ----------------------- -------------- +c 1 WORKL(IPNTR(14)) WORKL(IPNTR(14)+NP) +c 2 WORKL(IPNTR(14)+1) WORKL(IPNTR(14)+NP+1) +c . . +c . . +c . . +c NP WORKL(IPNTR(14)+NP-1) WORKL(IPNTR(14)+2*NP-1). +c +c Only complex conjugate pairs of shifts may be applied and the pairs +c must be placed in consecutive locations. The real part of the +c eigenvalues of the current upper Hessenberg matrix are located in +c WORKL(IPNTR(6)) through WORKL(IPNTR(6)+NCV-1) and the imaginary part +c in WORKL(IPNTR(7)) through WORKL(IPNTR(7)+NCV-1). They are ordered +c according to the order defined by WHICH. The complex conjugate +c pairs are kept together and the associated Ritz estimates are located in +c WORKL(IPNTR(8)), WORKL(IPNTR(8)+1), ... , WORKL(IPNTR(8)+NCV-1). +c +c----------------------------------------------------------------------- +c +c\Data Distribution Note: +c +c Fortran-D syntax: +c ================ +c Double precision resid(n), v(ldv,ncv), workd(3*n), workl(lworkl) +c decompose d1(n), d2(n,ncv) +c align resid(i) with d1(i) +c align v(i,j) with d2(i,j) +c align workd(i) with d1(i) range (1:n) +c align workd(i) with d1(i-n) range (n+1:2*n) +c align workd(i) with d1(i-2*n) range (2*n+1:3*n) +c distribute d1(block), d2(block,:) +c replicated workl(lworkl) +c +c Cray MPP syntax: +c =============== +c Double precision resid(n), v(ldv,ncv), workd(n,3), workl(lworkl) +c shared resid(block), v(block,:), workd(block,:) +c replicated workl(lworkl) +c +c CM2/CM5 syntax: +c ============== +c +c----------------------------------------------------------------------- +c +c include 'ex-nonsym.doc' +c +c----------------------------------------------------------------------- +c +c\BeginLib +c +c\Local variables: +c xxxxxx real +c +c\References: +c 1. D.C. Sorensen, "Implicit Application of Polynomial Filters in +c a k-Step Arnoldi Method", SIAM J. Matr. Anal. Apps., 13 (1992), +c pp 357-385. +c 2. R.B. Lehoucq, "Analysis and Implementation of an Implicitly +c Restarted Arnoldi Iteration", Rice University Technical Report +c TR95-13, Department of Computational and Applied Mathematics. +c 3. B.N. Parlett & Y. Saad, "Complex Shift and Invert Strategies for +c Real Matrices", Linear Algebra and its Applications, vol 88/89, +c pp 575-595, (1987). +c +c\Routines called: +c igraphdnaup2 ARPACK routine that implements the Implicitly Restarted +c Arnoldi Iteration. +c igraphivout ARPACK utility routine that prints integers. +c igraphsecond ARPACK utility routine for timing. +c igraphdvout ARPACK utility routine that prints vectors. +c dlamch LAPACK routine that determines machine constants. +c +c\Author +c Danny Sorensen Phuong Vu +c Richard Lehoucq CRPC / Rice University +c Dept. of Computational & Houston, Texas +c Applied Mathematics +c Rice University +c Houston, Texas +c +c\Revision history: +c 12/16/93: Version '1.1' +c +c\SCCS Information: @(#) +c FILE: naupd.F SID: 2.5 DATE OF SID: 8/27/96 RELEASE: 2 +c +c\Remarks +c +c\EndLib +c +c----------------------------------------------------------------------- +c + subroutine igraphdnaupd + & ( ido, bmat, n, which, nev, tol, resid, ncv, v, ldv, iparam, + & ipntr, workd, workl, lworkl, info ) +c +c %----------------------------------------------------% +c | Include files for debugging and timing information | +c %----------------------------------------------------% +c + include 'debug.h' + include 'stat.h' +c +c %------------------% +c | Scalar Arguments | +c %------------------% +c + character bmat*1, which*2 + integer ido, info, ldv, lworkl, n, ncv, nev + Double precision + & tol +c +c %-----------------% +c | Array Arguments | +c %-----------------% +c + integer iparam(11), ipntr(14) + Double precision + & resid(n), v(ldv,ncv), workd(3*n), workl(lworkl) +c +c %------------% +c | Parameters | +c %------------% +c + Double precision + & one, zero + parameter (one = 1.0D+0, zero = 0.0D+0) +c +c %---------------% +c | Local Scalars | +c %---------------% +c + integer bounds, ierr, ih, iq, ishift, iupd, iw, + & ldh, ldq, levec, mode, msglvl, mxiter, nb, + & nev0, next, np, ritzi, ritzr, j + save bounds, ih, iq, ishift, iupd, iw, ldh, ldq, + & levec, mode, msglvl, mxiter, nb, nev0, next, + & np, ritzi, ritzr +c +c %----------------------% +c | External Subroutines | +c %----------------------% +c + external igraphdnaup2, igraphdvout, igraphivout, + & igraphsecond, igraphdstatn +c +c %--------------------% +c | External Functions | +c %--------------------% +c + Double precision + & dlamch + external dlamch +c +c %-----------------------% +c | Executable Statements | +c %-----------------------% +c + if (ido .eq. 0) then +c +c %-------------------------------% +c | Initialize timing statistics | +c | & message level for debugging | +c %-------------------------------% +c + call igraphdstatn + call igraphsecond (t0) + msglvl = mnaupd +c +c %----------------% +c | Error checking | +c %----------------% +c + ierr = 0 + ishift = iparam(1) + levec = iparam(2) + mxiter = iparam(3) + nb = iparam(4) +c +c %--------------------------------------------% +c | Revision 2 performs only implicit restart. | +c %--------------------------------------------% +c + iupd = 1 + mode = iparam(7) +c + if (n .le. 0) then + ierr = -1 + else if (nev .le. 0) then + ierr = -2 + else if (ncv .le. nev+1 .or. ncv .gt. n) then + ierr = -3 + else if (mxiter .le. 0) then + ierr = -4 + else if (which .ne. 'LM' .and. + & which .ne. 'SM' .and. + & which .ne. 'LR' .and. + & which .ne. 'SR' .and. + & which .ne. 'LI' .and. + & which .ne. 'SI') then + ierr = -5 + else if (bmat .ne. 'I' .and. bmat .ne. 'G') then + ierr = -6 + else if (lworkl .lt. 3*ncv**2 + 6*ncv) then + ierr = -7 + else if (mode .lt. 1 .or. mode .gt. 5) then + ierr = -10 + else if (mode .eq. 1 .and. bmat .eq. 'G') then + ierr = -11 + else if (ishift .lt. 0 .or. ishift .gt. 1) then + ierr = -12 + end if +c +c %------------% +c | Error Exit | +c %------------% +c + if (ierr .ne. 0) then + info = ierr + ido = 99 + go to 9000 + end if +c +c %------------------------% +c | Set default parameters | +c %------------------------% +c + if (nb .le. 0) nb = 1 + if (tol .le. zero) tol = dlamch('EpsMach') +c +c %----------------------------------------------% +c | NP is the number of additional steps to | +c | extend the length NEV Lanczos factorization. | +c | NEV0 is the local variable designating the | +c | size of the invariant subspace desired. | +c %----------------------------------------------% +c + np = ncv - nev + nev0 = nev +c +c %-----------------------------% +c | Zero out internal workspace | +c %-----------------------------% +c + do 10 j = 1, 3*ncv**2 + 6*ncv + workl(j) = zero + 10 continue +c +c %-------------------------------------------------------------% +c | Pointer into WORKL for address of H, RITZ, BOUNDS, Q | +c | etc... and the remaining workspace. | +c | Also update pointer to be used on output. | +c | Memory is laid out as follows: | +c | workl(1:ncv*ncv) := generated Hessenberg matrix | +c | workl(ncv*ncv+1:ncv*ncv+2*ncv) := real and imaginary | +c | parts of ritz values | +c | workl(ncv*ncv+2*ncv+1:ncv*ncv+3*ncv) := error bounds | +c | workl(ncv*ncv+3*ncv+1:2*ncv*ncv+3*ncv) := rotation matrix Q | +c | workl(2*ncv*ncv+3*ncv+1:3*ncv*ncv+6*ncv) := workspace | +c | The final workspace is needed by subroutine igraphdneigh called | +c | by igraphdnaup2. Subroutine igraphdneigh calls LAPACK routines for | +c | calculating eigenvalues and the last row of the eigenvector | +c | matrix. | +c %-------------------------------------------------------------% +c + ldh = ncv + ldq = ncv + ih = 1 + ritzr = ih + ldh*ncv + ritzi = ritzr + ncv + bounds = ritzi + ncv + iq = bounds + ncv + iw = iq + ldq*ncv + next = iw + ncv**2 + 3*ncv +c + ipntr(4) = next + ipntr(5) = ih + ipntr(6) = ritzr + ipntr(7) = ritzi + ipntr(8) = bounds + ipntr(14) = iw +c + end if +c +c %-------------------------------------------------------% +c | Carry out the Implicitly restarted Arnoldi Iteration. | +c %-------------------------------------------------------% +c + call igraphdnaup2 + & ( ido, bmat, n, which, nev0, np, tol, resid, mode, iupd, + & ishift, mxiter, v, ldv, workl(ih), ldh, workl(ritzr), + & workl(ritzi), workl(bounds), workl(iq), ldq, workl(iw), + & ipntr, workd, info ) +c +c %--------------------------------------------------% +c | ido .ne. 99 implies use of reverse communication | +c | to compute operations involving OP or shifts. | +c %--------------------------------------------------% +c + if (ido .eq. 3) iparam(8) = np + if (ido .ne. 99) go to 9000 +c + iparam(3) = mxiter + iparam(5) = np + iparam(9) = nopx + iparam(10) = nbx + iparam(11) = nrorth +c +c %------------------------------------% +c | Exit if there was an informational | +c | error within igraphdnaup2. | +c %------------------------------------% +c + if (info .lt. 0) go to 9000 + if (info .eq. 2) info = 3 +c + if (msglvl .gt. 0) then + call igraphivout (logfil, 1, [mxiter], ndigit, + & '_naupd: Number of update iterations taken') + call igraphivout (logfil, 1, [np], ndigit, + & '_naupd: Number of wanted "converged" Ritz values') + call igraphdvout (logfil, np, workl(ritzr), ndigit, + & '_naupd: Real part of the final Ritz values') + call igraphdvout (logfil, np, workl(ritzi), ndigit, + & '_naupd: Imaginary part of the final Ritz values') + call igraphdvout (logfil, np, workl(bounds), ndigit, + & '_naupd: Associated Ritz estimates') + end if +c + call igraphsecond (t1) + tnaupd = t1 - t0 +c +c + 9000 continue +c + return +c +c %---------------% +c | End of igraphdnaupd | +c %---------------% +c + end diff --git a/src/rigraph/vendor/arpack/dnconv.f b/src/rigraph/vendor/arpack/dnconv.f new file mode 100644 index 0000000..4735159 --- /dev/null +++ b/src/rigraph/vendor/arpack/dnconv.f @@ -0,0 +1,146 @@ +c----------------------------------------------------------------------- +c\BeginDoc +c +c\Name: igraphdnconv +c +c\Description: +c Convergence testing for the nonsymmetric Arnoldi eigenvalue routine. +c +c\Usage: +c call igraphdnconv +c ( N, RITZR, RITZI, BOUNDS, TOL, NCONV ) +c +c\Arguments +c N Integer. (INPUT) +c Number of Ritz values to check for convergence. +c +c RITZR, Double precision arrays of length N. (INPUT) +c RITZI Real and imaginary parts of the Ritz values to be checked +c for convergence. + +c BOUNDS Double precision array of length N. (INPUT) +c Ritz estimates for the Ritz values in RITZR and RITZI. +c +c TOL Double precision scalar. (INPUT) +c Desired backward error for a Ritz value to be considered +c "converged". +c +c NCONV Integer scalar. (OUTPUT) +c Number of "converged" Ritz values. +c +c\EndDoc +c +c----------------------------------------------------------------------- +c +c\BeginLib +c +c\Local variables: +c xxxxxx real +c +c\Routines called: +c igraphsecond ARPACK utility routine for timing. +c dlamch LAPACK routine that determines machine constants. +c dlapy2 LAPACK routine to compute sqrt(x**2+y**2) carefully. +c +c\Author +c Danny Sorensen Phuong Vu +c Richard Lehoucq CRPC / Rice University +c Dept. of Computational & Houston, Texas +c Applied Mathematics +c Rice University +c Houston, Texas +c +c\Revision history: +c xx/xx/92: Version ' 2.1' +c +c\SCCS Information: @(#) +c FILE: nconv.F SID: 2.3 DATE OF SID: 4/20/96 RELEASE: 2 +c +c\Remarks +c 1. xxxx +c +c\EndLib +c +c----------------------------------------------------------------------- +c + subroutine igraphdnconv (n, ritzr, ritzi, bounds, tol, nconv) +c +c %----------------------------------------------------% +c | Include files for debugging and timing information | +c %----------------------------------------------------% +c + include 'debug.h' + include 'stat.h' +c +c %------------------% +c | Scalar Arguments | +c %------------------% +c + integer n, nconv + Double precision + & tol +c +c %-----------------% +c | Array Arguments | +c %-----------------% + + Double precision + & ritzr(n), ritzi(n), bounds(n) +c +c %---------------% +c | Local Scalars | +c %---------------% +c + integer i + Double precision + & temp, eps23 +c +c %--------------------% +c | External Functions | +c %--------------------% +c + Double precision + & dlapy2, dlamch + external dlapy2, dlamch + +c %-----------------------% +c | Executable Statements | +c %-----------------------% +c +c %-------------------------------------------------------------% +c | Convergence test: unlike in the symmetric code, I am not | +c | using things like refined error bounds and gap condition | +c | because I don't know the exact equivalent concept. | +c | | +c | Instead the i-th Ritz value is considered "converged" when: | +c | | +c | bounds(i) .le. ( TOL * | ritz | ) | +c | | +c | for some appropriate choice of norm. | +c %-------------------------------------------------------------% +c + call igraphsecond (t0) +c +c %---------------------------------% +c | Get machine dependent constant. | +c %---------------------------------% +c + eps23 = dlamch('Epsilon-Machine') + eps23 = eps23**(2.0D+0 / 3.0D+0) +c + nconv = 0 + do 20 i = 1, n + temp = max( eps23, dlapy2( ritzr(i), ritzi(i) ) ) + if (bounds(i) .le. tol*temp) nconv = nconv + 1 + 20 continue +c + call igraphsecond (t1) + tnconv = tnconv + (t1 - t0) +c + return +c +c %---------------% +c | End of igraphdnconv | +c %---------------% +c + end diff --git a/src/rigraph/vendor/arpack/dneigh.f b/src/rigraph/vendor/arpack/dneigh.f new file mode 100644 index 0000000..53c7c89 --- /dev/null +++ b/src/rigraph/vendor/arpack/dneigh.f @@ -0,0 +1,315 @@ +c----------------------------------------------------------------------- +c\BeginDoc +c +c\Name: igraphdneigh +c +c\Description: +c Compute the eigenvalues of the current upper Hessenberg matrix +c and the corresponding Ritz estimates given the current residual norm. +c +c\Usage: +c call igraphdneigh +c ( RNORM, N, H, LDH, RITZR, RITZI, BOUNDS, Q, LDQ, WORKL, IERR ) +c +c\Arguments +c RNORM Double precision scalar. (INPUT) +c Residual norm corresponding to the current upper Hessenberg +c matrix H. +c +c N Integer. (INPUT) +c Size of the matrix H. +c +c H Double precision N by N array. (INPUT) +c H contains the current upper Hessenberg matrix. +c +c LDH Integer. (INPUT) +c Leading dimension of H exactly as declared in the calling +c program. +c +c RITZR, Double precision arrays of length N. (OUTPUT) +c RITZI On output, RITZR(1:N) (resp. RITZI(1:N)) contains the real +c (respectively imaginary) parts of the eigenvalues of H. +c +c BOUNDS Double precision array of length N. (OUTPUT) +c On output, BOUNDS contains the Ritz estimates associated with +c the eigenvalues RITZR and RITZI. This is equal to RNORM +c times the last components of the eigenvectors corresponding +c to the eigenvalues in RITZR and RITZI. +c +c Q Double precision N by N array. (WORKSPACE) +c Workspace needed to store the eigenvectors of H. +c +c LDQ Integer. (INPUT) +c Leading dimension of Q exactly as declared in the calling +c program. +c +c WORKL Double precision work array of length N**2 + 3*N. (WORKSPACE) +c Private (replicated) array on each PE or array allocated on +c the front end. This is needed to keep the full Schur form +c of H and also in the calculation of the eigenvectors of H. +c +c IERR Integer. (OUTPUT) +c Error exit flag from igraphdlaqrb or dtrevc. +c +c\EndDoc +c +c----------------------------------------------------------------------- +c +c\BeginLib +c +c\Local variables: +c xxxxxx real +c +c\Routines called: +c igraphdlaqrb ARPACK routine to compute the real Schur form of an +c upper Hessenberg matrix and last row of the Schur vectors. +c igraphsecond ARPACK utility routine for timing. +c igraphdmout ARPACK utility routine that prints matrices +c igraphdvout ARPACK utility routine that prints vectors. +c dlacpy LAPACK matrix copy routine. +c dlapy2 LAPACK routine to compute sqrt(x**2+y**2) carefully. +c dtrevc LAPACK routine to compute the eigenvectors of a matrix +c in upper quasi-triangular form +c dgemv Level 2 BLAS routine for matrix vector multiplication. +c dcopy Level 1 BLAS that copies one vector to another . +c dnrm2 Level 1 BLAS that computes the norm of a vector. +c dscal Level 1 BLAS that scales a vector. +c +c +c\Author +c Danny Sorensen Phuong Vu +c Richard Lehoucq CRPC / Rice University +c Dept. of Computational & Houston, Texas +c Applied Mathematics +c Rice University +c Houston, Texas +c +c\Revision history: +c xx/xx/92: Version ' 2.1' +c +c\SCCS Information: @(#) +c FILE: neigh.F SID: 2.3 DATE OF SID: 4/20/96 RELEASE: 2 +c +c\Remarks +c None +c +c\EndLib +c +c----------------------------------------------------------------------- +c + subroutine igraphdneigh (rnorm, n, h, ldh, ritzr, ritzi, bounds, + & q, ldq, workl, ierr) +c +c %----------------------------------------------------% +c | Include files for debugging and timing information | +c %----------------------------------------------------% +c + include 'debug.h' + include 'stat.h' +c +c %------------------% +c | Scalar Arguments | +c %------------------% +c + integer ierr, n, ldh, ldq + Double precision + & rnorm +c +c %-----------------% +c | Array Arguments | +c %-----------------% +c + Double precision + & bounds(n), h(ldh,n), q(ldq,n), ritzi(n), ritzr(n), + & workl(n*(n+3)) +c +c %------------% +c | Parameters | +c %------------% +c + Double precision + & one, zero + parameter (one = 1.0D+0, zero = 0.0D+0) +c +c %------------------------% +c | Local Scalars & Arrays | +c %------------------------% +c + logical select(1) + integer i, iconj, msglvl + Double precision + & temp, vl(1) +c +c %----------------------% +c | External Subroutines | +c %----------------------% +c + external dcopy, dlacpy, igraphdlaqrb, dtrevc, igraphdvout, + & igraphsecond +c +c %--------------------% +c | External Functions | +c %--------------------% +c + Double precision + & dlapy2, dnrm2 + external dlapy2, dnrm2 +c +c %---------------------% +c | Intrinsic Functions | +c %---------------------% +c + intrinsic abs +c +c %-----------------------% +c | Executable Statements | +c %-----------------------% +c +c +c %-------------------------------% +c | Initialize timing statistics | +c | & message level for debugging | +c %-------------------------------% +c + call igraphsecond (t0) + msglvl = mneigh +c + if (msglvl .gt. 2) then + call igraphdmout (logfil, n, n, h, ldh, ndigit, + & '_neigh: Entering upper Hessenberg matrix H ') + end if +c +c %-----------------------------------------------------------% +c | 1. Compute the eigenvalues, the last components of the | +c | corresponding Schur vectors and the full Schur form T | +c | of the current upper Hessenberg matrix H. | +c | igraphdlaqrb returns the full Schur form of H in WORKL(1:N**2) | +c | and the last components of the Schur vectors in BOUNDS. | +c %-----------------------------------------------------------% +c + call dlacpy ('All', n, n, h, ldh, workl, n) + call igraphdlaqrb (.true., n, 1, n, workl, n, ritzr, ritzi, + & bounds, ierr) + if (ierr .ne. 0) go to 9000 +c + if (msglvl .gt. 1) then + call igraphdvout (logfil, n, bounds, ndigit, + & '_neigh: last row of the Schur matrix for H') + end if +c +c %-----------------------------------------------------------% +c | 2. Compute the eigenvectors of the full Schur form T and | +c | apply the last components of the Schur vectors to get | +c | the last components of the corresponding eigenvectors. | +c | Remember that if the i-th and (i+1)-st eigenvalues are | +c | complex conjugate pairs, then the real & imaginary part | +c | of the eigenvector components are split across adjacent | +c | columns of Q. | +c %-----------------------------------------------------------% +c + call dtrevc ('R', 'A', select, n, workl, n, vl, n, q, ldq, + & n, n, workl(n*n+1), ierr) +c + if (ierr .ne. 0) go to 9000 +c +c %------------------------------------------------% +c | Scale the returning eigenvectors so that their | +c | euclidean norms are all one. LAPACK subroutine | +c | dtrevc returns each eigenvector normalized so | +c | that the element of largest magnitude has | +c | magnitude 1; here the magnitude of a complex | +c | number (x,y) is taken to be |x| + |y|. | +c %------------------------------------------------% +c + iconj = 0 + do 10 i=1, n + if ( abs( ritzi(i) ) .le. zero ) then +c +c %----------------------% +c | Real eigenvalue case | +c %----------------------% +c + temp = dnrm2( n, q(1,i), 1 ) + call dscal ( n, one / temp, q(1,i), 1 ) + else +c +c %-------------------------------------------% +c | Complex conjugate pair case. Note that | +c | since the real and imaginary part of | +c | the eigenvector are stored in consecutive | +c | columns, we further normalize by the | +c | square root of two. | +c %-------------------------------------------% +c + if (iconj .eq. 0) then + temp = dlapy2( dnrm2( n, q(1,i), 1 ), + & dnrm2( n, q(1,i+1), 1 ) ) + call dscal ( n, one / temp, q(1,i), 1 ) + call dscal ( n, one / temp, q(1,i+1), 1 ) + iconj = 1 + else + iconj = 0 + end if + end if + 10 continue +c + call dgemv ('T', n, n, one, q, ldq, bounds, 1, zero, workl, 1) +c + if (msglvl .gt. 1) then + call igraphdvout (logfil, n, workl, ndigit, + & '_neigh: Last row of the eigenvector matrix for H') + end if +c +c %----------------------------% +c | Compute the Ritz estimates | +c %----------------------------% +c + iconj = 0 + do 20 i = 1, n + if ( abs( ritzi(i) ) .le. zero ) then +c +c %----------------------% +c | Real eigenvalue case | +c %----------------------% +c + bounds(i) = rnorm * abs( workl(i) ) + else +c +c %-------------------------------------------% +c | Complex conjugate pair case. Note that | +c | since the real and imaginary part of | +c | the eigenvector are stored in consecutive | +c | columns, we need to take the magnitude | +c | of the last components of the two vectors | +c %-------------------------------------------% +c + if (iconj .eq. 0) then + bounds(i) = rnorm * dlapy2( workl(i), workl(i+1) ) + bounds(i+1) = bounds(i) + iconj = 1 + else + iconj = 0 + end if + end if + 20 continue +c + if (msglvl .gt. 2) then + call igraphdvout (logfil, n, ritzr, ndigit, + & '_neigh: Real part of the eigenvalues of H') + call igraphdvout (logfil, n, ritzi, ndigit, + & '_neigh: Imaginary part of the eigenvalues of H') + call igraphdvout (logfil, n, bounds, ndigit, + & '_neigh: Ritz estimates for the eigenvalues of H') + end if +c + call igraphsecond (t1) + tneigh = tneigh + (t1 - t0) +c + 9000 continue + return +c +c %---------------% +c | End of igraphdneigh | +c %---------------% +c + end diff --git a/src/rigraph/vendor/arpack/dneupd.f b/src/rigraph/vendor/arpack/dneupd.f new file mode 100644 index 0000000..92b4bc3 --- /dev/null +++ b/src/rigraph/vendor/arpack/dneupd.f @@ -0,0 +1,1044 @@ +c\BeginDoc +c +c\Name: igraphdneupd +c +c\Description: +c +c This subroutine returns the converged approximations to eigenvalues +c of A*z = lambda*B*z and (optionally): +c +c (1) The corresponding approximate eigenvectors; +c +c (2) An orthonormal basis for the associated approximate +c invariant subspace; +c +c (3) Both. +c +c There is negligible additional cost to obtain eigenvectors. An orthonormal +c basis is always computed. There is an additional storage cost of n*nev +c if both are requested (in this case a separate array Z must be supplied). +c +c The approximate eigenvalues and eigenvectors of A*z = lambda*B*z +c are derived from approximate eigenvalues and eigenvectors of +c of the linear operator OP prescribed by the MODE selection in the +c call to DNAUPD. DNAUPD must be called before this routine is called. +c These approximate eigenvalues and vectors are commonly called Ritz +c values and Ritz vectors respectively. They are referred to as such +c in the comments that follow. The computed orthonormal basis for the +c invariant subspace corresponding to these Ritz values is referred to as a +c Schur basis. +c +c See documentation in the header of the subroutine DNAUPD for +c definition of OP as well as other terms and the relation of computed +c Ritz values and Ritz vectors of OP with respect to the given problem +c A*z = lambda*B*z. For a brief description, see definitions of +c IPARAM(7), MODE and WHICH in the documentation of DNAUPD. +c +c\Usage: +c call igraphdneupd +c ( RVEC, HOWMNY, SELECT, DR, DI, Z, LDZ, SIGMAR, SIGMAI, WORKEV, BMAT, +c N, WHICH, NEV, TOL, RESID, NCV, V, LDV, IPARAM, IPNTR, WORKD, WORKL, +c LWORKL, INFO ) +c +c\Arguments: +c RVEC LOGICAL (INPUT) +c Specifies whether a basis for the invariant subspace corresponding +c to the converged Ritz value approximations for the eigenproblem +c A*z = lambda*B*z is computed. +c +c RVEC = .FALSE. Compute Ritz values only. +c +c RVEC = .TRUE. Compute the Ritz vectors or Schur vectors. +c See Remarks below. +c +c HOWMNY Character*1 (INPUT) +c Specifies the form of the basis for the invariant subspace +c corresponding to the converged Ritz values that is to be computed. +c +c = 'A': Compute NEV Ritz vectors; +c = 'P': Compute NEV Schur vectors; +c = 'S': compute some of the Ritz vectors, specified +c by the logical array SELECT. +c +c SELECT Logical array of dimension NCV. (INPUT) +c If HOWMNY = 'S', SELECT specifies the Ritz vectors to be +c computed. To select the Ritz vector corresponding to a +c Ritz value (DR(j), DI(j)), SELECT(j) must be set to .TRUE.. +c If HOWMNY = 'A' or 'P', SELECT is used as internal workspace. +c +c DR Double precision array of dimension NEV+1. (OUTPUT) +c If IPARAM(7) = 1,2 or 3 and SIGMAI=0.0 then on exit: DR contains +c the real part of the Ritz approximations to the eigenvalues of +c A*z = lambda*B*z. +c If IPARAM(7) = 3, 4 and SIGMAI is not equal to zero, then on exit: +c DR contains the real part of the Ritz values of OP computed by +c DNAUPD. A further computation must be performed by the user +c to transform the Ritz values computed for OP by DNAUPD to those +c of the original system A*z = lambda*B*z. See remark 3 below. +c +c DI Double precision array of dimension NEV+1. (OUTPUT) +c On exit, DI contains the imaginary part of the Ritz value +c approximations to the eigenvalues of A*z = lambda*B*z associated +c with DR. +c +c NOTE: When Ritz values are complex, they will come in complex +c conjugate pairs. If eigenvectors are requested, the +c corresponding Ritz vectors will also come in conjugate +c pairs and the real and imaginary parts of these are +c represented in two consecutive columns of the array Z +c (see below). +c +c Z Double precision N by NEV+1 array if RVEC = .TRUE. and HOWMNY = 'A'. (OUTPUT) +c On exit, if RVEC = .TRUE. and HOWMNY = 'A', then the columns of +c Z represent approximate eigenvectors (Ritz vectors) corresponding +c to the NCONV=IPARAM(5) Ritz values for eigensystem +c A*z = lambda*B*z. +c +c The complex Ritz vector associated with the Ritz value +c with positive imaginary part is stored in two consecutive +c columns. The first column holds the real part of the Ritz +c vector and the igraphsecond column holds the imaginary part. The +c Ritz vector associated with the Ritz value with negative +c imaginary part is simply the complex conjugate of the Ritz vector +c associated with the positive imaginary part. +c +c If RVEC = .FALSE. or HOWMNY = 'P', then Z is not referenced. +c +c NOTE: If if RVEC = .TRUE. and a Schur basis is not required, +c the array Z may be set equal to first NEV+1 columns of the Arnoldi +c basis array V computed by DNAUPD. In this case the Arnoldi basis +c will be destroyed and overwritten with the eigenvector basis. +c +c LDZ Integer. (INPUT) +c The leading dimension of the array Z. If Ritz vectors are +c desired, then LDZ >= max( 1, N ). In any case, LDZ >= 1. +c +c SIGMAR Double precision (INPUT) +c If IPARAM(7) = 3 or 4, represents the real part of the shift. +c Not referenced if IPARAM(7) = 1 or 2. +c +c SIGMAI Double precision (INPUT) +c If IPARAM(7) = 3 or 4, represents the imaginary part of the shift. +c Not referenced if IPARAM(7) = 1 or 2. See remark 3 below. +c +c WORKEV Double precision work array of dimension 3*NCV. (WORKSPACE) +c +c **** The remaining arguments MUST be the same as for the **** +c **** call to DNAUPD that was just completed. **** +c +c NOTE: The remaining arguments +c +c BMAT, N, WHICH, NEV, TOL, RESID, NCV, V, LDV, IPARAM, IPNTR, +c WORKD, WORKL, LWORKL, INFO +c +c must be passed directly to DNEUPD following the last call +c to DNAUPD. These arguments MUST NOT BE MODIFIED between +c the the last call to DNAUPD and the call to DNEUPD. +c +c Three of these parameters (V, WORKL, INFO) are also output parameters: +c +c V Double precision N by NCV array. (INPUT/OUTPUT) +c +c Upon INPUT: the NCV columns of V contain the Arnoldi basis +c vectors for OP as constructed by DNAUPD . +c +c Upon OUTPUT: If RVEC = .TRUE. the first NCONV=IPARAM(5) columns +c contain approximate Schur vectors that span the +c desired invariant subspace. See Remark 2 below. +c +c NOTE: If the array Z has been set equal to first NEV+1 columns +c of the array V and RVEC=.TRUE. and HOWMNY= 'A', then the +c Arnoldi basis held by V has been overwritten by the desired +c Ritz vectors. If a separate array Z has been passed then +c the first NCONV=IPARAM(5) columns of V will contain approximate +c Schur vectors that span the desired invariant subspace. +c +c WORKL Double precision work array of length LWORKL. (OUTPUT/WORKSPACE) +c WORKL(1:ncv*ncv+3*ncv) contains information obtained in +c igraphdnaupd. They are not changed by igraphdneupd. +c WORKL(ncv*ncv+3*ncv+1:3*ncv*ncv+6*ncv) holds the +c real and imaginary part of the untransformed Ritz values, +c the upper quasi-triangular matrix for H, and the +c associated matrix representation of the invariant subspace for H. +c +c Note: IPNTR(9:13) contains the pointer into WORKL for addresses +c of the above information computed by igraphdneupd. +c ------------------------------------------------------------- +c IPNTR(9): pointer to the real part of the NCV RITZ values of the +c original system. +c IPNTR(10): pointer to the imaginary part of the NCV RITZ values of +c the original system. +c IPNTR(11): pointer to the NCV corresponding error bounds. +c IPNTR(12): pointer to the NCV by NCV upper quasi-triangular +c Schur matrix for H. +c IPNTR(13): pointer to the NCV by NCV matrix of eigenvectors +c of the upper Hessenberg matrix H. Only referenced by +c igraphdneupd if RVEC = .TRUE. See Remark 2 below. +c ------------------------------------------------------------- +c +c INFO Integer. (OUTPUT) +c Error flag on output. +c +c = 0: Normal exit. +c +c = 1: The Schur form computed by LAPACK routine dlahqr +c could not be reordered by LAPACK routine dtrsen. +c Re-enter subroutine igraphdneupd with IPARAM(5)=NCV and +c increase the size of the arrays DR and DI to have +c dimension at least dimension NCV and allocate at least NCV +c columns for Z. NOTE: Not necessary if Z and V share +c the same space. Please notify the authors if this error +c occurs. +c +c = -1: N must be positive. +c = -2: NEV must be positive. +c = -3: NCV-NEV >= 2 and less than or equal to N. +c = -5: WHICH must be one of 'LM', 'SM', 'LR', 'SR', 'LI', 'SI' +c = -6: BMAT must be one of 'I' or 'G'. +c = -7: Length of private work WORKL array is not sufficient. +c = -8: Error return from calculation of a real Schur form. +c Informational error from LAPACK routine dlahqr. +c = -9: Error return from calculation of eigenvectors. +c Informational error from LAPACK routine dtrevc. +c = -10: IPARAM(7) must be 1,2,3,4. +c = -11: IPARAM(7) = 1 and BMAT = 'G' are incompatible. +c = -12: HOWMNY = 'S' not yet implemented +c = -13: HOWMNY must be one of 'A' or 'P' if RVEC = .true. +c = -14: DNAUPD did not find any eigenvalues to sufficient +c accuracy. +c +c\BeginLib +c +c\References: +c 1. D.C. Sorensen, "Implicit Application of Polynomial Filters in +c a k-Step Arnoldi Method", SIAM J. Matr. Anal. Apps., 13 (1992), +c pp 357-385. +c 2. R.B. Lehoucq, "Analysis and Implementation of an Implicitly +c Restarted Arnoldi Iteration", Rice University Technical Report +c TR95-13, Department of Computational and Applied Mathematics. +c 3. B.N. Parlett & Y. Saad, "Complex Shift and Invert Strategies for +c Real Matrices", Linear Algebra and its Applications, vol 88/89, +c pp 575-595, (1987). +c +c\Routines called: +c igraphivout ARPACK utility routine that prints integers. +c igraphdmout ARPACK utility routine that prints matrices +c igraphdvout ARPACK utility routine that prints vectors. +c dgeqr2 LAPACK routine that computes the QR factorization of +c a matrix. +c dlacpy LAPACK matrix copy routine. +c dlahqr LAPACK routine to compute the real Schur form of an +c upper Hessenberg matrix. +c dlamch LAPACK routine that determines machine constants. +c dlapy2 LAPACK routine to compute sqrt(x**2+y**2) carefully. +c dlaset LAPACK matrix initialization routine. +c dorm2r LAPACK routine that applies an orthogonal matrix in +c factored form. +c dtrevc LAPACK routine to compute the eigenvectors of a matrix +c in upper quasi-triangular form. +c dtrsen LAPACK routine that re-orders the Schur form. +c dtrmm Level 3 BLAS matrix times an upper triangular matrix. +c dger Level 2 BLAS rank one update to a matrix. +c dcopy Level 1 BLAS that copies one vector to another . +c ddot Level 1 BLAS that computes the scalar product of two vectors. +c dnrm2 Level 1 BLAS that computes the norm of a vector. +c dscal Level 1 BLAS that scales a vector. +c +c\Remarks +c +c 1. Currently only HOWMNY = 'A' and 'P' are implemented. +c +c Let X' denote the transpose of X. +c +c 2. Schur vectors are an orthogonal representation for the basis of +c Ritz vectors. Thus, their numerical properties are often superior. +c If RVEC = .TRUE. then the relationship +c A * V(:,1:IPARAM(5)) = V(:,1:IPARAM(5)) * T, and +c V(:,1:IPARAM(5))' * V(:,1:IPARAM(5)) = I are approximately satisfied. +c Here T is the leading submatrix of order IPARAM(5) of the real +c upper quasi-triangular matrix stored workl(ipntr(12)). That is, +c T is block upper triangular with 1-by-1 and 2-by-2 diagonal blocks; +c each 2-by-2 diagonal block has its diagonal elements equal and its +c off-diagonal elements of opposite sign. Corresponding to each 2-by-2 +c diagonal block is a complex conjugate pair of Ritz values. The real +c Ritz values are stored on the diagonal of T. +c +c 3. If IPARAM(7) = 3 or 4 and SIGMAI is not equal zero, then the user must +c form the IPARAM(5) Rayleigh quotients in order to transform the Ritz +c values computed by DNAUPD for OP to those of A*z = lambda*B*z. +c Set RVEC = .true. and HOWMNY = 'A', and +c compute +c Z(:,I)' * A * Z(:,I) if DI(I) = 0. +c If DI(I) is not equal to zero and DI(I+1) = - D(I), +c then the desired real and imaginary parts of the Ritz value are +c Z(:,I)' * A * Z(:,I) + Z(:,I+1)' * A * Z(:,I+1), +c Z(:,I)' * A * Z(:,I+1) - Z(:,I+1)' * A * Z(:,I), respectively. +c Another possibility is to set RVEC = .true. and HOWMNY = 'P' and +c compute V(:,1:IPARAM(5))' * A * V(:,1:IPARAM(5)) and then an upper +c quasi-triangular matrix of order IPARAM(5) is computed. See remark +c 2 above. +c +c\Authors +c Danny Sorensen Phuong Vu +c Richard Lehoucq CRPC / Rice University +c Chao Yang Houston, Texas +c Dept. of Computational & +c Applied Mathematics +c Rice University +c Houston, Texas +c +c\SCCS Information: @(#) +c FILE: neupd.F SID: 2.5 DATE OF SID: 7/31/96 RELEASE: 2 +c +c\EndLib +c +c----------------------------------------------------------------------- + subroutine igraphdneupd (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) +c +c %----------------------------------------------------% +c | Include files for debugging and timing information | +c %----------------------------------------------------% +c + include 'debug.h' + include 'stat.h' +c +c %------------------% +c | Scalar Arguments | +c %------------------% +c + character bmat, howmny, which*2 + logical rvec + integer info, ldz, ldv, lworkl, n, ncv, nev + Double precision + & sigmar, sigmai, tol +c +c %-----------------% +c | Array Arguments | +c %-----------------% +c + integer iparam(11), ipntr(14) + logical select(ncv) + Double precision + & dr(nev+1), di(nev+1), resid(n), v(ldv,ncv), z(ldz,*), + & workd(3*n), workl(lworkl), workev(3*ncv) +c +c %------------% +c | Parameters | +c %------------% +c + Double precision + & one, zero + parameter (one = 1.0D+0, zero = 0.0D+0) +c +c %---------------% +c | Local Scalars | +c %---------------% +c + character type*6 + integer bounds, ierr, ih, ihbds, iheigr, iheigi, iconj, nconv, + & invsub, iuptri, iwev, iwork(1), j, k, ktrord, + & ldh, ldq, mode, msglvl, outncv, ritzr, ritzi, wri, wrr, + & irr, iri, ibd + logical reord + Double precision + & conds, rnorm, sep, temp, thres, vl(1,1), temp1, eps23 +c +c %----------------------% +c | External Subroutines | +c %----------------------% +c + external dcopy, dger, dgeqr2, dlacpy, dlahqr, dlaset, + & igraphdmout, dorm2r, dtrevc, dtrmm, dtrsen, dscal, + & igraphdvout, igraphivout +c +c %--------------------% +c | External Functions | +c %--------------------% +c + Double precision + & dlapy2, dnrm2, dlamch, ddot + external dlapy2, dnrm2, dlamch, ddot +c +c %---------------------% +c | Intrinsic Functions | +c %---------------------% +c + intrinsic abs, min, sqrt +c +c %-----------------------% +c | Executable Statements | +c %-----------------------% +c +c %------------------------% +c | Set default parameters | +c %------------------------% +c + msglvl = mneupd + mode = iparam(7) + nconv = iparam(5) + info = 0 +c +c %---------------------------------% +c | Get machine dependent constant. | +c %---------------------------------% +c + eps23 = dlamch('Epsilon-Machine') + eps23 = eps23**(2.0D+0 / 3.0D+0) +c +c %--------------% +c | Quick return | +c %--------------% +c + ierr = 0 +c + if (nconv .le. 0) then + ierr = -14 + else if (n .le. 0) then + ierr = -1 + else if (nev .le. 0) then + ierr = -2 + else if (ncv .le. nev+1 .or. ncv .gt. n) then + ierr = -3 + else if (which .ne. 'LM' .and. + & which .ne. 'SM' .and. + & which .ne. 'LR' .and. + & which .ne. 'SR' .and. + & which .ne. 'LI' .and. + & which .ne. 'SI') then + ierr = -5 + else if (bmat .ne. 'I' .and. bmat .ne. 'G') then + ierr = -6 + else if (lworkl .lt. 3*ncv**2 + 6*ncv) then + ierr = -7 + else if ( (howmny .ne. 'A' .and. + & howmny .ne. 'P' .and. + & howmny .ne. 'S') .and. rvec ) then + ierr = -13 + else if (howmny .eq. 'S' ) then + ierr = -12 + end if +c + if (mode .eq. 1 .or. mode .eq. 2) then + type = 'REGULR' + else if (mode .eq. 3 .and. sigmai .eq. zero) then + type = 'SHIFTI' + else if (mode .eq. 3 ) then + type = 'REALPT' + else if (mode .eq. 4 ) then + type = 'IMAGPT' + else + ierr = -10 + end if + if (mode .eq. 1 .and. bmat .eq. 'G') ierr = -11 +c +c %------------% +c | Error Exit | +c %------------% +c + if (ierr .ne. 0) then + info = ierr + go to 9000 + end if +c +c %--------------------------------------------------------% +c | Pointer into WORKL for address of H, RITZ, BOUNDS, Q | +c | etc... and the remaining workspace. | +c | Also update pointer to be used on output. | +c | Memory is laid out as follows: | +c | workl(1:ncv*ncv) := generated Hessenberg matrix | +c | workl(ncv*ncv+1:ncv*ncv+2*ncv) := real and imaginary | +c | parts of ritz values | +c | workl(ncv*ncv+2*ncv+1:ncv*ncv+3*ncv) := error bounds | +c %--------------------------------------------------------% +c +c %-----------------------------------------------------------% +c | The following is used and set by DNEUPD. | +c | workl(ncv*ncv+3*ncv+1:ncv*ncv+4*ncv) := The untransformed | +c | real part of the Ritz values. | +c | workl(ncv*ncv+4*ncv+1:ncv*ncv+5*ncv) := The untransformed | +c | imaginary part of the Ritz values. | +c | workl(ncv*ncv+5*ncv+1:ncv*ncv+6*ncv) := The untransformed | +c | error bounds of the Ritz values | +c | workl(ncv*ncv+6*ncv+1:2*ncv*ncv+6*ncv) := Holds the upper | +c | quasi-triangular matrix for H | +c | workl(2*ncv*ncv+6*ncv+1: 3*ncv*ncv+6*ncv) := Holds the | +c | associated matrix representation of the invariant | +c | subspace for H. | +c | GRAND total of NCV * ( 3 * NCV + 6 ) locations. | +c %-----------------------------------------------------------% +c + 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 +c +c %-----------------------------------------% +c | irr points to the REAL part of the Ritz | +c | values computed by _neigh before | +c | exiting _naup2. | +c | iri points to the IMAGINARY part of the | +c | Ritz values computed by _neigh | +c | before exiting _naup2. | +c | ibd points to the Ritz estimates | +c | computed by _neigh before exiting | +c | _naup2. | +c %-----------------------------------------% +c + irr = ipntr(14)+ncv*ncv + iri = irr+ncv + ibd = iri+ncv +c +c %------------------------------------% +c | RNORM is B-norm of the RESID(1:N). | +c %------------------------------------% +c + rnorm = workl(ih+2) + workl(ih+2) = zero +c + if (rvec) then +c +c %-------------------------------------------% +c | Get converged Ritz value on the boundary. | +c | Note: converged Ritz values have been | +c | placed in the first NCONV locations in | +c | workl(ritzr) and workl(ritzi). They have | +c | been sorted (in _naup2) according to the | +c | WHICH selection criterion. | +c %-------------------------------------------% +c + if (which .eq. 'LM' .or. which .eq. 'SM') then + thres = dlapy2( workl(ritzr), workl(ritzi) ) + else if (which .eq. 'LR' .or. which .eq. 'SR') then + thres = workl(ritzr) + else if (which .eq. 'LI' .or. which .eq. 'SI') then + thres = abs( workl(ritzi) ) + end if +c + if (msglvl .gt. 2) then + call igraphdvout(logfil, 1, [thres], ndigit, + & '_neupd: Threshold eigenvalue used for re-ordering') + end if +c +c %----------------------------------------------------------% +c | Check to see if all converged Ritz values appear at the | +c | top of the upper quasi-triangular matrix computed by | +c | _neigh in _naup2. This is done in the following way: | +c | | +c | 1) For each Ritz value obtained from _neigh, compare it | +c | with the threshold Ritz value computed above to | +c | determine whether it is a wanted one. | +c | | +c | 2) If it is wanted, then check the corresponding Ritz | +c | estimate to see if it has converged. If it has, set | +c | correponding entry in the logical array SELECT to | +c | .TRUE.. | +c | | +c | If SELECT(j) = .TRUE. and j > NCONV, then there is a | +c | converged Ritz value that does not appear at the top of | +c | the upper quasi-triangular matrix computed by _neigh in | +c | _naup2. Reordering is needed. | +c %----------------------------------------------------------% +c + reord = .false. + ktrord = 0 + do 10 j = 0, ncv-1 + select(j+1) = .false. + if (which .eq. 'LM') then + if (dlapy2(workl(irr+j), workl(iri+j)) + & .ge. thres) then + temp1 = max( eps23, + & dlapy2( workl(irr+j), workl(iri+j) ) ) + if (workl(ibd+j) .le. tol*temp1) + & select(j+1) = .true. + end if + else if (which .eq. 'SM') then + if (dlapy2(workl(irr+j), workl(iri+j)) + & .le. thres) then + temp1 = max( eps23, + & dlapy2( workl(irr+j), workl(iri+j) ) ) + if (workl(ibd+j) .le. tol*temp1) + & select(j+1) = .true. + end if + else if (which .eq. 'LR') then + if (workl(irr+j) .ge. thres) then + temp1 = max( eps23, + & dlapy2( workl(irr+j), workl(iri+j) ) ) + if (workl(ibd+j) .le. tol*temp1) + & select(j+1) = .true. + end if + else if (which .eq. 'SR') then + if (workl(irr+j) .le. thres) then + temp1 = max( eps23, + & dlapy2( workl(irr+j), workl(iri+j) ) ) + if (workl(ibd+j) .le. tol*temp1) + & select(j+1) = .true. + end if + else if (which .eq. 'LI') then + if (abs(workl(iri+j)) .ge. thres) then + temp1 = max( eps23, + & dlapy2( workl(irr+j), workl(iri+j) ) ) + if (workl(ibd+j) .le. tol*temp1) + & select(j+1) = .true. + end if + else if (which .eq. 'SI') then + if (abs(workl(iri+j)) .le. thres) then + temp1 = max( eps23, + & dlapy2( workl(irr+j), workl(iri+j) ) ) + if (workl(ibd+j) .le. tol*temp1) + & select(j+1) = .true. + end if + end if + if (j+1 .gt. nconv ) reord = ( select(j+1) .or. reord ) + if (select(j+1)) ktrord = ktrord + 1 + 10 continue +c + if (msglvl .gt. 2) then + call igraphivout(logfil, 1, [ktrord], ndigit, + & '_neupd: Number of specified eigenvalues') + call igraphivout(logfil, 1, [nconv], ndigit, + & '_neupd: Number of "converged" eigenvalues') + end if +c +c %-----------------------------------------------------------% +c | Call LAPACK routine dlahqr to compute the real Schur form | +c | of the upper Hessenberg matrix returned by DNAUPD. | +c | Make a copy of the upper Hessenberg matrix. | +c | Initialize the Schur vector matrix Q to the identity. | +c %-----------------------------------------------------------% +c + call dcopy (ldh*ncv, workl(ih), 1, workl(iuptri), 1) + call dlaset ('All', ncv, ncv, zero, one, workl(invsub), ldq) + call dlahqr (.true., .true., ncv, 1, ncv, workl(iuptri), ldh, + & workl(iheigr), workl(iheigi), 1, ncv, + & workl(invsub), ldq, ierr) + call dcopy (ncv, workl(invsub+ncv-1), ldq, workl(ihbds), 1) +c + if (ierr .ne. 0) then + info = -8 + go to 9000 + end if +c + if (msglvl .gt. 1) then + call igraphdvout (logfil, ncv, workl(iheigr), ndigit, + & '_neupd: Real part of the eigenvalues of H') + call igraphdvout (logfil, ncv, workl(iheigi), ndigit, + & '_neupd: Imaginary part of the Eigenvalues of H') + call igraphdvout (logfil, ncv, workl(ihbds), ndigit, + & '_neupd: Last row of the Schur vector matrix') + if (msglvl .gt. 3) then + call igraphdmout (logfil, ncv, ncv, workl(iuptri), ldh, + & ndigit, + & '_neupd: The upper quasi-triangular matrix ') + end if + end if +c + if (reord) then +c +c %-----------------------------------------------------% +c | Reorder the computed upper quasi-triangular matrix. | +c %-----------------------------------------------------% +c + call dtrsen ('None', 'V', select, ncv, workl(iuptri), ldh, + & workl(invsub), ldq, workl(iheigr), workl(iheigi), + & nconv, conds, sep, workl(ihbds), ncv, iwork, 1, ierr) +c + if (ierr .eq. 1) then + info = 1 + go to 9000 + end if +c + if (msglvl .gt. 2) then + call igraphdvout (logfil, ncv, workl(iheigr), ndigit, + & '_neupd: Real part of the eigenvalues of H--reordered') + call igraphdvout (logfil, ncv, workl(iheigi), ndigit, + & '_neupd: Imag part of the eigenvalues of H--reordered') + if (msglvl .gt. 3) then + call igraphdmout (logfil, ncv, ncv, workl(iuptri), + & ldq, ndigit, + & '_neupd: Quasi-triangular matrix after re-ordering') + end if + end if +c + end if +c +c %---------------------------------------% +c | Copy the last row of the Schur vector | +c | into workl(ihbds). This will be used | +c | to compute the Ritz estimates of | +c | converged Ritz values. | +c %---------------------------------------% +c + call dcopy(ncv, workl(invsub+ncv-1), ldq, workl(ihbds), 1) +c +c %----------------------------------------------------% +c | Place the computed eigenvalues of H into DR and DI | +c | if a spectral transformation was not used. | +c %----------------------------------------------------% +c + if (type .eq. 'REGULR') then + call dcopy (nconv, workl(iheigr), 1, dr, 1) + call dcopy (nconv, workl(iheigi), 1, di, 1) + end if +c +c %----------------------------------------------------------% +c | Compute the QR factorization of the matrix representing | +c | the wanted invariant subspace located in the first NCONV | +c | columns of workl(invsub,ldq). | +c %----------------------------------------------------------% +c + call dgeqr2 (ncv, nconv, workl(invsub), ldq, workev, + & workev(ncv+1), ierr) +c +c %---------------------------------------------------------% +c | * Postmultiply V by Q using dorm2r. | +c | * Copy the first NCONV columns of VQ into Z. | +c | * Postmultiply Z by R. | +c | The N by NCONV matrix Z is now a matrix representation | +c | of the approximate invariant subspace associated with | +c | the Ritz values in workl(iheigr) and workl(iheigi) | +c | The first NCONV columns of V are now approximate Schur | +c | vectors associated with the real upper quasi-triangular | +c | matrix of order NCONV in workl(iuptri) | +c %---------------------------------------------------------% +c + call dorm2r ('Right', 'Notranspose', n, ncv, nconv, + & workl(invsub), ldq, workev, v, ldv, workd(n+1), ierr) + call dlacpy ('All', n, nconv, v, ldv, z, ldz) +c + do 20 j=1, nconv +c +c %---------------------------------------------------% +c | Perform both a column and row scaling if the | +c | diagonal element of workl(invsub,ldq) is negative | +c | I'm lazy and don't take advantage of the upper | +c | quasi-triangular form of workl(iuptri,ldq) | +c | Note that since Q is orthogonal, R is a diagonal | +c | matrix consisting of plus or minus ones | +c %---------------------------------------------------% +c + if (workl(invsub+(j-1)*ldq+j-1) .lt. zero) then + call dscal (nconv, -one, workl(iuptri+j-1), ldq) + call dscal (nconv, -one, workl(iuptri+(j-1)*ldq), 1) + end if +c + 20 continue +c + if (howmny .eq. 'A') then +c +c %--------------------------------------------% +c | Compute the NCONV wanted eigenvectors of T | +c | located in workl(iuptri,ldq). | +c %--------------------------------------------% +c + do 30 j=1, ncv + if (j .le. nconv) then + select(j) = .true. + else + select(j) = .false. + end if + 30 continue +c + call dtrevc ('Right', 'Select', select, ncv, workl(iuptri), + & ldq, vl, 1, workl(invsub), ldq, ncv, outncv, workev, + & ierr) +c + if (ierr .ne. 0) then + info = -9 + go to 9000 + end if +c +c %------------------------------------------------% +c | Scale the returning eigenvectors so that their | +c | Euclidean norms are all one. LAPACK subroutine | +c | dtrevc returns each eigenvector normalized so | +c | that the element of largest magnitude has | +c | magnitude 1; | +c %------------------------------------------------% +c + iconj = 0 + do 40 j=1, nconv +c + if ( workl(iheigi+j-1) .eq. zero ) then +c +c %----------------------% +c | real eigenvalue case | +c %----------------------% +c + temp = dnrm2( ncv, workl(invsub+(j-1)*ldq), 1 ) + call dscal ( ncv, one / temp, + & workl(invsub+(j-1)*ldq), 1 ) +c + else +c +c %-------------------------------------------% +c | Complex conjugate pair case. Note that | +c | since the real and imaginary part of | +c | the eigenvector are stored in consecutive | +c | columns, we further normalize by the | +c | square root of two. | +c %-------------------------------------------% +c + if (iconj .eq. 0) then + temp = dlapy2( dnrm2( ncv, workl(invsub+(j-1)*ldq), + & 1 ), dnrm2( ncv, workl(invsub+j*ldq), 1) ) + call dscal ( ncv, one / temp, + & workl(invsub+(j-1)*ldq), 1 ) + call dscal ( ncv, one / temp, + & workl(invsub+j*ldq), 1 ) + iconj = 1 + else + iconj = 0 + end if +c + end if +c + 40 continue +c + call dgemv('T', ncv, nconv, one, workl(invsub), + & ldq, workl(ihbds), 1, zero, workev, 1) +c + iconj = 0 + do 45 j=1, nconv + if (workl(iheigi+j-1) .ne. zero) then +c +c %-------------------------------------------% +c | Complex conjugate pair case. Note that | +c | since the real and imaginary part of | +c | the eigenvector are stored in consecutive | +c %-------------------------------------------% +c + if (iconj .eq. 0) then + workev(j) = dlapy2(workev(j), workev(j+1)) + workev(j+1) = workev(j) + iconj = 1 + else + iconj = 0 + end if + end if + 45 continue +c + if (msglvl .gt. 2) then + call dcopy(ncv, workl(invsub+ncv-1), ldq, + & workl(ihbds), 1) + call igraphdvout (logfil, ncv, workl(ihbds), ndigit, + & '_neupd: Last row of the eigenvector matrix for T') + if (msglvl .gt. 3) then + call igraphdmout (logfil, ncv, ncv, workl(invsub), + & ldq, ndigit, + & '_neupd: The eigenvector matrix for T') + end if + end if +c +c %---------------------------------------% +c | Copy Ritz estimates into workl(ihbds) | +c %---------------------------------------% +c + call dcopy(nconv, workev, 1, workl(ihbds), 1) +c +c %---------------------------------------------------------% +c | Compute the QR factorization of the eigenvector matrix | +c | associated with leading portion of T in the first NCONV | +c | columns of workl(invsub,ldq). | +c %---------------------------------------------------------% +c + call dgeqr2 (ncv, nconv, workl(invsub), ldq, workev, + & workev(ncv+1), ierr) +c +c %----------------------------------------------% +c | * Postmultiply Z by Q. | +c | * Postmultiply Z by R. | +c | The N by NCONV matrix Z is now contains the | +c | Ritz vectors associated with the Ritz values | +c | in workl(iheigr) and workl(iheigi). | +c %----------------------------------------------% +c + call dorm2r ('Right', 'Notranspose', n, ncv, nconv, + & workl(invsub), ldq, workev, z, ldz, workd(n+1), ierr) +c + call dtrmm ('Right', 'Upper', 'No transpose', 'Non-unit', + & n, nconv, one, workl(invsub), ldq, z, ldz) +c + end if +c + else +c +c %------------------------------------------------------% +c | An approximate invariant subspace is not needed. | +c | Place the Ritz values computed DNAUPD into DR and DI | +c %------------------------------------------------------% +c + call dcopy (nconv, workl(ritzr), 1, dr, 1) + call dcopy (nconv, workl(ritzi), 1, di, 1) + call dcopy (nconv, workl(ritzr), 1, workl(iheigr), 1) + call dcopy (nconv, workl(ritzi), 1, workl(iheigi), 1) + call dcopy (nconv, workl(bounds), 1, workl(ihbds), 1) + end if +c +c %------------------------------------------------% +c | Transform the Ritz values and possibly vectors | +c | and corresponding error bounds of OP to those | +c | of A*x = lambda*B*x. | +c %------------------------------------------------% +c + if (type .eq. 'REGULR') then +c + if (rvec) + & call dscal (ncv, rnorm, workl(ihbds), 1) +c + else +c +c %---------------------------------------% +c | A spectral transformation was used. | +c | * Determine the Ritz estimates of the | +c | Ritz values in the original system. | +c %---------------------------------------% +c + if (type .eq. 'SHIFTI') then +c + if (rvec) + & call dscal (ncv, rnorm, workl(ihbds), 1) +c + do 50 k=1, ncv + temp = dlapy2( workl(iheigr+k-1), + & workl(iheigi+k-1) ) + workl(ihbds+k-1) = abs( workl(ihbds+k-1) ) + & / temp / temp + 50 continue +c + else if (type .eq. 'REALPT') then +c + do 60 k=1, ncv + 60 continue +c + else if (type .eq. 'IMAGPT') then +c + do 70 k=1, ncv + 70 continue +c + end if +c +c %-----------------------------------------------------------% +c | * Transform the Ritz values back to the original system. | +c | For TYPE = 'SHIFTI' the transformation is | +c | lambda = 1/theta + sigma | +c | For TYPE = 'REALPT' or 'IMAGPT' the user must from | +c | Rayleigh quotients or a projection. See remark 3 above.| +c | NOTES: | +c | *The Ritz vectors are not affected by the transformation. | +c %-----------------------------------------------------------% +c + if (type .eq. 'SHIFTI') then +c + do 80 k=1, ncv + temp = dlapy2( 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 + 80 continue +c + call dcopy (nconv, workl(iheigr), 1, dr, 1) + call dcopy (nconv, workl(iheigi), 1, di, 1) +c + else if (type .eq. 'REALPT' .or. type .eq. 'IMAGPT') then +c + call dcopy (nconv, workl(iheigr), 1, dr, 1) + call dcopy (nconv, workl(iheigi), 1, di, 1) +c + end if +c + end if +c + if (type .eq. 'SHIFTI' .and. msglvl .gt. 1) then + call igraphdvout (logfil, nconv, dr, ndigit, + & '_neupd: Untransformed real part of the Ritz valuess.') + call igraphdvout (logfil, nconv, di, ndigit, + & '_neupd: Untransformed imag part of the Ritz valuess.') + call igraphdvout (logfil, nconv, workl(ihbds), ndigit, + & '_neupd: Ritz estimates of untransformed Ritz values.') + else if (type .eq. 'REGULR' .and. msglvl .gt. 1) then + call igraphdvout (logfil, nconv, dr, ndigit, + & '_neupd: Real parts of converged Ritz values.') + call igraphdvout (logfil, nconv, di, ndigit, + & '_neupd: Imag parts of converged Ritz values.') + call igraphdvout (logfil, nconv, workl(ihbds), ndigit, + & '_neupd: Associated Ritz estimates.') + end if +c +c %-------------------------------------------------% +c | Eigenvector Purification step. Formally perform | +c | one of inverse subspace iteration. Only used | +c | for MODE = 2. | +c %-------------------------------------------------% +c + if (rvec .and. howmny .eq. 'A' .and. type .eq. 'SHIFTI') then +c +c %------------------------------------------------% +c | Purify the computed Ritz vectors by adding a | +c | little bit of the residual vector: | +c | T | +c | resid(:)*( e s ) / theta | +c | NCV | +c | where H s = s theta. Remember that when theta | +c | has nonzero imaginary part, the corresponding | +c | Ritz vector is stored across two columns of Z. | +c %------------------------------------------------% +c + iconj = 0 + do 110 j=1, nconv + if (workl(iheigi+j-1) .eq. zero) then + workev(j) = workl(invsub+(j-1)*ldq+ncv-1) / + & workl(iheigr+j-1) + else if (iconj .eq. 0) then + temp = dlapy2( 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 + end if + 110 continue +c +c %---------------------------------------% +c | Perform a rank one update to Z and | +c | purify all the Ritz vectors together. | +c %---------------------------------------% +c + call dger (n, nconv, one, resid, 1, workev, 1, z, ldz) +c + end if +c + 9000 continue +c + return +c +c %---------------% +c | End of DNEUPD | +c %---------------% +c + end diff --git a/src/rigraph/vendor/arpack/dngets.f b/src/rigraph/vendor/arpack/dngets.f new file mode 100644 index 0000000..0202090 --- /dev/null +++ b/src/rigraph/vendor/arpack/dngets.f @@ -0,0 +1,231 @@ +c----------------------------------------------------------------------- +c\BeginDoc +c +c\Name: igraphdngets +c +c\Description: +c Given the eigenvalues of the upper Hessenberg matrix H, +c computes the NP shifts AMU that are zeros of the polynomial of +c degree NP which filters out components of the unwanted eigenvectors +c corresponding to the AMU's based on some given criteria. +c +c NOTE: call this even in the case of user specified shifts in order +c to sort the eigenvalues, and error bounds of H for later use. +c +c\Usage: +c call igraphdngets +c ( ISHIFT, WHICH, KEV, NP, RITZR, RITZI, BOUNDS, SHIFTR, SHIFTI ) +c +c\Arguments +c ISHIFT Integer. (INPUT) +c Method for selecting the implicit shifts at each iteration. +c ISHIFT = 0: user specified shifts +c ISHIFT = 1: exact shift with respect to the matrix H. +c +c WHICH Character*2. (INPUT) +c Shift selection criteria. +c 'LM' -> want the KEV eigenvalues of largest magnitude. +c 'SM' -> want the KEV eigenvalues of smallest magnitude. +c 'LR' -> want the KEV eigenvalues of largest real part. +c 'SR' -> want the KEV eigenvalues of smallest real part. +c 'LI' -> want the KEV eigenvalues of largest imaginary part. +c 'SI' -> want the KEV eigenvalues of smallest imaginary part. +c +c KEV Integer. (INPUT/OUTPUT) +c INPUT: KEV+NP is the size of the matrix H. +c OUTPUT: Possibly increases KEV by one to keep complex conjugate +c pairs together. +c +c NP Integer. (INPUT/OUTPUT) +c Number of implicit shifts to be computed. +c OUTPUT: Possibly decreases NP by one to keep complex conjugate +c pairs together. +c +c RITZR, Double precision array of length KEV+NP. (INPUT/OUTPUT) +c RITZI On INPUT, RITZR and RITZI contain the real and imaginary +c parts of the eigenvalues of H. +c On OUTPUT, RITZR and RITZI are sorted so that the unwanted +c eigenvalues are in the first NP locations and the wanted +c portion is in the last KEV locations. When exact shifts are +c selected, the unwanted part corresponds to the shifts to +c be applied. Also, if ISHIFT .eq. 1, the unwanted eigenvalues +c are further sorted so that the ones with largest Ritz values +c are first. +c +c BOUNDS Double precision array of length KEV+NP. (INPUT/OUTPUT) +c Error bounds corresponding to the ordering in RITZ. +c +c SHIFTR, SHIFTI *** USE deprecated as of version 2.1. *** +c +c +c\EndDoc +c +c----------------------------------------------------------------------- +c +c\BeginLib +c +c\Local variables: +c xxxxxx real +c +c\Routines called: +c igraphdsortc ARPACK sorting routine. +c dcopy Level 1 BLAS that copies one vector to another . +c +c\Author +c Danny Sorensen Phuong Vu +c Richard Lehoucq CRPC / Rice University +c Dept. of Computational & Houston, Texas +c Applied Mathematics +c Rice University +c Houston, Texas +c +c\Revision history: +c xx/xx/92: Version ' 2.1' +c +c\SCCS Information: @(#) +c FILE: ngets.F SID: 2.3 DATE OF SID: 4/20/96 RELEASE: 2 +c +c\Remarks +c 1. xxxx +c +c\EndLib +c +c----------------------------------------------------------------------- +c + subroutine igraphdngets ( ishift, which, kev, np, ritzr, ritzi, + & bounds, shiftr, shifti ) +c +c %----------------------------------------------------% +c | Include files for debugging and timing information | +c %----------------------------------------------------% +c + include 'debug.h' + include 'stat.h' +c +c %------------------% +c | Scalar Arguments | +c %------------------% +c + character*2 which + integer ishift, kev, np +c +c %-----------------% +c | Array Arguments | +c %-----------------% +c + Double precision + & bounds(kev+np), ritzr(kev+np), ritzi(kev+np), + & shiftr(1), shifti(1) +c +c %------------% +c | Parameters | +c %------------% +c + Double precision + & one, zero + parameter (one = 1.0, zero = 0.0) +c +c %---------------% +c | Local Scalars | +c %---------------% +c + integer msglvl +c +c %----------------------% +c | External Subroutines | +c %----------------------% +c + external dcopy, igraphdsortc, igraphsecond +c +c %----------------------% +c | Intrinsics Functions | +c %----------------------% +c + intrinsic abs +c +c %-----------------------% +c | Executable Statements | +c %-----------------------% +c +c %-------------------------------% +c | Initialize timing statistics | +c | & message level for debugging | +c %-------------------------------% +c + call igraphsecond (t0) + msglvl = mngets +c +c %----------------------------------------------------% +c | LM, SM, LR, SR, LI, SI case. | +c | Sort the eigenvalues of H into the desired order | +c | and apply the resulting order to BOUNDS. | +c | The eigenvalues are sorted so that the wanted part | +c | are always in the last KEV locations. | +c | We first do a pre-processing sort in order to keep | +c | complex conjugate pairs together | +c %----------------------------------------------------% +c + if (which .eq. 'LM') then + call igraphdsortc ('LR', .true., kev+np, ritzr, ritzi, bounds) + else if (which .eq. 'SM') then + call igraphdsortc ('SR', .true., kev+np, ritzr, ritzi, bounds) + else if (which .eq. 'LR') then + call igraphdsortc ('LM', .true., kev+np, ritzr, ritzi, bounds) + else if (which .eq. 'SR') then + call igraphdsortc ('SM', .true., kev+np, ritzr, ritzi, bounds) + else if (which .eq. 'LI') then + call igraphdsortc ('LM', .true., kev+np, ritzr, ritzi, bounds) + else if (which .eq. 'SI') then + call igraphdsortc ('SM', .true., kev+np, ritzr, ritzi, bounds) + end if +c + call igraphdsortc (which, .true., kev+np, ritzr, ritzi, bounds) +c +c %-------------------------------------------------------% +c | Increase KEV by one if the ( ritzr(np),ritzi(np) ) | +c | = ( ritzr(np+1),-ritzi(np+1) ) and ritz(np) .ne. zero | +c | Accordingly decrease NP by one. In other words keep | +c | complex conjugate pairs together. | +c %-------------------------------------------------------% +c + if ( ( ritzr(np+1) - ritzr(np) ) .eq. zero + & .and. ( ritzi(np+1) + ritzi(np) ) .eq. zero ) then + np = np - 1 + kev = kev + 1 + end if +c + if ( ishift .eq. 1 ) then +c +c %-------------------------------------------------------% +c | Sort the unwanted Ritz values used as shifts so that | +c | the ones with largest Ritz estimates are first | +c | This will tend to minimize the effects of the | +c | forward instability of the iteration when they shifts | +c | are applied in subroutine igraphdnapps. | +c | Be careful and use 'SR' since we want to sort BOUNDS! | +c %-------------------------------------------------------% +c + call igraphdsortc ( 'SR', .true., np, bounds, ritzr, ritzi ) + end if +c + call igraphsecond (t1) + tngets = tngets + (t1 - t0) +c + if (msglvl .gt. 0) then + call igraphivout (logfil, 1, [kev], ndigit, '_ngets: KEV is') + call igraphivout (logfil, 1, [np], ndigit, '_ngets: NP is') + call igraphdvout (logfil, kev+np, ritzr, ndigit, + & '_ngets: Eigenvalues of current H matrix -- real part') + call igraphdvout (logfil, kev+np, ritzi, ndigit, + & '_ngets: Eigenvalues of current H matrix -- imag part') + call igraphdvout (logfil, kev+np, bounds, ndigit, + & '_ngets: Ritz estimates of the current KEV+NP Ritz values') + end if +c + return +c +c %---------------% +c | End of igraphdngets | +c %---------------% +c + end diff --git a/src/rigraph/vendor/arpack/dsaitr.f b/src/rigraph/vendor/arpack/dsaitr.f new file mode 100644 index 0000000..4a4698c --- /dev/null +++ b/src/rigraph/vendor/arpack/dsaitr.f @@ -0,0 +1,854 @@ +c----------------------------------------------------------------------- +c\BeginDoc +c +c\Name: igraphdsaitr +c +c\Description: +c Reverse communication interface for applying NP additional steps to +c a K step symmetric Arnoldi factorization. +c +c Input: OP*V_{k} - V_{k}*H = r_{k}*e_{k}^T +c +c with (V_{k}^T)*B*V_{k} = I, (V_{k}^T)*B*r_{k} = 0. +c +c Output: OP*V_{k+p} - V_{k+p}*H = r_{k+p}*e_{k+p}^T +c +c with (V_{k+p}^T)*B*V_{k+p} = I, (V_{k+p}^T)*B*r_{k+p} = 0. +c +c where OP and B are as in igraphdsaupd. The B-norm of r_{k+p} is also +c computed and returned. +c +c\Usage: +c call igraphdsaitr +c ( IDO, BMAT, N, K, NP, MODE, RESID, RNORM, V, LDV, H, LDH, +c IPNTR, WORKD, INFO ) +c +c\Arguments +c IDO Integer. (INPUT/OUTPUT) +c Reverse communication flag. +c ------------------------------------------------------------- +c IDO = 0: first call to the reverse communication interface +c IDO = -1: compute Y = OP * X where +c IPNTR(1) is the pointer into WORK for X, +c IPNTR(2) is the pointer into WORK for Y. +c This is for the restart phase to force the new +c starting vector into the range of OP. +c IDO = 1: compute Y = OP * X where +c IPNTR(1) is the pointer into WORK for X, +c IPNTR(2) is the pointer into WORK for Y, +c IPNTR(3) is the pointer into WORK for B * X. +c IDO = 2: compute Y = B * X where +c IPNTR(1) is the pointer into WORK for X, +c IPNTR(2) is the pointer into WORK for Y. +c IDO = 99: done +c ------------------------------------------------------------- +c When the routine is used in the "shift-and-invert" mode, the +c vector B * Q is already available and does not need to be +c recomputed in forming OP * Q. +c +c BMAT Character*1. (INPUT) +c BMAT specifies the type of matrix B that defines the +c semi-inner product for the operator OP. See igraphdsaupd. +c B = 'I' -> standard eigenvalue problem A*x = lambda*x +c B = 'G' -> generalized eigenvalue problem A*x = lambda*M*x +c +c N Integer. (INPUT) +c Dimension of the eigenproblem. +c +c K Integer. (INPUT) +c Current order of H and the number of columns of V. +c +c NP Integer. (INPUT) +c Number of additional Arnoldi steps to take. +c +c MODE Integer. (INPUT) +c Signifies which form for "OP". If MODE=2 then +c a reduction in the number of B matrix vector multiplies +c is possible since the B-norm of OP*x is equivalent to +c the inv(B)-norm of A*x. +c +c RESID Double precision array of length N. (INPUT/OUTPUT) +c On INPUT: RESID contains the residual vector r_{k}. +c On OUTPUT: RESID contains the residual vector r_{k+p}. +c +c RNORM Double precision scalar. (INPUT/OUTPUT) +c On INPUT the B-norm of r_{k}. +c On OUTPUT the B-norm of the updated residual r_{k+p}. +c +c V Double precision N by K+NP array. (INPUT/OUTPUT) +c On INPUT: V contains the Arnoldi vectors in the first K +c columns. +c On OUTPUT: V contains the new NP Arnoldi vectors in the next +c NP columns. The first K columns are unchanged. +c +c LDV Integer. (INPUT) +c Leading dimension of V exactly as declared in the calling +c program. +c +c H Double precision (K+NP) by 2 array. (INPUT/OUTPUT) +c H is used to store the generated symmetric tridiagonal matrix +c with the subdiagonal in the first column starting at H(2,1) +c and the main diagonal in the igraphsecond column. +c +c LDH Integer. (INPUT) +c Leading dimension of H exactly as declared in the calling +c program. +c +c IPNTR Integer array of length 3. (OUTPUT) +c Pointer to mark the starting locations in the WORK for +c vectors used by the Arnoldi iteration. +c ------------------------------------------------------------- +c IPNTR(1): pointer to the current operand vector X. +c IPNTR(2): pointer to the current result vector Y. +c IPNTR(3): pointer to the vector B * X when used in the +c shift-and-invert mode. X is the current operand. +c ------------------------------------------------------------- +c +c WORKD Double precision work array of length 3*N. (REVERSE COMMUNICATION) +c Distributed array to be used in the basic Arnoldi iteration +c for reverse communication. The calling program should not +c use WORKD as temporary workspace during the iteration !!!!!! +c On INPUT, WORKD(1:N) = B*RESID where RESID is associated +c with the K step Arnoldi factorization. Used to save some +c computation at the first step. +c On OUTPUT, WORKD(1:N) = B*RESID where RESID is associated +c with the K+NP step Arnoldi factorization. +c +c INFO Integer. (OUTPUT) +c = 0: Normal exit. +c > 0: Size of an invariant subspace of OP is found that is +c less than K + NP. +c +c\EndDoc +c +c----------------------------------------------------------------------- +c +c\BeginLib +c +c\Local variables: +c xxxxxx real +c +c\Routines called: +c igraphdgetv0 ARPACK routine to generate the initial vector. +c igraphivout ARPACK utility routine that prints integers. +c igraphdmout ARPACK utility routine that prints matrices. +c igraphdvout ARPACK utility routine that prints vectors. +c dlamch LAPACK routine that determines machine constants. +c dlascl LAPACK routine for careful scaling of a matrix. +c dgemv Level 2 BLAS routine for matrix vector multiplication. +c daxpy Level 1 BLAS that computes a vector triad. +c dscal Level 1 BLAS that scales a vector. +c dcopy Level 1 BLAS that copies one vector to another . +c ddot Level 1 BLAS that computes the scalar product of two vectors. +c dnrm2 Level 1 BLAS that computes the norm of a vector. +c +c\Author +c Danny Sorensen Phuong Vu +c Richard Lehoucq CRPC / Rice University +c Dept. of Computational & Houston, Texas +c Applied Mathematics +c Rice University +c Houston, Texas +c +c\Revision history: +c xx/xx/93: Version ' 2.4' +c +c\SCCS Information: @(#) +c FILE: saitr.F SID: 2.6 DATE OF SID: 8/28/96 RELEASE: 2 +c +c\Remarks +c The algorithm implemented is: +c +c restart = .false. +c Given V_{k} = [v_{1}, ..., v_{k}], r_{k}; +c r_{k} contains the initial residual vector even for k = 0; +c Also assume that rnorm = || B*r_{k} || and B*r_{k} are already +c computed by the calling program. +c +c betaj = rnorm ; p_{k+1} = B*r_{k} ; +c For j = k+1, ..., k+np Do +c 1) if ( betaj < tol ) stop or restart depending on j. +c if ( restart ) generate a new starting vector. +c 2) v_{j} = r(j-1)/betaj; V_{j} = [V_{j-1}, v_{j}]; +c p_{j} = p_{j}/betaj +c 3) r_{j} = OP*v_{j} where OP is defined as in igraphdsaupd +c For shift-invert mode p_{j} = B*v_{j} is already available. +c wnorm = || OP*v_{j} || +c 4) Compute the j-th step residual vector. +c w_{j} = V_{j}^T * B * OP * v_{j} +c r_{j} = OP*v_{j} - V_{j} * w_{j} +c alphaj <- j-th component of w_{j} +c rnorm = || r_{j} || +c betaj+1 = rnorm +c If (rnorm > 0.717*wnorm) accept step and go back to 1) +c 5) Re-orthogonalization step: +c s = V_{j}'*B*r_{j} +c r_{j} = r_{j} - V_{j}*s; rnorm1 = || r_{j} || +c alphaj = alphaj + s_{j}; +c 6) Iterative refinement step: +c If (rnorm1 > 0.717*rnorm) then +c rnorm = rnorm1 +c accept step and go back to 1) +c Else +c rnorm = rnorm1 +c If this is the first time in step 6), go to 5) +c Else r_{j} lies in the span of V_{j} numerically. +c Set r_{j} = 0 and rnorm = 0; go to 1) +c EndIf +c End Do +c +c\EndLib +c +c----------------------------------------------------------------------- +c + subroutine igraphdsaitr + & (ido, bmat, n, k, np, mode, resid, rnorm, v, ldv, h, ldh, + & ipntr, workd, info) +c +c %----------------------------------------------------% +c | Include files for debugging and timing information | +c %----------------------------------------------------% +c + include 'debug.h' + include 'stat.h' +c +c %------------------% +c | Scalar Arguments | +c %------------------% +c + character bmat*1 + integer ido, info, k, ldh, ldv, n, mode, np + Double precision + & rnorm +c +c %-----------------% +c | Array Arguments | +c %-----------------% +c + integer ipntr(3) + Double precision + & h(ldh,2), resid(n), v(ldv,k+np), workd(3*n) +c +c %------------% +c | Parameters | +c %------------% +c + Double precision + & one, zero + parameter (one = 1.0D+0, zero = 0.0D+0) +c +c %---------------% +c | Local Scalars | +c %---------------% +c + logical first, orth1, orth2, rstart, step3, step4 + integer i, ierr, ipj, irj, ivj, iter, itry, j, msglvl, + & infol, jj + Double precision + & rnorm1, wnorm, safmin, temp1 + save orth1, orth2, rstart, step3, step4, + & ierr, ipj, irj, ivj, iter, itry, j, msglvl, + & rnorm1, safmin, wnorm +c +c %-----------------------% +c | Local Array Arguments | +c %-----------------------% +c + Double precision + & xtemp(2) +c +c %----------------------% +c | External Subroutines | +c %----------------------% +c + external daxpy, dcopy, dscal, dgemv, igraphdgetv0, + & igraphdvout, igraphdmout, + & dlascl, igraphivout, igraphsecond +c +c %--------------------% +c | External Functions | +c %--------------------% +c + Double precision + & ddot, dnrm2, dlamch + external ddot, dnrm2, dlamch +c +c %-----------------% +c | Data statements | +c %-----------------% +c + data first / .true. / +c +c %-----------------------% +c | Executable Statements | +c %-----------------------% +c + if (first) then + first = .false. +c +c %--------------------------------% +c | safmin = safe minimum is such | +c | that 1/sfmin does not overflow | +c %--------------------------------% +c + safmin = dlamch('safmin') + end if +c + if (ido .eq. 0) then +c +c %-------------------------------% +c | Initialize timing statistics | +c | & message level for debugging | +c %-------------------------------% +c + call igraphsecond (t0) + msglvl = msaitr +c +c %------------------------------% +c | Initial call to this routine | +c %------------------------------% +c + info = 0 + step3 = .false. + step4 = .false. + rstart = .false. + orth1 = .false. + orth2 = .false. +c +c %--------------------------------% +c | Pointer to the current step of | +c | the factorization to build | +c %--------------------------------% +c + j = k + 1 +c +c %------------------------------------------% +c | Pointers used for reverse communication | +c | when using WORKD. | +c %------------------------------------------% +c + ipj = 1 + irj = ipj + n + ivj = irj + n + end if +c +c %-------------------------------------------------% +c | When in reverse communication mode one of: | +c | STEP3, STEP4, ORTH1, ORTH2, RSTART | +c | will be .true. | +c | STEP3: return from computing OP*v_{j}. | +c | STEP4: return from computing B-norm of OP*v_{j} | +c | ORTH1: return from computing B-norm of r_{j+1} | +c | ORTH2: return from computing B-norm of | +c | correction to the residual vector. | +c | RSTART: return from OP computations needed by | +c | igraphdgetv0. | +c %-------------------------------------------------% +c + if (step3) go to 50 + if (step4) go to 60 + if (orth1) go to 70 + if (orth2) go to 90 + if (rstart) go to 30 +c +c %------------------------------% +c | Else this is the first step. | +c %------------------------------% +c +c %--------------------------------------------------------------% +c | | +c | A R N O L D I I T E R A T I O N L O O P | +c | | +c | Note: B*r_{j-1} is already in WORKD(1:N)=WORKD(IPJ:IPJ+N-1) | +c %--------------------------------------------------------------% +c + 1000 continue +c + if (msglvl .gt. 2) then + call igraphivout (logfil, 1, [j], ndigit, + & '_saitr: generating Arnoldi vector no.') + call igraphdvout (logfil, 1, [rnorm], ndigit, + & '_saitr: B-norm of the current residual =') + end if +c +c %---------------------------------------------------------% +c | Check for exact zero. Equivalent to determing whether a | +c | j-step Arnoldi factorization is present. | +c %---------------------------------------------------------% +c + if (rnorm .gt. zero) go to 40 +c +c %---------------------------------------------------% +c | Invariant subspace found, generate a new starting | +c | vector which is orthogonal to the current Arnoldi | +c | basis and continue the iteration. | +c %---------------------------------------------------% +c + if (msglvl .gt. 0) then + call igraphivout (logfil, 1, [j], ndigit, + & '_saitr: ****** restart at step ******') + end if +c +c %---------------------------------------------% +c | ITRY is the loop variable that controls the | +c | maximum amount of times that a restart is | +c | attempted. NRSTRT is used by stat.h | +c %---------------------------------------------% +c + nrstrt = nrstrt + 1 + itry = 1 + 20 continue + rstart = .true. + ido = 0 + 30 continue +c +c %--------------------------------------% +c | If in reverse communication mode and | +c | RSTART = .true. flow returns here. | +c %--------------------------------------% +c + call igraphdgetv0 (ido, bmat, itry, .false., n, j, v, ldv, + & resid, rnorm, ipntr, workd, ierr) + if (ido .ne. 99) go to 9000 + if (ierr .lt. 0) then + itry = itry + 1 + if (itry .le. 3) go to 20 +c +c %------------------------------------------------% +c | Give up after several restart attempts. | +c | Set INFO to the size of the invariant subspace | +c | which spans OP and exit. | +c %------------------------------------------------% +c + info = j - 1 + call igraphsecond (t1) + tsaitr = tsaitr + (t1 - t0) + ido = 99 + go to 9000 + end if +c + 40 continue +c +c %---------------------------------------------------------% +c | STEP 2: v_{j} = r_{j-1}/rnorm and p_{j} = p_{j}/rnorm | +c | Note that p_{j} = B*r_{j-1}. In order to avoid overflow | +c | when reciprocating a small RNORM, test against lower | +c | machine bound. | +c %---------------------------------------------------------% +c + call dcopy (n, resid, 1, v(1,j), 1) + if (rnorm .ge. safmin) then + temp1 = one / rnorm + call dscal (n, temp1, v(1,j), 1) + call dscal (n, temp1, workd(ipj), 1) + else +c +c %-----------------------------------------% +c | To scale both v_{j} and p_{j} carefully | +c | use LAPACK routine SLASCL | +c %-----------------------------------------% +c + call dlascl ('General', i, i, rnorm, one, n, 1, + & v(1,j), n, infol) + call dlascl ('General', i, i, rnorm, one, n, 1, + & workd(ipj), n, infol) + end if +c +c %------------------------------------------------------% +c | STEP 3: r_{j} = OP*v_{j}; Note that p_{j} = B*v_{j} | +c | Note that this is not quite yet r_{j}. See STEP 4 | +c %------------------------------------------------------% +c + step3 = .true. + nopx = nopx + 1 + call igraphsecond (t2) + call dcopy (n, v(1,j), 1, workd(ivj), 1) + ipntr(1) = ivj + ipntr(2) = irj + ipntr(3) = ipj + ido = 1 +c +c %-----------------------------------% +c | Exit in order to compute OP*v_{j} | +c %-----------------------------------% +c + go to 9000 + 50 continue +c +c %-----------------------------------% +c | Back from reverse communication; | +c | WORKD(IRJ:IRJ+N-1) := OP*v_{j}. | +c %-----------------------------------% +c + call igraphsecond (t3) + tmvopx = tmvopx + (t3 - t2) +c + step3 = .false. +c +c %------------------------------------------% +c | Put another copy of OP*v_{j} into RESID. | +c %------------------------------------------% +c + call dcopy (n, workd(irj), 1, resid, 1) +c +c %-------------------------------------------% +c | STEP 4: Finish extending the symmetric | +c | Arnoldi to length j. If MODE = 2 | +c | then B*OP = B*inv(B)*A = A and | +c | we don't need to compute B*OP. | +c | NOTE: If MODE = 2 WORKD(IVJ:IVJ+N-1) is | +c | assumed to have A*v_{j}. | +c %-------------------------------------------% +c + if (mode .eq. 2) go to 65 + call igraphsecond (t2) + if (bmat .eq. 'G') then + nbx = nbx + 1 + step4 = .true. + ipntr(1) = irj + ipntr(2) = ipj + ido = 2 +c +c %-------------------------------------% +c | Exit in order to compute B*OP*v_{j} | +c %-------------------------------------% +c + go to 9000 + else if (bmat .eq. 'I') then + call dcopy(n, resid, 1 , workd(ipj), 1) + end if + 60 continue +c +c %-----------------------------------% +c | Back from reverse communication; | +c | WORKD(IPJ:IPJ+N-1) := B*OP*v_{j}. | +c %-----------------------------------% +c + if (bmat .eq. 'G') then + call igraphsecond (t3) + tmvbx = tmvbx + (t3 - t2) + end if +c + step4 = .false. +c +c %-------------------------------------% +c | The following is needed for STEP 5. | +c | Compute the B-norm of OP*v_{j}. | +c %-------------------------------------% +c + 65 continue + if (mode .eq. 2) then +c +c %----------------------------------% +c | Note that the B-norm of OP*v_{j} | +c | is the inv(B)-norm of A*v_{j}. | +c %----------------------------------% +c + wnorm = ddot (n, resid, 1, workd(ivj), 1) + wnorm = sqrt(abs(wnorm)) + else if (bmat .eq. 'G') then + wnorm = ddot (n, resid, 1, workd(ipj), 1) + wnorm = sqrt(abs(wnorm)) + else if (bmat .eq. 'I') then + wnorm = dnrm2(n, resid, 1) + end if +c +c %-----------------------------------------% +c | Compute the j-th residual corresponding | +c | to the j step factorization. | +c | Use Classical Gram Schmidt and compute: | +c | w_{j} <- V_{j}^T * B * OP * v_{j} | +c | r_{j} <- OP*v_{j} - V_{j} * w_{j} | +c %-----------------------------------------% +c +c +c %------------------------------------------% +c | Compute the j Fourier coefficients w_{j} | +c | WORKD(IPJ:IPJ+N-1) contains B*OP*v_{j}. | +c %------------------------------------------% +c + if (mode .ne. 2 ) then + call dgemv('T', n, j, one, v, ldv, workd(ipj), 1, zero, + & workd(irj), 1) + else if (mode .eq. 2) then + call dgemv('T', n, j, one, v, ldv, workd(ivj), 1, zero, + & workd(irj), 1) + end if +c +c %--------------------------------------% +c | Orthgonalize r_{j} against V_{j}. | +c | RESID contains OP*v_{j}. See STEP 3. | +c %--------------------------------------% +c + call dgemv('N', n, j, -one, v, ldv, workd(irj), 1, one, + & resid, 1) +c +c %--------------------------------------% +c | Extend H to have j rows and columns. | +c %--------------------------------------% +c + h(j,2) = workd(irj + j - 1) + if (j .eq. 1 .or. rstart) then + h(j,1) = zero + else + h(j,1) = rnorm + end if + call igraphsecond (t4) +c + orth1 = .true. + iter = 0 +c + call igraphsecond (t2) + if (bmat .eq. 'G') then + nbx = nbx + 1 + call dcopy (n, resid, 1, workd(irj), 1) + ipntr(1) = irj + ipntr(2) = ipj + ido = 2 +c +c %----------------------------------% +c | Exit in order to compute B*r_{j} | +c %----------------------------------% +c + go to 9000 + else if (bmat .eq. 'I') then + call dcopy (n, resid, 1, workd(ipj), 1) + end if + 70 continue +c +c %---------------------------------------------------% +c | Back from reverse communication if ORTH1 = .true. | +c | WORKD(IPJ:IPJ+N-1) := B*r_{j}. | +c %---------------------------------------------------% +c + if (bmat .eq. 'G') then + call igraphsecond (t3) + tmvbx = tmvbx + (t3 - t2) + end if +c + orth1 = .false. +c +c %------------------------------% +c | Compute the B-norm of r_{j}. | +c %------------------------------% +c + if (bmat .eq. 'G') then + rnorm = ddot (n, resid, 1, workd(ipj), 1) + rnorm = sqrt(abs(rnorm)) + else if (bmat .eq. 'I') then + rnorm = dnrm2(n, resid, 1) + end if +c +c %-----------------------------------------------------------% +c | STEP 5: Re-orthogonalization / Iterative refinement phase | +c | Maximum NITER_ITREF tries. | +c | | +c | s = V_{j}^T * B * r_{j} | +c | r_{j} = r_{j} - V_{j}*s | +c | alphaj = alphaj + s_{j} | +c | | +c | The stopping criteria used for iterative refinement is | +c | discussed in Parlett's book SEP, page 107 and in Gragg & | +c | Reichel ACM TOMS paper; Algorithm 686, Dec. 1990. | +c | Determine if we need to correct the residual. The goal is | +c | to enforce ||v(:,1:j)^T * r_{j}|| .le. eps * || r_{j} || | +c %-----------------------------------------------------------% +c + if (rnorm .gt. 0.717*wnorm) go to 100 + nrorth = nrorth + 1 +c +c %---------------------------------------------------% +c | Enter the Iterative refinement phase. If further | +c | refinement is necessary, loop back here. The loop | +c | variable is ITER. Perform a step of Classical | +c | Gram-Schmidt using all the Arnoldi vectors V_{j} | +c %---------------------------------------------------% +c + 80 continue +c + if (msglvl .gt. 2) then + xtemp(1) = wnorm + xtemp(2) = rnorm + call igraphdvout (logfil, 2, xtemp, ndigit, + & '_saitr: re-orthonalization ; wnorm and rnorm are') + end if +c +c %----------------------------------------------------% +c | Compute V_{j}^T * B * r_{j}. | +c | WORKD(IRJ:IRJ+J-1) = v(:,1:J)'*WORKD(IPJ:IPJ+N-1). | +c %----------------------------------------------------% +c + call dgemv ('T', n, j, one, v, ldv, workd(ipj), 1, + & zero, workd(irj), 1) +c +c %----------------------------------------------% +c | Compute the correction to the residual: | +c | r_{j} = r_{j} - V_{j} * WORKD(IRJ:IRJ+J-1). | +c | The correction to H is v(:,1:J)*H(1:J,1:J) + | +c | v(:,1:J)*WORKD(IRJ:IRJ+J-1)*e'_j, but only | +c | H(j,j) is updated. | +c %----------------------------------------------% +c + call dgemv ('N', n, j, -one, v, ldv, workd(irj), 1, + & one, resid, 1) +c + if (j .eq. 1 .or. rstart) h(j,1) = zero + h(j,2) = h(j,2) + workd(irj + j - 1) +c + orth2 = .true. + call igraphsecond (t2) + if (bmat .eq. 'G') then + nbx = nbx + 1 + call dcopy (n, resid, 1, workd(irj), 1) + ipntr(1) = irj + ipntr(2) = ipj + ido = 2 +c +c %-----------------------------------% +c | Exit in order to compute B*r_{j}. | +c | r_{j} is the corrected residual. | +c %-----------------------------------% +c + go to 9000 + else if (bmat .eq. 'I') then + call dcopy (n, resid, 1, workd(ipj), 1) + end if + 90 continue +c +c %---------------------------------------------------% +c | Back from reverse communication if ORTH2 = .true. | +c %---------------------------------------------------% +c + if (bmat .eq. 'G') then + call igraphsecond (t3) + tmvbx = tmvbx + (t3 - t2) + end if +c +c %-----------------------------------------------------% +c | Compute the B-norm of the corrected residual r_{j}. | +c %-----------------------------------------------------% +c + if (bmat .eq. 'G') then + rnorm1 = ddot (n, resid, 1, workd(ipj), 1) + rnorm1 = sqrt(abs(rnorm1)) + else if (bmat .eq. 'I') then + rnorm1 = dnrm2(n, resid, 1) + end if +c + if (msglvl .gt. 0 .and. iter .gt. 0) then + call igraphivout (logfil, 1, [j], ndigit, + & '_saitr: Iterative refinement for Arnoldi residual') + if (msglvl .gt. 2) then + xtemp(1) = rnorm + xtemp(2) = rnorm1 + call igraphdvout (logfil, 2, xtemp, ndigit, + & '_saitr: iterative refinement ; rnorm and rnorm1 are') + end if + end if +c +c %-----------------------------------------% +c | Determine if we need to perform another | +c | step of re-orthogonalization. | +c %-----------------------------------------% +c + if (rnorm1 .gt. 0.717*rnorm) then +c +c %--------------------------------% +c | No need for further refinement | +c %--------------------------------% +c + rnorm = rnorm1 +c + else +c +c %-------------------------------------------% +c | Another step of iterative refinement step | +c | is required. NITREF is used by stat.h | +c %-------------------------------------------% +c + nitref = nitref + 1 + rnorm = rnorm1 + iter = iter + 1 + if (iter .le. 1) go to 80 +c +c %-------------------------------------------------% +c | Otherwise RESID is numerically in the span of V | +c %-------------------------------------------------% +c + do 95 jj = 1, n + resid(jj) = zero + 95 continue + rnorm = zero + end if +c +c %----------------------------------------------% +c | Branch here directly if iterative refinement | +c | wasn't necessary or after at most NITER_REF | +c | steps of iterative refinement. | +c %----------------------------------------------% +c + 100 continue +c + rstart = .false. + orth2 = .false. +c + call igraphsecond (t5) + titref = titref + (t5 - t4) +c +c %----------------------------------------------------------% +c | Make sure the last off-diagonal element is non negative | +c | If not perform a similarity transformation on H(1:j,1:j) | +c | and scale v(:,j) by -1. | +c %----------------------------------------------------------% +c + if (h(j,1) .lt. zero) then + h(j,1) = -h(j,1) + if ( j .lt. k+np) then + call dscal(n, -one, v(1,j+1), 1) + else + call dscal(n, -one, resid, 1) + end if + end if +c +c %------------------------------------% +c | STEP 6: Update j = j+1; Continue | +c %------------------------------------% +c + j = j + 1 + if (j .gt. k+np) then + call igraphsecond (t1) + tsaitr = tsaitr + (t1 - t0) + ido = 99 +c + if (msglvl .gt. 1) then + call igraphdvout (logfil, k+np, h(1,2), ndigit, + & '_saitr: main diagonal of matrix H of step K+NP.') + if (k+np .gt. 1) then + call igraphdvout (logfil, k+np-1, h(2,1), ndigit, + & '_saitr: sub diagonal of matrix H of step K+NP.') + end if + end if +c + go to 9000 + end if +c +c %--------------------------------------------------------% +c | Loop back to extend the factorization by another step. | +c %--------------------------------------------------------% +c + go to 1000 +c +c %---------------------------------------------------------------% +c | | +c | E N D O F M A I N I T E R A T I O N L O O P | +c | | +c %---------------------------------------------------------------% +c + 9000 continue + return +c +c %---------------% +c | End of igraphdsaitr | +c %---------------% +c + end diff --git a/src/rigraph/vendor/arpack/dsapps.f b/src/rigraph/vendor/arpack/dsapps.f new file mode 100644 index 0000000..850e3fd --- /dev/null +++ b/src/rigraph/vendor/arpack/dsapps.f @@ -0,0 +1,516 @@ +c----------------------------------------------------------------------- +c\BeginDoc +c +c\Name: igraphdsapps +c +c\Description: +c Given the Arnoldi factorization +c +c A*V_{k} - V_{k}*H_{k} = r_{k+p}*e_{k+p}^T, +c +c apply NP shifts implicitly resulting in +c +c A*(V_{k}*Q) - (V_{k}*Q)*(Q^T* H_{k}*Q) = r_{k+p}*e_{k+p}^T * Q +c +c where Q is an orthogonal matrix of order KEV+NP. Q is the product of +c rotations resulting from the NP bulge chasing sweeps. The updated Arnoldi +c factorization becomes: +c +c A*VNEW_{k} - VNEW_{k}*HNEW_{k} = rnew_{k}*e_{k}^T. +c +c\Usage: +c call igraphdsapps +c ( N, KEV, NP, SHIFT, V, LDV, H, LDH, RESID, Q, LDQ, WORKD ) +c +c\Arguments +c N Integer. (INPUT) +c Problem size, i.e. dimension of matrix A. +c +c KEV Integer. (INPUT) +c INPUT: KEV+NP is the size of the input matrix H. +c OUTPUT: KEV is the size of the updated matrix HNEW. +c +c NP Integer. (INPUT) +c Number of implicit shifts to be applied. +c +c SHIFT Double precision array of length NP. (INPUT) +c The shifts to be applied. +c +c V Double precision N by (KEV+NP) array. (INPUT/OUTPUT) +c INPUT: V contains the current KEV+NP Arnoldi vectors. +c OUTPUT: VNEW = V(1:n,1:KEV); the updated Arnoldi vectors +c are in the first KEV columns of V. +c +c LDV Integer. (INPUT) +c Leading dimension of V exactly as declared in the calling +c program. +c +c H Double precision (KEV+NP) by 2 array. (INPUT/OUTPUT) +c INPUT: H contains the symmetric tridiagonal matrix of the +c Arnoldi factorization with the subdiagonal in the 1st column +c starting at H(2,1) and the main diagonal in the 2nd column. +c OUTPUT: H contains the updated tridiagonal matrix in the +c KEV leading submatrix. +c +c LDH Integer. (INPUT) +c Leading dimension of H exactly as declared in the calling +c program. +c +c RESID Double precision array of length (N). (INPUT/OUTPUT) +c INPUT: RESID contains the the residual vector r_{k+p}. +c OUTPUT: RESID is the updated residual vector rnew_{k}. +c +c Q Double precision KEV+NP by KEV+NP work array. (WORKSPACE) +c Work array used to accumulate the rotations during the bulge +c chase sweep. +c +c LDQ Integer. (INPUT) +c Leading dimension of Q exactly as declared in the calling +c program. +c +c WORKD Double precision work array of length 2*N. (WORKSPACE) +c Distributed array used in the application of the accumulated +c orthogonal matrix Q. +c +c\EndDoc +c +c----------------------------------------------------------------------- +c +c\BeginLib +c +c\Local variables: +c xxxxxx real +c +c\References: +c 1. D.C. Sorensen, "Implicit Application of Polynomial Filters in +c a k-Step Arnoldi Method", SIAM J. Matr. Anal. Apps., 13 (1992), +c pp 357-385. +c 2. R.B. Lehoucq, "Analysis and Implementation of an Implicitly +c Restarted Arnoldi Iteration", Rice University Technical Report +c TR95-13, Department of Computational and Applied Mathematics. +c +c\Routines called: +c igraphivout ARPACK utility routine that prints integers. +c igraphsecond ARPACK utility routine for timing. +c igraphdvout ARPACK utility routine that prints vectors. +c dlamch LAPACK routine that determines machine constants. +c dlartg LAPACK Givens rotation construction routine. +c dlacpy LAPACK matrix copy routine. +c dlaset LAPACK matrix initialization routine. +c dgemv Level 2 BLAS routine for matrix vector multiplication. +c daxpy Level 1 BLAS that computes a vector triad. +c dcopy Level 1 BLAS that copies one vector to another. +c dscal Level 1 BLAS that scales a vector. +c +c\Author +c Danny Sorensen Phuong Vu +c Richard Lehoucq CRPC / Rice University +c Dept. of Computational & Houston, Texas +c Applied Mathematics +c Rice University +c Houston, Texas +c +c\Revision history: +c 12/16/93: Version ' 2.1' +c +c\SCCS Information: @(#) +c FILE: sapps.F SID: 2.5 DATE OF SID: 4/19/96 RELEASE: 2 +c +c\Remarks +c 1. In this version, each shift is applied to all the subblocks of +c the tridiagonal matrix H and not just to the submatrix that it +c comes from. This routine assumes that the subdiagonal elements +c of H that are stored in h(1:kev+np,1) are nonegative upon input +c and enforce this condition upon output. This version incorporates +c deflation. See code for documentation. +c +c\EndLib +c +c----------------------------------------------------------------------- +c + subroutine igraphdsapps + & ( n, kev, np, shift, v, ldv, h, ldh, resid, q, ldq, workd ) +c +c %----------------------------------------------------% +c | Include files for debugging and timing information | +c %----------------------------------------------------% +c + include 'debug.h' + include 'stat.h' +c +c %------------------% +c | Scalar Arguments | +c %------------------% +c + integer kev, ldh, ldq, ldv, n, np +c +c %-----------------% +c | Array Arguments | +c %-----------------% +c + Double precision + & h(ldh,2), q(ldq,kev+np), resid(n), shift(np), + & v(ldv,kev+np), workd(2*n) +c +c %------------% +c | Parameters | +c %------------% +c + Double precision + & one, zero + parameter (one = 1.0D+0, zero = 0.0D+0) +c +c %---------------% +c | Local Scalars | +c %---------------% +c + integer i, iend, istart, itop, j, jj, kplusp, msglvl + logical first + Double precision + & a1, a2, a3, a4, big, c, epsmch, f, g, r, s + save epsmch, first +c +c +c %----------------------% +c | External Subroutines | +c %----------------------% +c + external daxpy, dcopy, dscal, dlacpy, dlartg, dlaset, + & igraphdvout, igraphivout, igraphsecond, dgemv +c +c %--------------------% +c | External Functions | +c %--------------------% +c + Double precision + & dlamch + external dlamch +c +c %----------------------% +c | Intrinsics Functions | +c %----------------------% +c + intrinsic abs +c +c %----------------% +c | Data statments | +c %----------------% +c + data first / .true. / +c +c %-----------------------% +c | Executable Statements | +c %-----------------------% +c + if (first) then + epsmch = dlamch('Epsilon-Machine') + first = .false. + end if + itop = 1 +c +c %-------------------------------% +c | Initialize timing statistics | +c | & message level for debugging | +c %-------------------------------% +c + call igraphsecond (t0) + msglvl = msapps +c + kplusp = kev + np +c +c %----------------------------------------------% +c | Initialize Q to the identity matrix of order | +c | kplusp used to accumulate the rotations. | +c %----------------------------------------------% +c + call dlaset ('All', kplusp, kplusp, zero, one, q, ldq) +c +c %----------------------------------------------% +c | Quick return if there are no shifts to apply | +c %----------------------------------------------% +c + if (np .eq. 0) go to 9000 +c +c %----------------------------------------------------------% +c | Apply the np shifts implicitly. Apply each shift to the | +c | whole matrix and not just to the submatrix from which it | +c | comes. | +c %----------------------------------------------------------% +c + do 90 jj = 1, np +c + istart = itop +c +c %----------------------------------------------------------% +c | Check for splitting and deflation. Currently we consider | +c | an off-diagonal element h(i+1,1) negligible if | +c | h(i+1,1) .le. epsmch*( |h(i,2)| + |h(i+1,2)| ) | +c | for i=1:KEV+NP-1. | +c | If above condition tests true then we set h(i+1,1) = 0. | +c | Note that h(1:KEV+NP,1) are assumed to be non negative. | +c %----------------------------------------------------------% +c + 20 continue +c +c %------------------------------------------------% +c | The following loop exits early if we encounter | +c | a negligible off diagonal element. | +c %------------------------------------------------% +c + do 30 i = istart, kplusp-1 + big = abs(h(i,2)) + abs(h(i+1,2)) + if (h(i+1,1) .le. epsmch*big) then + if (msglvl .gt. 0) then + call igraphivout (logfil, 1, i, ndigit, + & '_sapps: deflation at row/column no.') + call igraphivout (logfil, 1, jj, ndigit, + & '_sapps: occured before shift number.') + call igraphdvout (logfil, 1, h(i+1,1), ndigit, + & '_sapps: the corresponding off diagonal element') + end if + h(i+1,1) = zero + iend = i + go to 40 + end if + 30 continue + iend = kplusp + 40 continue +c + if (istart .lt. iend) then +c +c %--------------------------------------------------------% +c | Construct the plane rotation G'(istart,istart+1,theta) | +c | that attempts to drive h(istart+1,1) to zero. | +c %--------------------------------------------------------% +c + f = h(istart,2) - shift(jj) + g = h(istart+1,1) + call dlartg (f, g, c, s, r) +c +c %-------------------------------------------------------% +c | Apply rotation to the left and right of H; | +c | H <- G' * H * G, where G = G(istart,istart+1,theta). | +c | This will create a "bulge". | +c %-------------------------------------------------------% +c + a1 = c*h(istart,2) + s*h(istart+1,1) + a2 = c*h(istart+1,1) + s*h(istart+1,2) + a4 = c*h(istart+1,2) - s*h(istart+1,1) + a3 = c*h(istart+1,1) - s*h(istart,2) + h(istart,2) = c*a1 + s*a2 + h(istart+1,2) = c*a4 - s*a3 + h(istart+1,1) = c*a3 + s*a4 +c +c %----------------------------------------------------% +c | Accumulate the rotation in the matrix Q; Q <- Q*G | +c %----------------------------------------------------% +c + do 60 j = 1, min(istart+jj,kplusp) + a1 = c*q(j,istart) + s*q(j,istart+1) + q(j,istart+1) = - s*q(j,istart) + c*q(j,istart+1) + q(j,istart) = a1 + 60 continue +c +c +c %----------------------------------------------% +c | The following loop chases the bulge created. | +c | Note that the previous rotation may also be | +c | done within the following loop. But it is | +c | kept separate to make the distinction among | +c | the bulge chasing sweeps and the first plane | +c | rotation designed to drive h(istart+1,1) to | +c | zero. | +c %----------------------------------------------% +c + do 70 i = istart+1, iend-1 +c +c %----------------------------------------------% +c | Construct the plane rotation G'(i,i+1,theta) | +c | that zeros the i-th bulge that was created | +c | by G(i-1,i,theta). g represents the bulge. | +c %----------------------------------------------% +c + f = h(i,1) + g = s*h(i+1,1) +c +c %----------------------------------% +c | Final update with G(i-1,i,theta) | +c %----------------------------------% +c + h(i+1,1) = c*h(i+1,1) + call dlartg (f, g, c, s, r) +c +c %-------------------------------------------% +c | The following ensures that h(1:iend-1,1), | +c | the first iend-2 off diagonal of elements | +c | H, remain non negative. | +c %-------------------------------------------% +c + if (r .lt. zero) then + r = -r + c = -c + s = -s + end if +c +c %--------------------------------------------% +c | Apply rotation to the left and right of H; | +c | H <- G * H * G', where G = G(i,i+1,theta) | +c %--------------------------------------------% +c + h(i,1) = r +c + a1 = c*h(i,2) + s*h(i+1,1) + a2 = c*h(i+1,1) + s*h(i+1,2) + a3 = c*h(i+1,1) - s*h(i,2) + a4 = c*h(i+1,2) - s*h(i+1,1) +c + h(i,2) = c*a1 + s*a2 + h(i+1,2) = c*a4 - s*a3 + h(i+1,1) = c*a3 + s*a4 +c +c %----------------------------------------------------% +c | Accumulate the rotation in the matrix Q; Q <- Q*G | +c %----------------------------------------------------% +c + do 50 j = 1, min( j+jj, kplusp ) + a1 = c*q(j,i) + s*q(j,i+1) + q(j,i+1) = - s*q(j,i) + c*q(j,i+1) + q(j,i) = a1 + 50 continue +c + 70 continue +c + end if +c +c %--------------------------% +c | Update the block pointer | +c %--------------------------% +c + istart = iend + 1 +c +c %------------------------------------------% +c | Make sure that h(iend,1) is non-negative | +c | If not then set h(iend,1) <-- -h(iend,1) | +c | and negate the last column of Q. | +c | We have effectively carried out a | +c | similarity on transformation H | +c %------------------------------------------% +c + if (h(iend,1) .lt. zero) then + h(iend,1) = -h(iend,1) + call dscal(kplusp, -one, q(1,iend), 1) + end if +c +c %--------------------------------------------------------% +c | Apply the same shift to the next block if there is any | +c %--------------------------------------------------------% +c + if (iend .lt. kplusp) go to 20 +c +c %-----------------------------------------------------% +c | Check if we can increase the the start of the block | +c %-----------------------------------------------------% +c + do 80 i = itop, kplusp-1 + if (h(i+1,1) .gt. zero) go to 90 + itop = itop + 1 + 80 continue +c +c %-----------------------------------% +c | Finished applying the jj-th shift | +c %-----------------------------------% +c + 90 continue +c +c %------------------------------------------% +c | All shifts have been applied. Check for | +c | more possible deflation that might occur | +c | after the last shift is applied. | +c %------------------------------------------% +c + do 100 i = itop, kplusp-1 + big = abs(h(i,2)) + abs(h(i+1,2)) + if (h(i+1,1) .le. epsmch*big) then + if (msglvl .gt. 0) then + call igraphivout (logfil, 1, i, ndigit, + & '_sapps: deflation at row/column no.') + call igraphdvout (logfil, 1, h(i+1,1), ndigit, + & '_sapps: the corresponding off diagonal element') + end if + h(i+1,1) = zero + end if + 100 continue +c +c %-------------------------------------------------% +c | Compute the (kev+1)-st column of (V*Q) and | +c | temporarily store the result in WORKD(N+1:2*N). | +c | This is not necessary if h(kev+1,1) = 0. | +c %-------------------------------------------------% +c + if ( h(kev+1,1) .gt. zero ) + & call dgemv ('N', n, kplusp, one, v, ldv, + & q(1,kev+1), 1, zero, workd(n+1), 1) +c +c %-------------------------------------------------------% +c | Compute column 1 to kev of (V*Q) in backward order | +c | taking advantage that Q is an upper triangular matrix | +c | with lower bandwidth np. | +c | Place results in v(:,kplusp-kev:kplusp) temporarily. | +c %-------------------------------------------------------% +c + do 130 i = 1, kev + call dgemv ('N', n, kplusp-i+1, one, v, ldv, + & q(1,kev-i+1), 1, zero, workd, 1) + call dcopy (n, workd, 1, v(1,kplusp-i+1), 1) + 130 continue +c +c %-------------------------------------------------% +c | Move v(:,kplusp-kev+1:kplusp) into v(:,1:kev). | +c %-------------------------------------------------% +c + call dlacpy ('All', n, kev, v(1,np+1), ldv, v, ldv) +c +c %--------------------------------------------% +c | Copy the (kev+1)-st column of (V*Q) in the | +c | appropriate place if h(kev+1,1) .ne. zero. | +c %--------------------------------------------% +c + if ( h(kev+1,1) .gt. zero ) + & call dcopy (n, workd(n+1), 1, v(1,kev+1), 1) +c +c %-------------------------------------% +c | Update the residual vector: | +c | r <- sigmak*r + betak*v(:,kev+1) | +c | where | +c | sigmak = (e_{kev+p}'*Q)*e_{kev} | +c | betak = e_{kev+1}'*H*e_{kev} | +c %-------------------------------------% +c + call dscal (n, q(kplusp,kev), resid, 1) + if (h(kev+1,1) .gt. zero) + & call daxpy (n, h(kev+1,1), v(1,kev+1), 1, resid, 1) +c + if (msglvl .gt. 1) then + call igraphdvout (logfil, 1, q(kplusp,kev), ndigit, + & '_sapps: sigmak of the updated residual vector') + call igraphdvout (logfil, 1, h(kev+1,1), ndigit, + & '_sapps: betak of the updated residual vector') + call igraphdvout (logfil, kev, h(1,2), ndigit, + & '_sapps: updated main diagonal of H for next iteration') + if (kev .gt. 1) then + call igraphdvout (logfil, kev-1, h(2,1), ndigit, + & '_sapps: updated sub diagonal of H for next iteration') + end if + end if +c + call igraphsecond (t1) + tsapps = tsapps + (t1 - t0) +c + 9000 continue + return +c +c %---------------% +c | End of igraphdsapps | +c %---------------% +c + end diff --git a/src/rigraph/vendor/arpack/dsaup2.f b/src/rigraph/vendor/arpack/dsaup2.f new file mode 100644 index 0000000..116dd31 --- /dev/null +++ b/src/rigraph/vendor/arpack/dsaup2.f @@ -0,0 +1,853 @@ +c----------------------------------------------------------------------- +c\BeginDoc +c +c\Name: igraphdsaup2 +c +c\Description: +c Intermediate level interface called by igraphdsaupd. +c +c\Usage: +c call igraphdsaup2 +c ( IDO, BMAT, N, WHICH, NEV, NP, TOL, RESID, MODE, IUPD, +c ISHIFT, MXITER, V, LDV, H, LDH, RITZ, BOUNDS, Q, LDQ, WORKL, +c IPNTR, WORKD, INFO ) +c +c\Arguments +c +c IDO, BMAT, N, WHICH, NEV, TOL, RESID: same as defined in igraphdsaupd. +c MODE, ISHIFT, MXITER: see the definition of IPARAM in igraphdsaupd. +c +c NP Integer. (INPUT/OUTPUT) +c Contains the number of implicit shifts to apply during +c each Arnoldi/Lanczos iteration. +c If ISHIFT=1, NP is adjusted dynamically at each iteration +c to accelerate convergence and prevent stagnation. +c This is also roughly equal to the number of matrix-vector +c products (involving the operator OP) per Arnoldi iteration. +c The logic for adjusting is contained within the current +c subroutine. +c If ISHIFT=0, NP is the number of shifts the user needs +c to provide via reverse comunication. 0 < NP < NCV-NEV. +c NP may be less than NCV-NEV since a leading block of the current +c upper Tridiagonal matrix has split off and contains "unwanted" +c Ritz values. +c Upon termination of the IRA iteration, NP contains the number +c of "converged" wanted Ritz values. +c +c IUPD Integer. (INPUT) +c IUPD .EQ. 0: use explicit restart instead implicit update. +c IUPD .NE. 0: use implicit update. +c +c V Double precision N by (NEV+NP) array. (INPUT/OUTPUT) +c The Lanczos basis vectors. +c +c LDV Integer. (INPUT) +c Leading dimension of V exactly as declared in the calling +c program. +c +c H Double precision (NEV+NP) by 2 array. (OUTPUT) +c H is used to store the generated symmetric tridiagonal matrix +c The subdiagonal is stored in the first column of H starting +c at H(2,1). The main diagonal is stored in the igraphsecond column +c of H starting at H(1,2). If igraphdsaup2 converges store the +c B-norm of the final residual vector in H(1,1). +c +c LDH Integer. (INPUT) +c Leading dimension of H exactly as declared in the calling +c program. +c +c RITZ Double precision array of length NEV+NP. (OUTPUT) +c RITZ(1:NEV) contains the computed Ritz values of OP. +c +c BOUNDS Double precision array of length NEV+NP. (OUTPUT) +c BOUNDS(1:NEV) contain the error bounds corresponding to RITZ. +c +c Q Double precision (NEV+NP) by (NEV+NP) array. (WORKSPACE) +c Private (replicated) work array used to accumulate the +c rotation in the shift application step. +c +c LDQ Integer. (INPUT) +c Leading dimension of Q exactly as declared in the calling +c program. +c +c WORKL Double precision array of length at least 3*(NEV+NP). (INPUT/WORKSPACE) +c Private (replicated) array on each PE or array allocated on +c the front end. It is used in the computation of the +c tridiagonal eigenvalue problem, the calculation and +c application of the shifts and convergence checking. +c If ISHIFT .EQ. O and IDO .EQ. 3, the first NP locations +c of WORKL are used in reverse communication to hold the user +c supplied shifts. +c +c IPNTR Integer array of length 3. (OUTPUT) +c Pointer to mark the starting locations in the WORKD for +c vectors used by the Lanczos iteration. +c ------------------------------------------------------------- +c IPNTR(1): pointer to the current operand vector X. +c IPNTR(2): pointer to the current result vector Y. +c IPNTR(3): pointer to the vector B * X when used in one of +c the spectral transformation modes. X is the current +c operand. +c ------------------------------------------------------------- +c +c WORKD Double precision work array of length 3*N. (REVERSE COMMUNICATION) +c Distributed array to be used in the basic Lanczos iteration +c for reverse communication. The user should not use WORKD +c as temporary workspace during the iteration !!!!!!!!!! +c See Data Distribution Note in igraphdsaupd. +c +c INFO Integer. (INPUT/OUTPUT) +c If INFO .EQ. 0, a randomly initial residual vector is used. +c If INFO .NE. 0, RESID contains the initial residual vector, +c possibly from a previous run. +c Error flag on output. +c = 0: Normal return. +c = 1: All possible eigenvalues of OP has been found. +c NP returns the size of the invariant subspace +c spanning the operator OP. +c = 2: No shifts could be applied. +c = -8: Error return from trid. eigenvalue calculation; +c This should never happen. +c = -9: Starting vector is zero. +c = -9999: Could not build an Lanczos factorization. +c Size that was built in returned in NP. +c +c\EndDoc +c +c----------------------------------------------------------------------- +c +c\BeginLib +c +c\References: +c 1. D.C. Sorensen, "Implicit Application of Polynomial Filters in +c a k-Step Arnoldi Method", SIAM J. Matr. Anal. Apps., 13 (1992), +c pp 357-385. +c 2. R.B. Lehoucq, "Analysis and Implementation of an Implicitly +c Restarted Arnoldi Iteration", Rice University Technical Report +c TR95-13, Department of Computational and Applied Mathematics. +c 3. B.N. Parlett, "The Symmetric Eigenvalue Problem". Prentice-Hall, +c 1980. +c 4. B.N. Parlett, B. Nour-Omid, "Towards a Black Box Lanczos Program", +c Computer Physics Communications, 53 (1989), pp 169-179. +c 5. B. Nour-Omid, B.N. Parlett, T. Ericson, P.S. Jensen, "How to +c Implement the Spectral Transformation", Math. Comp., 48 (1987), +c pp 663-673. +c 6. R.G. Grimes, J.G. Lewis and H.D. Simon, "A Shifted Block Lanczos +c Algorithm for Solving Sparse Symmetric Generalized Eigenproblems", +c SIAM J. Matr. Anal. Apps., January (1993). +c 7. L. Reichel, W.B. Gragg, "Algorithm 686: FORTRAN Subroutines +c for Updating the QR decomposition", ACM TOMS, December 1990, +c Volume 16 Number 4, pp 369-377. +c +c\Routines called: +c igraphdgetv0 ARPACK initial vector generation routine. +c igraphdsaitr ARPACK Lanczos factorization routine. +c igraphdsapps ARPACK application of implicit shifts routine. +c igraphdsconv ARPACK convergence of Ritz values routine. +c igraphdseigt ARPACK compute Ritz values and error bounds routine. +c igraphdsgets ARPACK reorder Ritz values and error bounds routine. +c igraphdsortr ARPACK sorting routine. +c igraphivout ARPACK utility routine that prints integers. +c igraphsecond ARPACK utility routine for timing. +c igraphdvout ARPACK utility routine that prints vectors. +c dlamch LAPACK routine that determines machine constants. +c dcopy Level 1 BLAS that copies one vector to another. +c ddot Level 1 BLAS that computes the scalar product of two vectors. +c dnrm2 Level 1 BLAS that computes the norm of a vector. +c dscal Level 1 BLAS that scales a vector. +c dswap Level 1 BLAS that swaps two vectors. +c +c\Author +c Danny Sorensen Phuong Vu +c Richard Lehoucq CRPC / Rice University +c Dept. of Computational & Houston, Texas +c Applied Mathematics +c Rice University +c Houston, Texas +c +c\Revision history: +c 12/15/93: Version ' 2.4' +c xx/xx/95: Version ' 2.4'. (R.B. Lehoucq) +c +c\SCCS Information: @(#) +c FILE: saup2.F SID: 2.6 DATE OF SID: 8/16/96 RELEASE: 2 +c +c\EndLib +c +c----------------------------------------------------------------------- +c + subroutine igraphdsaup2 + & ( ido, bmat, n, which, nev, np, tol, resid, mode, iupd, + & ishift, mxiter, v, ldv, h, ldh, ritz, bounds, + & q, ldq, workl, ipntr, workd, info ) +c +c %----------------------------------------------------% +c | Include files for debugging and timing information | +c %----------------------------------------------------% +c + include 'debug.h' + include 'stat.h' +c +c %------------------% +c | Scalar Arguments | +c %------------------% +c + character bmat*1, which*2 + integer ido, info, ishift, iupd, ldh, ldq, ldv, mxiter, + & n, mode, nev, np + Double precision + & tol +c +c %-----------------% +c | Array Arguments | +c %-----------------% +c + integer ipntr(3) + Double precision + & bounds(nev+np), h(ldh,2), q(ldq,nev+np), resid(n), + & ritz(nev+np), v(ldv,nev+np), workd(3*n), + & workl(3*(nev+np)) +c +c %------------% +c | Parameters | +c %------------% +c + Double precision + & one, zero + parameter (one = 1.0D+0, zero = 0.0D+0) +c +c %---------------% +c | Local Scalars | +c %---------------% +c + character wprime*2 + logical cnorm, getv0, initv, update, ushift + integer ierr, iter, j, kplusp, msglvl, nconv, nevbef, nev0, + & np0, nptemp, nevd2, nevm2, kp(3) + Double precision + & rnorm, temp, eps23 + save cnorm, getv0, initv, update, ushift, + & iter, kplusp, msglvl, nconv, nev0, np0, + & rnorm, eps23 +c +c %----------------------% +c | External Subroutines | +c %----------------------% +c + external dcopy, igraphdgetv0, igraphdsaitr, dscal, + & igraphdsconv, igraphdseigt, igraphdsgets, + & igraphdsapps, igraphdsortr, igraphdvout, igraphivout, + & igraphsecond, dswap +c +c %--------------------% +c | External Functions | +c %--------------------% +c + Double precision + & ddot, dnrm2, dlamch + external ddot, dnrm2, dlamch +c +c %---------------------% +c | Intrinsic Functions | +c %---------------------% +c + intrinsic min +c +c %-----------------------% +c | Executable Statements | +c %-----------------------% +c + if (ido .eq. 0) then +c +c %-------------------------------% +c | Initialize timing statistics | +c | & message level for debugging | +c %-------------------------------% +c + call igraphsecond (t0) + msglvl = msaup2 +c +c %---------------------------------% +c | Set machine dependent constant. | +c %---------------------------------% +c + eps23 = dlamch('Epsilon-Machine') + eps23 = eps23**(2.0D+0/3.0D+0) +c +c %-------------------------------------% +c | nev0 and np0 are integer variables | +c | hold the initial values of NEV & NP | +c %-------------------------------------% +c + nev0 = nev + np0 = np +c +c %-------------------------------------% +c | kplusp is the bound on the largest | +c | Lanczos factorization built. | +c | nconv is the current number of | +c | "converged" eigenvlues. | +c | iter is the counter on the current | +c | iteration step. | +c %-------------------------------------% +c + kplusp = nev0 + np0 + nconv = 0 + iter = 0 +c +c %--------------------------------------------% +c | Set flags for computing the first NEV steps | +c | of the Lanczos factorization. | +c %--------------------------------------------% +c + getv0 = .true. + update = .false. + ushift = .false. + cnorm = .false. +c + if (info .ne. 0) then +c +c %--------------------------------------------% +c | User provides the initial residual vector. | +c %--------------------------------------------% +c + initv = .true. + info = 0 + else + initv = .false. + end if + end if +c +c %---------------------------------------------% +c | Get a possibly random starting vector and | +c | force it into the range of the operator OP. | +c %---------------------------------------------% +c + 10 continue +c + if (getv0) then + call igraphdgetv0 (ido, bmat, 1, initv, n, 1, v, ldv, resid, + & rnorm, ipntr, workd, info) +c + if (ido .ne. 99) go to 9000 +c + if (rnorm .eq. zero) then +c +c %-----------------------------------------% +c | The initial vector is zero. Error exit. | +c %-----------------------------------------% +c + info = -9 + go to 1200 + end if + getv0 = .false. + ido = 0 + end if +c +c %------------------------------------------------------------% +c | Back from reverse communication: continue with update step | +c %------------------------------------------------------------% +c + if (update) go to 20 +c +c %-------------------------------------------% +c | Back from computing user specified shifts | +c %-------------------------------------------% +c + if (ushift) go to 50 +c +c %-------------------------------------% +c | Back from computing residual norm | +c | at the end of the current iteration | +c %-------------------------------------% +c + if (cnorm) go to 100 +c +c %----------------------------------------------------------% +c | Compute the first NEV steps of the Lanczos factorization | +c %----------------------------------------------------------% +c + call igraphdsaitr (ido, bmat, n, 0, nev0, mode, resid, rnorm, v, + & ldv, h, ldh, ipntr, workd, info) +c +c %---------------------------------------------------% +c | ido .ne. 99 implies use of reverse communication | +c | to compute operations involving OP and possibly B | +c %---------------------------------------------------% +c + if (ido .ne. 99) go to 9000 +c + if (info .gt. 0) then +c +c %-----------------------------------------------------% +c | igraphdsaitr was unable to build an Lanczos factorization | +c | of length NEV0. INFO is returned with the size of | +c | the factorization built. Exit main loop. | +c %-----------------------------------------------------% +c + np = info + mxiter = iter + info = -9999 + go to 1200 + end if +c +c %--------------------------------------------------------------% +c | | +c | M A I N LANCZOS I T E R A T I O N L O O P | +c | Each iteration implicitly restarts the Lanczos | +c | factorization in place. | +c | | +c %--------------------------------------------------------------% +c + 1000 continue +c + iter = iter + 1 +c + if (msglvl .gt. 0) then + call igraphivout (logfil, 1, [iter], ndigit, + & '_saup2: **** Start of major iteration number ****') + end if + if (msglvl .gt. 1) then + call igraphivout (logfil, 1, [nev], ndigit, + & '_saup2: The length of the current Lanczos factorization') + call igraphivout (logfil, 1, [np], ndigit, + & '_saup2: Extend the Lanczos factorization by') + end if +c +c %------------------------------------------------------------% +c | Compute NP additional steps of the Lanczos factorization. | +c %------------------------------------------------------------% +c + ido = 0 + 20 continue + update = .true. +c + call igraphdsaitr (ido, bmat, n, nev, np, mode, resid, rnorm, + & v, ldv, h, ldh, ipntr, workd, info) +c +c %---------------------------------------------------% +c | ido .ne. 99 implies use of reverse communication | +c | to compute operations involving OP and possibly B | +c %---------------------------------------------------% +c + if (ido .ne. 99) go to 9000 +c + if (info .gt. 0) then +c +c %-----------------------------------------------------% +c | igraphdsaitr was unable to build an Lanczos factorization | +c | of length NEV0+NP0. INFO is returned with the size | +c | of the factorization built. Exit main loop. | +c %-----------------------------------------------------% +c + np = info + mxiter = iter + info = -9999 + go to 1200 + end if + update = .false. +c + if (msglvl .gt. 1) then + call igraphdvout (logfil, 1, [rnorm], ndigit, + & '_saup2: Current B-norm of residual for factorization') + end if +c +c %--------------------------------------------------------% +c | Compute the eigenvalues and corresponding error bounds | +c | of the current symmetric tridiagonal matrix. | +c %--------------------------------------------------------% +c + call igraphdseigt (rnorm, kplusp, h, ldh, ritz, bounds, workl, + & ierr) +c + if (ierr .ne. 0) then + info = -8 + go to 1200 + end if +c +c %----------------------------------------------------% +c | Make a copy of eigenvalues and corresponding error | +c | bounds obtained from _seigt. | +c %----------------------------------------------------% +c + call dcopy(kplusp, ritz, 1, workl(kplusp+1), 1) + call dcopy(kplusp, bounds, 1, workl(2*kplusp+1), 1) +c +c %---------------------------------------------------% +c | Select the wanted Ritz values and their bounds | +c | to be used in the convergence test. | +c | The selection is based on the requested number of | +c | eigenvalues instead of the current NEV and NP to | +c | prevent possible misconvergence. | +c | * Wanted Ritz values := RITZ(NP+1:NEV+NP) | +c | * Shifts := RITZ(1:NP) := WORKL(1:NP) | +c %---------------------------------------------------% +c + nev = nev0 + np = np0 + call igraphdsgets (ishift, which, nev, np, ritz, bounds, workl) +c +c %-------------------% +c | Convergence test. | +c %-------------------% +c + call dcopy (nev, bounds(np+1), 1, workl(np+1), 1) + call igraphdsconv (nev, ritz(np+1), workl(np+1), tol, nconv) +c + if (msglvl .gt. 2) then + kp(1) = nev + kp(2) = np + kp(3) = nconv + call igraphivout (logfil, 3, kp, ndigit, + & '_saup2: NEV, NP, NCONV are') + call igraphdvout (logfil, kplusp, ritz, ndigit, + & '_saup2: The eigenvalues of H') + call igraphdvout (logfil, kplusp, bounds, ndigit, + & '_saup2: Ritz estimates of the current NCV Ritz values') + end if +c +c %---------------------------------------------------------% +c | Count the number of unwanted Ritz values that have zero | +c | Ritz estimates. If any Ritz estimates are equal to zero | +c | then a leading block of H of order equal to at least | +c | the number of Ritz values with zero Ritz estimates has | +c | split off. None of these Ritz values may be removed by | +c | shifting. Decrease NP the number of shifts to apply. If | +c | no shifts may be applied, then prepare to exit | +c %---------------------------------------------------------% +c + nptemp = np + do 30 j=1, nptemp + if (bounds(j) .eq. zero) then + np = np - 1 + nev = nev + 1 + end if + 30 continue +c + if ( (nconv .ge. nev0) .or. + & (iter .gt. mxiter) .or. + & (np .eq. 0) ) then +c +c %------------------------------------------------% +c | Prepare to exit. Put the converged Ritz values | +c | and corresponding bounds in RITZ(1:NCONV) and | +c | BOUNDS(1:NCONV) respectively. Then sort. Be | +c | careful when NCONV > NP since we don't want to | +c | swap overlapping locations. | +c %------------------------------------------------% +c + if (which .eq. 'BE') then +c +c %-----------------------------------------------------% +c | Both ends of the spectrum are requested. | +c | Sort the eigenvalues into algebraically decreasing | +c | order first then swap low end of the spectrum next | +c | to high end in appropriate locations. | +c | NOTE: when np < floor(nev/2) be careful not to swap | +c | overlapping locations. | +c %-----------------------------------------------------% +c + wprime = 'SA' + call igraphdsortr (wprime, .true., kplusp, ritz, bounds) + nevd2 = nev / 2 + nevm2 = nev - nevd2 + if ( nev .gt. 1 ) then + call dswap ( min(nevd2,np), ritz(nevm2+1), 1, + & ritz( max(kplusp-nevd2+1,kplusp-np+1) ), 1) + call dswap ( min(nevd2,np), bounds(nevm2+1), 1, + & bounds( max(kplusp-nevd2+1,kplusp-np)+1 ), 1) + end if +c + else +c +c %--------------------------------------------------% +c | LM, SM, LA, SA case. | +c | Sort the eigenvalues of H into the an order that | +c | is opposite to WHICH, and apply the resulting | +c | order to BOUNDS. The eigenvalues are sorted so | +c | that the wanted part are always within the first | +c | NEV locations. | +c %--------------------------------------------------% +c + if (which .eq. 'LM') wprime = 'SM' + if (which .eq. 'SM') wprime = 'LM' + if (which .eq. 'LA') wprime = 'SA' + if (which .eq. 'SA') wprime = 'LA' +c + call igraphdsortr (wprime, .true., kplusp, ritz, bounds) +c + end if +c +c %--------------------------------------------------% +c | Scale the Ritz estimate of each Ritz value | +c | by 1 / max(eps23,magnitude of the Ritz value). | +c %--------------------------------------------------% +c + do 35 j = 1, nev0 + temp = max( eps23, abs(ritz(j)) ) + bounds(j) = bounds(j)/temp + 35 continue +c +c %----------------------------------------------------% +c | Sort the Ritz values according to the scaled Ritz | +c | esitmates. This will push all the converged ones | +c | towards the front of ritzr, ritzi, bounds | +c | (in the case when NCONV < NEV.) | +c %----------------------------------------------------% +c + wprime = 'LA' + call igraphdsortr(wprime, .true., nev0, bounds, ritz) +c +c %----------------------------------------------% +c | Scale the Ritz estimate back to its original | +c | value. | +c %----------------------------------------------% +c + do 40 j = 1, nev0 + temp = max( eps23, abs(ritz(j)) ) + bounds(j) = bounds(j)*temp + 40 continue +c +c %--------------------------------------------------% +c | Sort the "converged" Ritz values again so that | +c | the "threshold" values and their associated Ritz | +c | estimates appear at the appropriate position in | +c | ritz and bound. | +c %--------------------------------------------------% +c + if (which .eq. 'BE') then +c +c %------------------------------------------------% +c | Sort the "converged" Ritz values in increasing | +c | order. The "threshold" values are in the | +c | middle. | +c %------------------------------------------------% +c + wprime = 'LA' + call igraphdsortr(wprime, .true., nconv, ritz, bounds) +c + else +c +c %----------------------------------------------% +c | In LM, SM, LA, SA case, sort the "converged" | +c | Ritz values according to WHICH so that the | +c | "threshold" value appears at the front of | +c | ritz. | +c %----------------------------------------------% + + call igraphdsortr(which, .true., nconv, ritz, bounds) +c + end if +c +c %------------------------------------------% +c | Use h( 1,1 ) as storage to communicate | +c | rnorm to _seupd if needed | +c %------------------------------------------% +c + h(1,1) = rnorm +c + if (msglvl .gt. 1) then + call igraphdvout (logfil, kplusp, ritz, ndigit, + & '_saup2: Sorted Ritz values.') + call igraphdvout (logfil, kplusp, bounds, ndigit, + & '_saup2: Sorted ritz estimates.') + end if +c +c %------------------------------------% +c | Max iterations have been exceeded. | +c %------------------------------------% +c + if (iter .gt. mxiter .and. nconv .lt. nev) info = 1 +c +c %---------------------% +c | No shifts to apply. | +c %---------------------% +c + if (np .eq. 0 .and. nconv .lt. nev0) info = 2 +c + np = nconv + go to 1100 +c + else if (nconv .lt. nev .and. ishift .eq. 1) then +c +c %---------------------------------------------------% +c | Do not have all the requested eigenvalues yet. | +c | To prevent possible stagnation, adjust the number | +c | of Ritz values and the shifts. | +c %---------------------------------------------------% +c + nevbef = nev + nev = nev + min (nconv, np/2) + if (nev .eq. 1 .and. kplusp .ge. 6) then + nev = kplusp / 2 + else if (nev .eq. 1 .and. kplusp .gt. 2) then + nev = 2 + end if + np = kplusp - nev +c +c %---------------------------------------% +c | If the size of NEV was just increased | +c | resort the eigenvalues. | +c %---------------------------------------% +c + if (nevbef .lt. nev) + & call igraphdsgets (ishift, which, nev, np, ritz, bounds, + & workl) +c + end if +c + if (msglvl .gt. 0) then + call igraphivout (logfil, 1, [nconv], ndigit, + & '_saup2: no. of "converged" Ritz values at this iter.') + if (msglvl .gt. 1) then + kp(1) = nev + kp(2) = np + call igraphivout (logfil, 2, kp, ndigit, + & '_saup2: NEV and NP are') + call igraphdvout (logfil, nev, ritz(np+1), ndigit, + & '_saup2: "wanted" Ritz values.') + call igraphdvout (logfil, nev, bounds(np+1), ndigit, + & '_saup2: Ritz estimates of the "wanted" values ') + end if + end if + +c + if (ishift .eq. 0) then +c +c %-----------------------------------------------------% +c | User specified shifts: reverse communication to | +c | compute the shifts. They are returned in the first | +c | NP locations of WORKL. | +c %-----------------------------------------------------% +c + ushift = .true. + ido = 3 + go to 9000 + end if +c + 50 continue +c +c %------------------------------------% +c | Back from reverse communication; | +c | User specified shifts are returned | +c | in WORKL(1:*NP) | +c %------------------------------------% +c + ushift = .false. +c +c +c %---------------------------------------------------------% +c | Move the NP shifts to the first NP locations of RITZ to | +c | free up WORKL. This is for the non-exact shift case; | +c | in the exact shift case, igraphdsgets already handles this. | +c %---------------------------------------------------------% +c + if (ishift .eq. 0) call dcopy (np, workl, 1, ritz, 1) +c + if (msglvl .gt. 2) then + call igraphivout (logfil, 1, [np], ndigit, + & '_saup2: The number of shifts to apply ') + call igraphdvout (logfil, np, workl, ndigit, + & '_saup2: shifts selected') + if (ishift .eq. 1) then + call igraphdvout (logfil, np, bounds, ndigit, + & '_saup2: corresponding Ritz estimates') + end if + end if +c +c %---------------------------------------------------------% +c | Apply the NP0 implicit shifts by QR bulge chasing. | +c | Each shift is applied to the entire tridiagonal matrix. | +c | The first 2*N locations of WORKD are used as workspace. | +c | After igraphdsapps is done, we have a Lanczos | +c | factorization of length NEV. | +c %---------------------------------------------------------% +c + call igraphdsapps (n, nev, np, ritz, v, ldv, h, ldh, resid, + & q, ldq, workd) +c +c %---------------------------------------------% +c | Compute the B-norm of the updated residual. | +c | Keep B*RESID in WORKD(1:N) to be used in | +c | the first step of the next call to igraphdsaitr. | +c %---------------------------------------------% +c + cnorm = .true. + call igraphsecond (t2) + if (bmat .eq. 'G') then + nbx = nbx + 1 + call dcopy (n, resid, 1, workd(n+1), 1) + ipntr(1) = n + 1 + ipntr(2) = 1 + ido = 2 +c +c %----------------------------------% +c | Exit in order to compute B*RESID | +c %----------------------------------% +c + go to 9000 + else if (bmat .eq. 'I') then + call dcopy (n, resid, 1, workd, 1) + end if +c + 100 continue +c +c %----------------------------------% +c | Back from reverse communication; | +c | WORKD(1:N) := B*RESID | +c %----------------------------------% +c + if (bmat .eq. 'G') then + call igraphsecond (t3) + tmvbx = tmvbx + (t3 - t2) + end if +c + if (bmat .eq. 'G') then + rnorm = ddot (n, resid, 1, workd, 1) + rnorm = sqrt(abs(rnorm)) + else if (bmat .eq. 'I') then + rnorm = dnrm2(n, resid, 1) + end if + cnorm = .false. + 130 continue +c + if (msglvl .gt. 2) then + call igraphdvout (logfil, 1, [rnorm], ndigit, + & '_saup2: B-norm of residual for NEV factorization') + call igraphdvout (logfil, nev, h(1,2), ndigit, + & '_saup2: main diagonal of compressed H matrix') + call igraphdvout (logfil, nev-1, h(2,1), ndigit, + & '_saup2: subdiagonal of compressed H matrix') + end if +c + go to 1000 +c +c %---------------------------------------------------------------% +c | | +c | E N D O F M A I N I T E R A T I O N L O O P | +c | | +c %---------------------------------------------------------------% +c + 1100 continue +c + mxiter = iter + nev = nconv +c + 1200 continue + ido = 99 +c +c %------------% +c | Error exit | +c %------------% +c + call igraphsecond (t1) + tsaup2 = t1 - t0 +c + 9000 continue + return +c +c %---------------% +c | End of igraphdsaup2 | +c %---------------% +c + end diff --git a/src/rigraph/vendor/arpack/dsaupd.f b/src/rigraph/vendor/arpack/dsaupd.f new file mode 100644 index 0000000..7e85781 --- /dev/null +++ b/src/rigraph/vendor/arpack/dsaupd.f @@ -0,0 +1,653 @@ +c----------------------------------------------------------------------- +c\BeginDoc +c +c\Name: igraphdsaupd +c +c\Description: +c +c Reverse communication interface for the Implicitly Restarted Arnoldi +c Iteration. For symmetric problems this reduces to a variant of the Lanczos +c method. This method has been designed to compute approximations to a +c few eigenpairs of a linear operator OP that is real and symmetric +c with respect to a real positive semi-definite symmetric matrix B, +c i.e. +c +c B*OP = (OP')*B. +c +c Another way to express this condition is +c +c < x,OPy > = < OPx,y > where < z,w > = z'Bw . +c +c In the standard eigenproblem B is the identity matrix. +c ( A' denotes transpose of A) +c +c The computed approximate eigenvalues are called Ritz values and +c the corresponding approximate eigenvectors are called Ritz vectors. +c +c igraphdsaupd is usually called iteratively to solve one of the +c following problems: +c +c Mode 1: A*x = lambda*x, A symmetric +c ===> OP = A and B = I. +c +c Mode 2: A*x = lambda*M*x, A symmetric, M symmetric positive definite +c ===> OP = inv[M]*A and B = M. +c ===> (If M can be factored see remark 3 below) +c +c Mode 3: K*x = lambda*M*x, K symmetric, M symmetric positive semi-definite +c ===> OP = (inv[K - sigma*M])*M and B = M. +c ===> Shift-and-Invert mode +c +c Mode 4: K*x = lambda*KG*x, K symmetric positive semi-definite, +c KG symmetric indefinite +c ===> OP = (inv[K - sigma*KG])*K and B = K. +c ===> Buckling mode +c +c Mode 5: A*x = lambda*M*x, A symmetric, M symmetric positive semi-definite +c ===> OP = inv[A - sigma*M]*[A + sigma*M] and B = M. +c ===> Cayley transformed mode +c +c NOTE: The action of w <- inv[A - sigma*M]*v or w <- inv[M]*v +c should be accomplished either by a direct method +c using a sparse matrix factorization and solving +c +c [A - sigma*M]*w = v or M*w = v, +c +c or through an iterative method for solving these +c systems. If an iterative method is used, the +c convergence test must be more stringent than +c the accuracy requirements for the eigenvalue +c approximations. +c +c\Usage: +c call igraphdsaupd +c ( IDO, BMAT, N, WHICH, NEV, TOL, RESID, NCV, V, LDV, IPARAM, +c IPNTR, WORKD, WORKL, LWORKL, INFO ) +c +c\Arguments +c IDO Integer. (INPUT/OUTPUT) +c Reverse communication flag. IDO must be zero on the first +c call to igraphdsaupd. IDO will be set internally to +c indicate the type of operation to be performed. Control is +c then given back to the calling routine which has the +c responsibility to carry out the requested operation and call +c igraphdsaupd with the result. The operand is given in +c WORKD(IPNTR(1)), the result must be put in WORKD(IPNTR(2)). +c (If Mode = 2 see remark 5 below) +c ------------------------------------------------------------- +c IDO = 0: first call to the reverse communication interface +c IDO = -1: compute Y = OP * X where +c IPNTR(1) is the pointer into WORKD for X, +c IPNTR(2) is the pointer into WORKD for Y. +c This is for the initialization phase to force the +c starting vector into the range of OP. +c IDO = 1: compute Y = OP * X where +c IPNTR(1) is the pointer into WORKD for X, +c IPNTR(2) is the pointer into WORKD for Y. +c In mode 3,4 and 5, the vector B * X is already +c available in WORKD(ipntr(3)). It does not +c need to be recomputed in forming OP * X. +c IDO = 2: compute Y = B * X where +c IPNTR(1) is the pointer into WORKD for X, +c IPNTR(2) is the pointer into WORKD for Y. +c IDO = 3: compute the IPARAM(8) shifts where +c IPNTR(11) is the pointer into WORKL for +c placing the shifts. See remark 6 below. +c IDO = 99: done +c ------------------------------------------------------------- +c +c BMAT Character*1. (INPUT) +c BMAT specifies the type of the matrix B that defines the +c semi-inner product for the operator OP. +c B = 'I' -> standard eigenvalue problem A*x = lambda*x +c B = 'G' -> generalized eigenvalue problem A*x = lambda*B*x +c +c N Integer. (INPUT) +c Dimension of the eigenproblem. +c +c WHICH Character*2. (INPUT) +c Specify which of the Ritz values of OP to compute. +c +c 'LA' - compute the NEV largest (algebraic) eigenvalues. +c 'SA' - compute the NEV smallest (algebraic) eigenvalues. +c 'LM' - compute the NEV largest (in magnitude) eigenvalues. +c 'SM' - compute the NEV smallest (in magnitude) eigenvalues. +c 'BE' - compute NEV eigenvalues, half from each end of the +c spectrum. When NEV is odd, compute one more from the +c high end than from the low end. +c (see remark 1 below) +c +c NEV Integer. (INPUT) +c Number of eigenvalues of OP to be computed. 0 < NEV < N. +c +c TOL Double precision scalar. (INPUT) +c Stopping criterion: the relative accuracy of the Ritz value +c is considered acceptable if BOUNDS(I) .LE. TOL*ABS(RITZ(I)). +c If TOL .LE. 0. is passed a default is set: +c DEFAULT = DLAMCH('EPS') (machine precision as computed +c by the LAPACK auxiliary subroutine DLAMCH). +c +c RESID Double precision array of length N. (INPUT/OUTPUT) +c On INPUT: +c If INFO .EQ. 0, a random initial residual vector is used. +c If INFO .NE. 0, RESID contains the initial residual vector, +c possibly from a previous run. +c On OUTPUT: +c RESID contains the final residual vector. +c +c NCV Integer. (INPUT) +c Number of columns of the matrix V (less than or equal to N). +c This will indicate how many Lanczos vectors are generated +c at each iteration. After the startup phase in which NEV +c Lanczos vectors are generated, the algorithm generates +c NCV-NEV Lanczos vectors at each subsequent update iteration. +c Most of the cost in generating each Lanczos vector is in the +c matrix-vector product OP*x. (See remark 4 below). +c +c V Double precision N by NCV array. (OUTPUT) +c The NCV columns of V contain the Lanczos basis vectors. +c +c LDV Integer. (INPUT) +c Leading dimension of V exactly as declared in the calling +c program. +c +c IPARAM Integer array of length 11. (INPUT/OUTPUT) +c IPARAM(1) = ISHIFT: method for selecting the implicit shifts. +c The shifts selected at each iteration are used to restart +c the Arnoldi iteration in an implicit fashion. +c ------------------------------------------------------------- +c ISHIFT = 0: the shifts are provided by the user via +c reverse communication. The NCV eigenvalues of +c the current tridiagonal matrix T are returned in +c the part of WORKL array corresponding to RITZ. +c See remark 6 below. +c ISHIFT = 1: exact shifts with respect to the reduced +c tridiagonal matrix T. This is equivalent to +c restarting the iteration with a starting vector +c that is a linear combination of Ritz vectors +c associated with the "wanted" Ritz values. +c ------------------------------------------------------------- +c +c IPARAM(2) = LEVEC +c No longer referenced. See remark 2 below. +c +c IPARAM(3) = MXITER +c On INPUT: maximum number of Arnoldi update iterations allowed. +c On OUTPUT: actual number of Arnoldi update iterations taken. +c +c IPARAM(4) = NB: blocksize to be used in the recurrence. +c The code currently works only for NB = 1. +c +c IPARAM(5) = NCONV: number of "converged" Ritz values. +c This represents the number of Ritz values that satisfy +c the convergence criterion. +c +c IPARAM(6) = IUPD +c No longer referenced. Implicit restarting is ALWAYS used. +c +c IPARAM(7) = MODE +c On INPUT determines what type of eigenproblem is being solved. +c Must be 1,2,3,4,5; See under \Description of igraphdsaupd for the +c five modes available. +c +c IPARAM(8) = NP +c When ido = 3 and the user provides shifts through reverse +c communication (IPARAM(1)=0), igraphdsaupd returns NP, the number +c of shifts the user is to provide. 0 < NP <=NCV-NEV. See Remark +c 6 below. +c +c IPARAM(9) = NUMOP, IPARAM(10) = NUMOPB, IPARAM(11) = NUMREO, +c OUTPUT: NUMOP = total number of OP*x operations, +c NUMOPB = total number of B*x operations if BMAT='G', +c NUMREO = total number of steps of re-orthogonalization. +c +c IPNTR Integer array of length 11. (OUTPUT) +c Pointer to mark the starting locations in the WORKD and WORKL +c arrays for matrices/vectors used by the Lanczos iteration. +c ------------------------------------------------------------- +c IPNTR(1): pointer to the current operand vector X in WORKD. +c IPNTR(2): pointer to the current result vector Y in WORKD. +c IPNTR(3): pointer to the vector B * X in WORKD when used in +c the shift-and-invert mode. +c IPNTR(4): pointer to the next available location in WORKL +c that is untouched by the program. +c IPNTR(5): pointer to the NCV by 2 tridiagonal matrix T in WORKL. +c IPNTR(6): pointer to the NCV RITZ values array in WORKL. +c IPNTR(7): pointer to the Ritz estimates in array WORKL associated +c with the Ritz values located in RITZ in WORKL. +c IPNTR(11): pointer to the NP shifts in WORKL. See Remark 6 below. +c +c Note: IPNTR(8:10) is only referenced by igraphdseupd. See Remark 2. +c IPNTR(8): pointer to the NCV RITZ values of the original system. +c IPNTR(9): pointer to the NCV corresponding error bounds. +c IPNTR(10): pointer to the NCV by NCV matrix of eigenvectors +c of the tridiagonal matrix T. Only referenced by +c igraphdseupd if RVEC = .TRUE. See Remarks. +c ------------------------------------------------------------- +c +c WORKD Double precision work array of length 3*N. (REVERSE COMMUNICATION) +c Distributed array to be used in the basic Arnoldi iteration +c for reverse communication. The user should not use WORKD +c as temporary workspace during the iteration. Upon termination +c WORKD(1:N) contains B*RESID(1:N). If the Ritz vectors are desired +c subroutine igraphdseupd uses this output. +c See Data Distribution Note below. +c +c WORKL Double precision work array of length LWORKL. (OUTPUT/WORKSPACE) +c Private (replicated) array on each PE or array allocated on +c the front end. See Data Distribution Note below. +c +c LWORKL Integer. (INPUT) +c LWORKL must be at least NCV**2 + 8*NCV . +c +c INFO Integer. (INPUT/OUTPUT) +c If INFO .EQ. 0, a randomly initial residual vector is used. +c If INFO .NE. 0, RESID contains the initial residual vector, +c possibly from a previous run. +c Error flag on output. +c = 0: Normal exit. +c = 1: Maximum number of iterations taken. +c All possible eigenvalues of OP has been found. IPARAM(5) +c returns the number of wanted converged Ritz values. +c = 2: No longer an informational error. Deprecated starting +c with release 2 of ARPACK. +c = 3: No shifts could be applied during a cycle of the +c Implicitly restarted Arnoldi iteration. One possibility +c is to increase the size of NCV relative to NEV. +c See remark 4 below. +c = -1: N must be positive. +c = -2: NEV must be positive. +c = -3: NCV must be greater than NEV and less than or equal to N. +c = -4: The maximum number of Arnoldi update iterations allowed +c must be greater than zero. +c = -5: WHICH must be one of 'LM', 'SM', 'LA', 'SA' or 'BE'. +c = -6: BMAT must be one of 'I' or 'G'. +c = -7: Length of private work array WORKL is not sufficient. +c = -8: Error return from trid. eigenvalue calculation; +c Informatinal error from LAPACK routine dsteqr. +c = -9: Starting vector is zero. +c = -10: IPARAM(7) must be 1,2,3,4,5. +c = -11: IPARAM(7) = 1 and BMAT = 'G' are incompatable. +c = -12: IPARAM(1) must be equal to 0 or 1. +c = -13: NEV and WHICH = 'BE' are incompatable. +c = -9999: Could not build an Arnoldi factorization. +c IPARAM(5) returns the size of the current Arnoldi +c factorization. The user is advised to check that +c enough workspace and array storage has been allocated. +c +c +c\Remarks +c 1. The converged Ritz values are always returned in ascending +c algebraic order. The computed Ritz values are approximate +c eigenvalues of OP. The selection of WHICH should be made +c with this in mind when Mode = 3,4,5. After convergence, +c approximate eigenvalues of the original problem may be obtained +c with the ARPACK subroutine igraphdseupd. +c +c 2. If the Ritz vectors corresponding to the converged Ritz values +c are needed, the user must call igraphdseupd immediately following completion +c of igraphdsaupd. This is new starting with version 2.1 of ARPACK. +c +c 3. If M can be factored into a Cholesky factorization M = LL' +c then Mode = 2 should not be selected. Instead one should use +c Mode = 1 with OP = inv(L)*A*inv(L'). Appropriate triangular +c linear systems should be solved with L and L' rather +c than computing inverses. After convergence, an approximate +c eigenvector z of the original problem is recovered by solving +c L'z = x where x is a Ritz vector of OP. +c +c 4. At present there is no a-priori analysis to guide the selection +c of NCV relative to NEV. The only formal requrement is that NCV > NEV. +c However, it is recommended that NCV .ge. 2*NEV. If many problems of +c the same type are to be solved, one should experiment with increasing +c NCV while keeping NEV fixed for a given test problem. This will +c usually decrease the required number of OP*x operations but it +c also increases the work and storage required to maintain the orthogonal +c basis vectors. The optimal "cross-over" with respect to CPU time +c is problem dependent and must be determined empirically. +c +c 5. If IPARAM(7) = 2 then in the Reverse commuication interface the user +c must do the following. When IDO = 1, Y = OP * X is to be computed. +c When IPARAM(7) = 2 OP = inv(B)*A. After computing A*X the user +c must overwrite X with A*X. Y is then the solution to the linear set +c of equations B*Y = A*X. +c +c 6. When IPARAM(1) = 0, and IDO = 3, the user needs to provide the +c NP = IPARAM(8) shifts in locations: +c 1 WORKL(IPNTR(11)) +c 2 WORKL(IPNTR(11)+1) +c . +c . +c . +c NP WORKL(IPNTR(11)+NP-1). +c +c The eigenvalues of the current tridiagonal matrix are located in +c WORKL(IPNTR(6)) through WORKL(IPNTR(6)+NCV-1). They are in the +c order defined by WHICH. The associated Ritz estimates are located in +c WORKL(IPNTR(8)), WORKL(IPNTR(8)+1), ... , WORKL(IPNTR(8)+NCV-1). +c +c----------------------------------------------------------------------- +c +c\Data Distribution Note: +c +c Fortran-D syntax: +c ================ +c REAL RESID(N), V(LDV,NCV), WORKD(3*N), WORKL(LWORKL) +c DECOMPOSE D1(N), D2(N,NCV) +c ALIGN RESID(I) with D1(I) +c ALIGN V(I,J) with D2(I,J) +c ALIGN WORKD(I) with D1(I) range (1:N) +c ALIGN WORKD(I) with D1(I-N) range (N+1:2*N) +c ALIGN WORKD(I) with D1(I-2*N) range (2*N+1:3*N) +c DISTRIBUTE D1(BLOCK), D2(BLOCK,:) +c REPLICATED WORKL(LWORKL) +c +c Cray MPP syntax: +c =============== +c REAL RESID(N), V(LDV,NCV), WORKD(N,3), WORKL(LWORKL) +c SHARED RESID(BLOCK), V(BLOCK,:), WORKD(BLOCK,:) +c REPLICATED WORKL(LWORKL) +c +c +c\BeginLib +c +c\References: +c 1. D.C. Sorensen, "Implicit Application of Polynomial Filters in +c a k-Step Arnoldi Method", SIAM J. Matr. Anal. Apps., 13 (1992), +c pp 357-385. +c 2. R.B. Lehoucq, "Analysis and Implementation of an Implicitly +c Restarted Arnoldi Iteration", Rice University Technical Report +c TR95-13, Department of Computational and Applied Mathematics. +c 3. B.N. Parlett, "The Symmetric Eigenvalue Problem". Prentice-Hall, +c 1980. +c 4. B.N. Parlett, B. Nour-Omid, "Towards a Black Box Lanczos Program", +c Computer Physics Communications, 53 (1989), pp 169-179. +c 5. B. Nour-Omid, B.N. Parlett, T. Ericson, P.S. Jensen, "How to +c Implement the Spectral Transformation", Math. Comp., 48 (1987), +c pp 663-673. +c 6. R.G. Grimes, J.G. Lewis and H.D. Simon, "A Shifted Block Lanczos +c Algorithm for Solving Sparse Symmetric Generalized Eigenproblems", +c SIAM J. Matr. Anal. Apps., January (1993). +c 7. L. Reichel, W.B. Gragg, "Algorithm 686: FORTRAN Subroutines +c for Updating the QR decomposition", ACM TOMS, December 1990, +c Volume 16 Number 4, pp 369-377. +c 8. R.B. Lehoucq, D.C. Sorensen, "Implementation of Some Spectral +c Transformations in a k-Step Arnoldi Method". In Preparation. +c +c\Routines called: +c igraphdsaup2 ARPACK routine that implements the Implicitly Restarted +c Arnoldi Iteration. +c igraphdstats ARPACK routine that initialize timing and other statistics +c variables. +c igraphivout ARPACK utility routine that prints integers. +c igraphsecond ARPACK utility routine for timing. +c igraphdvout ARPACK utility routine that prints vectors. +c dlamch LAPACK routine that determines machine constants. +c +c\Authors +c Danny Sorensen Phuong Vu +c Richard Lehoucq CRPC / Rice University +c Dept. of Computational & Houston, Texas +c Applied Mathematics +c Rice University +c Houston, Texas +c +c\Revision history: +c 12/15/93: Version ' 2.4' +c +c\SCCS Information: @(#) +c FILE: saupd.F SID: 2.7 DATE OF SID: 8/27/96 RELEASE: 2 +c +c\Remarks +c 1. None +c +c\EndLib +c +c----------------------------------------------------------------------- +c + subroutine igraphdsaupd + & ( ido, bmat, n, which, nev, tol, resid, ncv, v, ldv, iparam, + & ipntr, workd, workl, lworkl, info ) +c +c %----------------------------------------------------% +c | Include files for debugging and timing information | +c %----------------------------------------------------% +c + include 'debug.h' + include 'stat.h' +c +c %------------------% +c | Scalar Arguments | +c %------------------% +c + character bmat*1, which*2 + integer ido, info, ldv, lworkl, n, ncv, nev + Double precision + & tol +c +c %-----------------% +c | Array Arguments | +c %-----------------% +c + integer iparam(11), ipntr(11) + Double precision + & resid(n), v(ldv,ncv), workd(3*n), workl(lworkl) +c +c %------------% +c | Parameters | +c %------------% +c + Double precision + & one, zero + parameter (one = 1.0D+0, zero = 0.0D+0) +c +c %---------------% +c | Local Scalars | +c %---------------% +c + integer bounds, ierr, ih, iq, ishift, iupd, iw, + & ldh, ldq, msglvl, mxiter, mode, nb, + & nev0, next, np, ritz, j + save bounds, ierr, ih, iq, ishift, iupd, iw, + & ldh, ldq, msglvl, mxiter, mode, nb, + & nev0, next, np, ritz +c +c %----------------------% +c | External Subroutines | +c %----------------------% +c + external igraphdsaup2, igraphdvout, igraphivout, + & igraphsecond, igraphdstats +c +c %--------------------% +c | External Functions | +c %--------------------% +c + Double precision + & dlamch + external dlamch +c +c %-----------------------% +c | Executable Statements | +c %-----------------------% +c + if (ido .eq. 0) then +c +c %-------------------------------% +c | Initialize timing statistics | +c | & message level for debugging | +c %-------------------------------% +c + call igraphdstats + call igraphsecond (t0) + msglvl = msaupd +c + ierr = 0 + ishift = iparam(1) + mxiter = iparam(3) + nb = iparam(4) +c +c %--------------------------------------------% +c | Revision 2 performs only implicit restart. | +c %--------------------------------------------% +c + iupd = 1 + mode = iparam(7) +c +c %----------------% +c | Error checking | +c %----------------% +c + if (n .le. 0) then + ierr = -1 + else if (nev .le. 0) then + ierr = -2 + else if (ncv .le. nev .or. ncv .gt. n) then + ierr = -3 + end if +c +c %----------------------------------------------% +c | NP is the number of additional steps to | +c | extend the length NEV Lanczos factorization. | +c %----------------------------------------------% +c + np = ncv - nev +c + if (mxiter .le. 0) ierr = -4 + if (which .ne. 'LM' .and. + & which .ne. 'SM' .and. + & which .ne. 'LA' .and. + & which .ne. 'SA' .and. + & which .ne. 'BE') ierr = -5 + if (bmat .ne. 'I' .and. bmat .ne. 'G') ierr = -6 +c + if (lworkl .lt. ncv**2 + 8*ncv) ierr = -7 + if (mode .lt. 1 .or. mode .gt. 5) then + ierr = -10 + else if (mode .eq. 1 .and. bmat .eq. 'G') then + ierr = -11 + else if (ishift .lt. 0 .or. ishift .gt. 1) then + ierr = -12 + else if (nev .eq. 1 .and. which .eq. 'BE') then + ierr = -13 + end if +c +c %------------% +c | Error Exit | +c %------------% +c + if (ierr .ne. 0) then + info = ierr + ido = 99 + go to 9000 + end if +c +c %------------------------% +c | Set default parameters | +c %------------------------% +c + if (nb .le. 0) nb = 1 + if (tol .le. zero) tol = dlamch('EpsMach') +c +c %----------------------------------------------% +c | NP is the number of additional steps to | +c | extend the length NEV Lanczos factorization. | +c | NEV0 is the local variable designating the | +c | size of the invariant subspace desired. | +c %----------------------------------------------% +c + np = ncv - nev + nev0 = nev +c +c %-----------------------------% +c | Zero out internal workspace | +c %-----------------------------% +c + do 10 j = 1, ncv**2 + 8*ncv + workl(j) = zero + 10 continue +c +c %-------------------------------------------------------% +c | Pointer into WORKL for address of H, RITZ, BOUNDS, Q | +c | etc... and the remaining workspace. | +c | Also update pointer to be used on output. | +c | Memory is laid out as follows: | +c | workl(1:2*ncv) := generated tridiagonal matrix | +c | workl(2*ncv+1:2*ncv+ncv) := ritz values | +c | workl(3*ncv+1:3*ncv+ncv) := computed error bounds | +c | workl(4*ncv+1:4*ncv+ncv*ncv) := rotation matrix Q | +c | workl(4*ncv+ncv*ncv+1:7*ncv+ncv*ncv) := workspace | +c %-------------------------------------------------------% +c + ldh = ncv + ldq = ncv + ih = 1 + ritz = ih + 2*ldh + bounds = ritz + ncv + iq = bounds + ncv + iw = iq + ncv**2 + next = iw + 3*ncv +c + ipntr(4) = next + ipntr(5) = ih + ipntr(6) = ritz + ipntr(7) = bounds + ipntr(11) = iw + end if +c +c %-------------------------------------------------------% +c | Carry out the Implicitly restarted Lanczos Iteration. | +c %-------------------------------------------------------% +c + call igraphdsaup2 + & ( ido, bmat, n, which, nev0, np, tol, resid, mode, iupd, + & ishift, mxiter, v, ldv, workl(ih), ldh, workl(ritz), + & workl(bounds), workl(iq), ldq, workl(iw), ipntr, workd, + & info ) +c +c %--------------------------------------------------% +c | ido .ne. 99 implies use of reverse communication | +c | to compute operations involving OP or shifts. | +c %--------------------------------------------------% +c + if (ido .eq. 3) iparam(8) = np + if (ido .ne. 99) go to 9000 +c + iparam(3) = mxiter + iparam(5) = np + iparam(9) = nopx + iparam(10) = nbx + iparam(11) = nrorth +c +c %------------------------------------% +c | Exit if there was an informational | +c | error within igraphdsaup2. | +c %------------------------------------% +c + if (info .lt. 0) go to 9000 + if (info .eq. 2) info = 3 +c + if (msglvl .gt. 0) then + call igraphivout (logfil, 1, mxiter, ndigit, + & '_saupd: number of update iterations taken') + call igraphivout (logfil, 1, np, ndigit, + & '_saupd: number of "converged" Ritz values') + call igraphdvout (logfil, np, workl(Ritz), ndigit, + & '_saupd: final Ritz values') + call igraphdvout (logfil, np, workl(Bounds), ndigit, + & '_saupd: corresponding error bounds') + end if +c + call igraphsecond (t1) + tsaupd = t1 - t0 +c +c + 9000 continue +c + return +c +c %---------------% +c | End of igraphdsaupd | +c %---------------% +c + end diff --git a/src/rigraph/vendor/arpack/dsconv.f b/src/rigraph/vendor/arpack/dsconv.f new file mode 100644 index 0000000..d8bac2e --- /dev/null +++ b/src/rigraph/vendor/arpack/dsconv.f @@ -0,0 +1,138 @@ +c----------------------------------------------------------------------- +c\BeginDoc +c +c\Name: igraphdsconv +c +c\Description: +c Convergence testing for the symmetric Arnoldi eigenvalue routine. +c +c\Usage: +c call igraphdsconv +c ( N, RITZ, BOUNDS, TOL, NCONV ) +c +c\Arguments +c N Integer. (INPUT) +c Number of Ritz values to check for convergence. +c +c RITZ Double precision array of length N. (INPUT) +c The Ritz values to be checked for convergence. +c +c BOUNDS Double precision array of length N. (INPUT) +c Ritz estimates associated with the Ritz values in RITZ. +c +c TOL Double precision scalar. (INPUT) +c Desired relative accuracy for a Ritz value to be considered +c "converged". +c +c NCONV Integer scalar. (OUTPUT) +c Number of "converged" Ritz values. +c +c\EndDoc +c +c----------------------------------------------------------------------- +c +c\BeginLib +c +c\Routines called: +c igraphsecond ARPACK utility routine for timing. +c dlamch LAPACK routine that determines machine constants. +c +c\Author +c Danny Sorensen Phuong Vu +c Richard Lehoucq CRPC / Rice University +c Dept. of Computational & Houston, Texas +c Applied Mathematics +c Rice University +c Houston, Texas +c +c\SCCS Information: @(#) +c FILE: sconv.F SID: 2.4 DATE OF SID: 4/19/96 RELEASE: 2 +c +c\Remarks +c 1. Starting with version 2.4, this routine no longer uses the +c Parlett strategy using the gap conditions. +c +c\EndLib +c +c----------------------------------------------------------------------- +c + subroutine igraphdsconv (n, ritz, bounds, tol, nconv) +c +c %----------------------------------------------------% +c | Include files for debugging and timing information | +c %----------------------------------------------------% +c + include 'debug.h' + include 'stat.h' +c +c %------------------% +c | Scalar Arguments | +c %------------------% +c + integer n, nconv + Double precision + & tol +c +c %-----------------% +c | Array Arguments | +c %-----------------% +c + Double precision + & ritz(n), bounds(n) +c +c %---------------% +c | Local Scalars | +c %---------------% +c + integer i + Double precision + & temp, eps23 +c +c %-------------------% +c | External routines | +c %-------------------% +c + Double precision + & dlamch + external dlamch + +c %---------------------% +c | Intrinsic Functions | +c %---------------------% +c + intrinsic abs +c +c %-----------------------% +c | Executable Statements | +c %-----------------------% +c + call igraphsecond (t0) +c + eps23 = dlamch('Epsilon-Machine') + eps23 = eps23**(2.0D+0 / 3.0D+0) +c + nconv = 0 + do 10 i = 1, n +c +c %-----------------------------------------------------% +c | The i-th Ritz value is considered "converged" | +c | when: bounds(i) .le. TOL*max(eps23, abs(ritz(i))) | +c %-----------------------------------------------------% +c + temp = max( eps23, abs(ritz(i)) ) + if ( bounds(i) .le. tol*temp ) then + nconv = nconv + 1 + end if +c + 10 continue +c + call igraphsecond (t1) + tsconv = tsconv + (t1 - t0) +c + return +c +c %---------------% +c | End of igraphdsconv | +c %---------------% +c + end diff --git a/src/rigraph/vendor/arpack/dseigt.f b/src/rigraph/vendor/arpack/dseigt.f new file mode 100644 index 0000000..dc5dccd --- /dev/null +++ b/src/rigraph/vendor/arpack/dseigt.f @@ -0,0 +1,181 @@ +c----------------------------------------------------------------------- +c\BeginDoc +c +c\Name: igraphdseigt +c +c\Description: +c Compute the eigenvalues of the current symmetric tridiagonal matrix +c and the corresponding error bounds given the current residual norm. +c +c\Usage: +c call igraphdseigt +c ( RNORM, N, H, LDH, EIG, BOUNDS, WORKL, IERR ) +c +c\Arguments +c RNORM Double precision scalar. (INPUT) +c RNORM contains the residual norm corresponding to the current +c symmetric tridiagonal matrix H. +c +c N Integer. (INPUT) +c Size of the symmetric tridiagonal matrix H. +c +c H Double precision N by 2 array. (INPUT) +c H contains the symmetric tridiagonal matrix with the +c subdiagonal in the first column starting at H(2,1) and the +c main diagonal in igraphsecond column. +c +c LDH Integer. (INPUT) +c Leading dimension of H exactly as declared in the calling +c program. +c +c EIG Double precision array of length N. (OUTPUT) +c On output, EIG contains the N eigenvalues of H possibly +c unsorted. The BOUNDS arrays are returned in the +c same sorted order as EIG. +c +c BOUNDS Double precision array of length N. (OUTPUT) +c On output, BOUNDS contains the error estimates corresponding +c to the eigenvalues EIG. This is equal to RNORM times the +c last components of the eigenvectors corresponding to the +c eigenvalues in EIG. +c +c WORKL Double precision work array of length 3*N. (WORKSPACE) +c Private (replicated) array on each PE or array allocated on +c the front end. +c +c IERR Integer. (OUTPUT) +c Error exit flag from igraphdstqrb. +c +c\EndDoc +c +c----------------------------------------------------------------------- +c +c\BeginLib +c +c\Local variables: +c xxxxxx real +c +c\Routines called: +c igraphdstqrb ARPACK routine that computes the eigenvalues and the +c last components of the eigenvectors of a symmetric +c and tridiagonal matrix. +c igraphsecond ARPACK utility routine for timing. +c igraphdvout ARPACK utility routine that prints vectors. +c dcopy Level 1 BLAS that copies one vector to another. +c +c\Author +c Danny Sorensen Phuong Vu +c Richard Lehoucq CRPC / Rice University +c Dept. of Computational & Houston, Texas +c Applied Mathematics +c Rice University +c Houston, Texas +c +c\Revision history: +c xx/xx/92: Version ' 2.4' +c +c\SCCS Information: @(#) +c FILE: seigt.F SID: 2.4 DATE OF SID: 8/27/96 RELEASE: 2 +c +c\Remarks +c None +c +c\EndLib +c +c----------------------------------------------------------------------- +c + subroutine igraphdseigt + & ( rnorm, n, h, ldh, eig, bounds, workl, ierr ) +c +c %----------------------------------------------------% +c | Include files for debugging and timing information | +c %----------------------------------------------------% +c + include 'debug.h' + include 'stat.h' +c +c %------------------% +c | Scalar Arguments | +c %------------------% +c + integer ierr, ldh, n + Double precision + & rnorm +c +c %-----------------% +c | Array Arguments | +c %-----------------% +c + Double precision + & eig(n), bounds(n), h(ldh,2), workl(3*n) +c +c %------------% +c | Parameters | +c %------------% +c + Double precision + & zero + parameter (zero = 0.0D+0) +c +c %---------------% +c | Local Scalars | +c %---------------% +c + integer i, k, msglvl +c +c %----------------------% +c | External Subroutines | +c %----------------------% +c + external dcopy, igraphdstqrb, igraphdvout, igraphsecond +c +c %-----------------------% +c | Executable Statements | +c %-----------------------% +c +c %-------------------------------% +c | Initialize timing statistics | +c | & message level for debugging | +c %-------------------------------% +c + call igraphsecond (t0) + msglvl = mseigt +c + if (msglvl .gt. 0) then + call igraphdvout (logfil, n, h(1,2), ndigit, + & '_seigt: main diagonal of matrix H') + if (n .gt. 1) then + call igraphdvout (logfil, n-1, h(2,1), ndigit, + & '_seigt: sub diagonal of matrix H') + end if + end if +c + call dcopy (n, h(1,2), 1, eig, 1) + call dcopy (n-1, h(2,1), 1, workl, 1) + call igraphdstqrb (n, eig, workl, bounds, workl(n+1), ierr) + if (ierr .ne. 0) go to 9000 + if (msglvl .gt. 1) then + call igraphdvout (logfil, n, bounds, ndigit, + & '_seigt: last row of the eigenvector matrix for H') + end if +c +c %-----------------------------------------------% +c | Finally determine the error bounds associated | +c | with the n Ritz values of H. | +c %-----------------------------------------------% +c + do 30 k = 1, n + bounds(k) = rnorm*abs(bounds(k)) + 30 continue +c + call igraphsecond (t1) + tseigt = tseigt + (t1 - t0) +c + 9000 continue + return +c +c %---------------% +c | End of igraphdseigt | +c %---------------% +c + end diff --git a/src/rigraph/vendor/arpack/dsesrt.f b/src/rigraph/vendor/arpack/dsesrt.f new file mode 100644 index 0000000..05e2c36 --- /dev/null +++ b/src/rigraph/vendor/arpack/dsesrt.f @@ -0,0 +1,217 @@ +c----------------------------------------------------------------------- +c\BeginDoc +c +c\Name: igraphdsesrt +c +c\Description: +c Sort the array X in the order specified by WHICH and optionally +c apply the permutation to the columns of the matrix A. +c +c\Usage: +c call igraphdsesrt +c ( WHICH, APPLY, N, X, NA, A, LDA) +c +c\Arguments +c WHICH Character*2. (Input) +c 'LM' -> X is sorted into increasing order of magnitude. +c 'SM' -> X is sorted into decreasing order of magnitude. +c 'LA' -> X is sorted into increasing order of algebraic. +c 'SA' -> X is sorted into decreasing order of algebraic. +c +c APPLY Logical. (Input) +c APPLY = .TRUE. -> apply the sorted order to A. +c APPLY = .FALSE. -> do not apply the sorted order to A. +c +c N Integer. (INPUT) +c Dimension of the array X. +c +c X Double precision array of length N. (INPUT/OUTPUT) +c The array to be sorted. +c +c NA Integer. (INPUT) +c Number of rows of the matrix A. +c +c A Double precision array of length NA by N. (INPUT/OUTPUT) +c +c LDA Integer. (INPUT) +c Leading dimension of A. +c +c\EndDoc +c +c----------------------------------------------------------------------- +c +c\BeginLib +c +c\Routines +c dswap Level 1 BLAS that swaps the contents of two vectors. +c +c\Authors +c Danny Sorensen Phuong Vu +c Richard Lehoucq CRPC / Rice University +c Dept. of Computational & Houston, Texas +c Applied Mathematics +c Rice University +c Houston, Texas +c +c\Revision history: +c 12/15/93: Version ' 2.1'. +c Adapted from the sort routine in LANSO and +c the ARPACK code igraphdsortr +c +c\SCCS Information: @(#) +c FILE: sesrt.F SID: 2.3 DATE OF SID: 4/19/96 RELEASE: 2 +c +c\EndLib +c +c----------------------------------------------------------------------- +c + subroutine igraphdsesrt (which, apply, n, x, na, a, lda) +c +c %------------------% +c | Scalar Arguments | +c %------------------% +c + character*2 which + logical apply + integer lda, n, na +c +c %-----------------% +c | Array Arguments | +c %-----------------% +c + Double precision + & x(0:n-1), a(lda, 0:n-1) +c +c %---------------% +c | Local Scalars | +c %---------------% +c + integer i, igap, j + Double precision + & temp +c +c %----------------------% +c | External Subroutines | +c %----------------------% +c + external dswap +c +c %-----------------------% +c | Executable Statements | +c %-----------------------% +c + igap = n / 2 +c + if (which .eq. 'SA') then +c +c X is sorted into decreasing order of algebraic. +c + 10 continue + if (igap .eq. 0) go to 9000 + do 30 i = igap, n-1 + j = i-igap + 20 continue +c + if (j.lt.0) go to 30 +c + if (x(j).lt.x(j+igap)) then + temp = x(j) + x(j) = x(j+igap) + x(j+igap) = temp + if (apply) call dswap( na, a(1, j), 1, a(1,j+igap), 1) + else + go to 30 + endif + j = j-igap + go to 20 + 30 continue + igap = igap / 2 + go to 10 +c + else if (which .eq. 'SM') then +c +c X is sorted into decreasing order of magnitude. +c + 40 continue + if (igap .eq. 0) go to 9000 + do 60 i = igap, n-1 + j = i-igap + 50 continue +c + if (j.lt.0) go to 60 +c + if (abs(x(j)).lt.abs(x(j+igap))) then + temp = x(j) + x(j) = x(j+igap) + x(j+igap) = temp + if (apply) call dswap( na, a(1, j), 1, a(1,j+igap), 1) + else + go to 60 + endif + j = j-igap + go to 50 + 60 continue + igap = igap / 2 + go to 40 +c + else if (which .eq. 'LA') then +c +c X is sorted into increasing order of algebraic. +c + 70 continue + if (igap .eq. 0) go to 9000 + do 90 i = igap, n-1 + j = i-igap + 80 continue +c + if (j.lt.0) go to 90 +c + if (x(j).gt.x(j+igap)) then + temp = x(j) + x(j) = x(j+igap) + x(j+igap) = temp + if (apply) call dswap( na, a(1, j), 1, a(1,j+igap), 1) + else + go to 90 + endif + j = j-igap + go to 80 + 90 continue + igap = igap / 2 + go to 70 +c + else if (which .eq. 'LM') then +c +c X is sorted into increasing order of magnitude. +c + 100 continue + if (igap .eq. 0) go to 9000 + do 120 i = igap, n-1 + j = i-igap + 110 continue +c + if (j.lt.0) go to 120 +c + if (abs(x(j)).gt.abs(x(j+igap))) then + temp = x(j) + x(j) = x(j+igap) + x(j+igap) = temp + if (apply) call dswap( na, a(1, j), 1, a(1,j+igap), 1) + else + go to 120 + endif + j = j-igap + go to 110 + 120 continue + igap = igap / 2 + go to 100 + end if +c + 9000 continue + return +c +c %---------------% +c | End of igraphdsesrt | +c %---------------% +c + end diff --git a/src/rigraph/vendor/arpack/dseupd.f b/src/rigraph/vendor/arpack/dseupd.f new file mode 100644 index 0000000..0336c3a --- /dev/null +++ b/src/rigraph/vendor/arpack/dseupd.f @@ -0,0 +1,905 @@ +c\BeginDoc +c +c\Name: igraphdseupd +c +c\Description: +c +c This subroutine returns the converged approximations to eigenvalues +c of A*z = lambda*B*z and (optionally): +c +c (1) the corresponding approximate eigenvectors, +c +c (2) an orthonormal (Lanczos) basis for the associated approximate +c invariant subspace, +c +c (3) Both. +c +c There is negligible additional cost to obtain eigenvectors. An orthonormal +c (Lanczos) basis is always computed. There is an additional storage cost +c of n*nev if both are requested (in this case a separate array Z must be +c supplied). +c +c These quantities are obtained from the Lanczos factorization computed +c by DSAUPD for the linear operator OP prescribed by the MODE selection +c (see IPARAM(7) in DSAUPD documentation.) DSAUPD must be called before +c this routine is called. These approximate eigenvalues and vectors are +c commonly called Ritz values and Ritz vectors respectively. They are +c referred to as such in the comments that follow. The computed orthonormal +c basis for the invariant subspace corresponding to these Ritz values is +c referred to as a Lanczos basis. +c +c See documentation in the header of the subroutine DSAUPD for a definition +c of OP as well as other terms and the relation of computed Ritz values +c and vectors of OP with respect to the given problem A*z = lambda*B*z. +c +c The approximate eigenvalues of the original problem are returned in +c ascending algebraic order. The user may elect to call this routine +c once for each desired Ritz vector and store it peripherally if desired. +c There is also the option of computing a selected set of these vectors +c with a single call. +c +c\Usage: +c call igraphdseupd +c ( RVEC, HOWMNY, SELECT, D, Z, LDZ, SIGMA, BMAT, N, WHICH, NEV, TOL, +c RESID, NCV, V, LDV, IPARAM, IPNTR, WORKD, WORKL, LWORKL, INFO ) +c +c RVEC LOGICAL (INPUT) +c Specifies whether Ritz vectors corresponding to the Ritz value +c approximations to the eigenproblem A*z = lambda*B*z are computed. +c +c RVEC = .FALSE. Compute Ritz values only. +c +c RVEC = .TRUE. Compute Ritz vectors. +c +c HOWMNY Character*1 (INPUT) +c Specifies how many Ritz vectors are wanted and the form of Z +c the matrix of Ritz vectors. See remark 1 below. +c = 'A': compute NEV Ritz vectors; +c = 'S': compute some of the Ritz vectors, specified +c by the logical array SELECT. +c +c SELECT Logical array of dimension NEV. (INPUT) +c If HOWMNY = 'S', SELECT specifies the Ritz vectors to be +c computed. To select the Ritz vector corresponding to a +c Ritz value D(j), SELECT(j) must be set to .TRUE.. +c If HOWMNY = 'A' , SELECT is not referenced. +c +c D Double precision array of dimension NEV. (OUTPUT) +c On exit, D contains the Ritz value approximations to the +c eigenvalues of A*z = lambda*B*z. The values are returned +c in ascending order. If IPARAM(7) = 3,4,5 then D represents +c the Ritz values of OP computed by igraphdsaupd transformed to +c those of the original eigensystem A*z = lambda*B*z. If +c IPARAM(7) = 1,2 then the Ritz values of OP are the same +c as the those of A*z = lambda*B*z. +c +c Z Double precision N by NEV array if HOWMNY = 'A'. (OUTPUT) +c On exit, Z contains the B-orthonormal Ritz vectors of the +c eigensystem A*z = lambda*B*z corresponding to the Ritz +c value approximations. +c If RVEC = .FALSE. then Z is not referenced. +c NOTE: The array Z may be set equal to first NEV columns of the +c Arnoldi/Lanczos basis array V computed by DSAUPD. +c +c LDZ Integer. (INPUT) +c The leading dimension of the array Z. If Ritz vectors are +c desired, then LDZ .ge. max( 1, N ). In any case, LDZ .ge. 1. +c +c SIGMA Double precision (INPUT) +c If IPARAM(7) = 3,4,5 represents the shift. Not referenced if +c IPARAM(7) = 1 or 2. +c +c +c **** The remaining arguments MUST be the same as for the **** +c **** call to DNAUPD that was just completed. **** +c +c NOTE: The remaining arguments +c +c BMAT, N, WHICH, NEV, TOL, RESID, NCV, V, LDV, IPARAM, IPNTR, +c WORKD, WORKL, LWORKL, INFO +c +c must be passed directly to DSEUPD following the last call +c to DSAUPD. These arguments MUST NOT BE MODIFIED between +c the the last call to DSAUPD and the call to DSEUPD. +c +c Two of these parameters (WORKL, INFO) are also output parameters: +c +c WORKL Double precision work array of length LWORKL. (OUTPUT/WORKSPACE) +c WORKL(1:4*ncv) contains information obtained in +c igraphdsaupd. They are not changed by igraphdseupd. +c WORKL(4*ncv+1:ncv*ncv+8*ncv) holds the +c untransformed Ritz values, the computed error estimates, +c and the associated eigenvector matrix of H. +c +c Note: IPNTR(8:10) contains the pointer into WORKL for addresses +c of the above information computed by igraphdseupd. +c ------------------------------------------------------------- +c IPNTR(8): pointer to the NCV RITZ values of the original system. +c IPNTR(9): pointer to the NCV corresponding error bounds. +c IPNTR(10): pointer to the NCV by NCV matrix of eigenvectors +c of the tridiagonal matrix T. Only referenced by +c igraphdseupd if RVEC = .TRUE. See Remarks. +c ------------------------------------------------------------- +c +c INFO Integer. (OUTPUT) +c Error flag on output. +c = 0: Normal exit. +c = -1: N must be positive. +c = -2: NEV must be positive. +c = -3: NCV must be greater than NEV and less than or equal to N. +c = -5: WHICH must be one of 'LM', 'SM', 'LA', 'SA' or 'BE'. +c = -6: BMAT must be one of 'I' or 'G'. +c = -7: Length of private work WORKL array is not sufficient. +c = -8: Error return from trid. eigenvalue calculation; +c Information error from LAPACK routine dsteqr. +c = -9: Starting vector is zero. +c = -10: IPARAM(7) must be 1,2,3,4,5. +c = -11: IPARAM(7) = 1 and BMAT = 'G' are incompatible. +c = -12: NEV and WHICH = 'BE' are incompatible. +c = -14: DSAUPD did not find any eigenvalues to sufficient +c accuracy. +c = -15: HOWMNY must be one of 'A' or 'S' if RVEC = .true. +c = -16: HOWMNY = 'S' not yet implemented +c +c\BeginLib +c +c\References: +c 1. D.C. Sorensen, "Implicit Application of Polynomial Filters in +c a k-Step Arnoldi Method", SIAM J. Matr. Anal. Apps., 13 (1992), +c pp 357-385. +c 2. R.B. Lehoucq, "Analysis and Implementation of an Implicitly +c Restarted Arnoldi Iteration", Rice University Technical Report +c TR95-13, Department of Computational and Applied Mathematics. +c 3. B.N. Parlett, "The Symmetric Eigenvalue Problem". Prentice-Hall, +c 1980. +c 4. B.N. Parlett, B. Nour-Omid, "Towards a Black Box Lanczos Program", +c Computer Physics Communications, 53 (1989), pp 169-179. +c 5. B. Nour-Omid, B.N. Parlett, T. Ericson, P.S. Jensen, "How to +c Implement the Spectral Transformation", Math. Comp., 48 (1987), +c pp 663-673. +c 6. R.G. Grimes, J.G. Lewis and H.D. Simon, "A Shifted Block Lanczos +c Algorithm for Solving Sparse Symmetric Generalized Eigenproblems", +c SIAM J. Matr. Anal. Apps., January (1993). +c 7. L. Reichel, W.B. Gragg, "Algorithm 686: FORTRAN Subroutines +c for Updating the QR decomposition", ACM TOMS, December 1990, +c Volume 16 Number 4, pp 369-377. +c +c\Remarks +c 1. The converged Ritz values are always returned in increasing +c (algebraic) order. +c +c 2. Currently only HOWMNY = 'A' is implemented. It is included at this +c stage for the user who wants to incorporate it. +c +c\Routines called: +c igraphdsesrt ARPACK routine that sorts an array X, and applies the +c corresponding permutation to a matrix A. +c igraphdsortr igraphdsortr ARPACK sorting routine. +c igraphivout ARPACK utility routine that prints integers. +c igraphdvout ARPACK utility routine that prints vectors. +c dgeqr2 LAPACK routine that computes the QR factorization of +c a matrix. +c dlacpy LAPACK matrix copy routine. +c dlamch LAPACK routine that determines machine constants. +c dorm2r LAPACK routine that applies an orthogonal matrix in +c factored form. +c dsteqr LAPACK routine that computes eigenvalues and eigenvectors +c of a tridiagonal matrix. +c dger Level 2 BLAS rank one update to a matrix. +c dcopy Level 1 BLAS that copies one vector to another . +c dnrm2 Level 1 BLAS that computes the norm of a vector. +c dscal Level 1 BLAS that scales a vector. +c dswap Level 1 BLAS that swaps the contents of two vectors. + +c\Authors +c Danny Sorensen Phuong Vu +c Richard Lehoucq CRPC / Rice University +c Chao Yang Houston, Texas +c Dept. of Computational & +c Applied Mathematics +c Rice University +c Houston, Texas +c +c\Revision history: +c 12/15/93: Version ' 2.1' +c +c\SCCS Information: @(#) +c FILE: seupd.F SID: 2.7 DATE OF SID: 8/27/96 RELEASE: 2 +c +c\EndLib +c +c----------------------------------------------------------------------- + subroutine igraphdseupd (rvec, howmny, select, d, z, ldz, + & sigma, bmat, n, which, nev, tol, resid, ncv, v, ldv, iparam, + & ipntr, workd, workl, lworkl, info ) +c +c %----------------------------------------------------% +c | Include files for debugging and timing information | +c %----------------------------------------------------% +c + include 'debug.h' + include 'stat.h' +c +c %------------------% +c | Scalar Arguments | +c %------------------% +c + character bmat, howmny, which*2 + logical rvec, select(ncv) + integer info, ldz, ldv, lworkl, n, ncv, nev + Double precision + & sigma, tol +c +c %-----------------% +c | Array Arguments | +c %-----------------% +c + integer iparam(7), ipntr(11) + Double precision + & d(nev), resid(n), v(ldv,ncv), z(ldz, nev), + & workd(2*n), workl(lworkl) +c +c %------------% +c | Parameters | +c %------------% +c + Double precision + & one, zero + parameter (one = 1.0D+0, zero = 0.0D+0) +c +c %---------------% +c | Local Scalars | +c %---------------% +c + character type*6 + integer bounds, ierr, ih, ihb, ihd, iq, iw, j, k, + & ldh, ldq, mode, msglvl, nconv, next, ritz, + & irz, ibd, ktrord, leftptr, rghtptr, ism, ilg + Double precision + & bnorm2, rnorm, temp, thres1, thres2, tempbnd, eps23 + logical reord +c +c %--------------% +c | Local Arrays | +c %--------------% +c + Double precision + & kv(2) +c +c %----------------------% +c | External Subroutines | +c %----------------------% +c + external dcopy, dger, dgeqr2, dlacpy, dorm2r, dscal, + & igraphdsesrt, dsteqr, dswap, igraphdvout, + & igraphivout, igraphdsortr +c +c %--------------------% +c | External Functions | +c %--------------------% +c + Double precision + & dnrm2, dlamch + external dnrm2, dlamch +c +c %---------------------% +c | Intrinsic Functions | +c %---------------------% +c + intrinsic min +c +c %-----------------------% +c | Executable Statements | +c %-----------------------% +c +c %------------------------% +c | Set default parameters | +c %------------------------% +c + msglvl = mseupd + mode = iparam(7) + nconv = iparam(5) + info = 0 +c +c %--------------% +c | Quick return | +c %--------------% +c + if (nconv .eq. 0) go to 9000 + ierr = 0 +c + if (nconv .le. 0) ierr = -14 + if (n .le. 0) ierr = -1 + if (nev .le. 0) ierr = -2 + if (ncv .le. nev .or. ncv .gt. n) ierr = -3 + if (which .ne. 'LM' .and. + & which .ne. 'SM' .and. + & which .ne. 'LA' .and. + & which .ne. 'SA' .and. + & which .ne. 'BE') ierr = -5 + if (bmat .ne. 'I' .and. bmat .ne. 'G') ierr = -6 + if ( (howmny .ne. 'A' .and. + & howmny .ne. 'P' .and. + & howmny .ne. 'S') .and. rvec ) + & ierr = -15 + if (rvec .and. howmny .eq. 'S') ierr = -16 +c + if (rvec .and. lworkl .lt. ncv**2+8*ncv) ierr = -7 +c + if (mode .eq. 1 .or. mode .eq. 2) then + type = 'REGULR' + else if (mode .eq. 3 ) then + type = 'SHIFTI' + else if (mode .eq. 4 ) then + type = 'BUCKLE' + else if (mode .eq. 5 ) then + type = 'CAYLEY' + else + ierr = -10 + end if + if (mode .eq. 1 .and. bmat .eq. 'G') ierr = -11 + if (nev .eq. 1 .and. which .eq. 'BE') ierr = -12 +c +c %------------% +c | Error Exit | +c %------------% +c + if (ierr .ne. 0) then + info = ierr + go to 9000 + end if +c +c %-------------------------------------------------------% +c | Pointer into WORKL for address of H, RITZ, BOUNDS, Q | +c | etc... and the remaining workspace. | +c | Also update pointer to be used on output. | +c | Memory is laid out as follows: | +c | workl(1:2*ncv) := generated tridiagonal matrix H | +c | The subdiagonal is stored in workl(2:ncv). | +c | The dead spot is workl(1) but upon exiting | +c | igraphdsaupd stores the B-norm of the last residual | +c | vector in workl(1). We use this !!! | +c | workl(2*ncv+1:2*ncv+ncv) := ritz values | +c | The wanted values are in the first NCONV spots. | +c | workl(3*ncv+1:3*ncv+ncv) := computed Ritz estimates | +c | The wanted values are in the first NCONV spots. | +c | NOTE: workl(1:4*ncv) is set by igraphdsaupd and is not | +c | modified by igraphdseupd. | +c %-------------------------------------------------------% +c +c %-------------------------------------------------------% +c | The following is used and set by igraphdseupd. | +c | workl(4*ncv+1:4*ncv+ncv) := used as workspace during | +c | computation of the eigenvectors of H. Stores | +c | the diagonal of H. Upon EXIT contains the NCV | +c | Ritz values of the original system. The first | +c | NCONV spots have the wanted values. If MODE = | +c | 1 or 2 then will equal workl(2*ncv+1:3*ncv). | +c | workl(5*ncv+1:5*ncv+ncv) := used as workspace during | +c | computation of the eigenvectors of H. Stores | +c | the subdiagonal of H. Upon EXIT contains the | +c | NCV corresponding Ritz estimates of the | +c | original system. The first NCONV spots have the | +c | wanted values. If MODE = 1,2 then will equal | +c | workl(3*ncv+1:4*ncv). | +c | workl(6*ncv+1:6*ncv+ncv*ncv) := orthogonal Q that is | +c | the eigenvector matrix for H as returned by | +c | dsteqr. Not referenced if RVEC = .False. | +c | Ordering follows that of workl(4*ncv+1:5*ncv) | +c | workl(6*ncv+ncv*ncv+1:6*ncv+ncv*ncv+2*ncv) := | +c | Workspace. Needed by dsteqr and by igraphdseupd. | +c | GRAND total of NCV*(NCV+8) locations. | +c %-------------------------------------------------------% +c +c + 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 + 2*ncv + ipntr(4) = next + ipntr(8) = ihd + ipntr(9) = ihb + ipntr(10) = iq +c +c %----------------------------------------% +c | irz points to the Ritz values computed | +c | by _seigt before exiting _saup2. | +c | ibd points to the Ritz estimates | +c | computed by _seigt before exiting | +c | _saup2. | +c %----------------------------------------% +c + irz = ipntr(11)+ncv + ibd = irz+ncv +c +c +c %---------------------------------% +c | Set machine dependent constant. | +c %---------------------------------% +c + eps23 = dlamch('Epsilon-Machine') + eps23 = eps23**(2.0D+0 / 3.0D+0) +c +c %---------------------------------------% +c | RNORM is B-norm of the RESID(1:N). | +c | BNORM2 is the 2 norm of B*RESID(1:N). | +c | Upon exit of igraphdsaupd WORKD(1:N) has | +c | B*RESID(1:N). | +c %---------------------------------------% +c + rnorm = workl(ih) + if (bmat .eq. 'I') then + bnorm2 = rnorm + else if (bmat .eq. 'G') then + bnorm2 = dnrm2(n, workd, 1) + end if +c + if (rvec) then +c +c %------------------------------------------------% +c | Get the converged Ritz value on the boundary. | +c | This value will be used to dermine whether we | +c | need to reorder the eigenvalues and | +c | eigenvectors comupted by _steqr, and is | +c | referred to as the "threshold" value. | +c | | +c | A Ritz value gamma is said to be a wanted | +c | one, if | +c | abs(gamma) .ge. threshold, when WHICH = 'LM'; | +c | abs(gamma) .le. threshold, when WHICH = 'SM'; | +c | gamma .ge. threshold, when WHICH = 'LA'; | +c | gamma .le. threshold, when WHICH = 'SA'; | +c | gamma .le. thres1 .or. gamma .ge. thres2 | +c | when WHICH = 'BE'; | +c | | +c | Note: converged Ritz values and associated | +c | Ritz estimates have been placed in the first | +c | NCONV locations in workl(ritz) and | +c | workl(bounds) respectively. They have been | +c | sorted (in _saup2) according to the WHICH | +c | selection criterion. (Except in the case | +c | WHICH = 'BE', they are sorted in an increasing | +c | order.) | +c %------------------------------------------------% +c + if (which .eq. 'LM' .or. which .eq. 'SM' + & .or. which .eq. 'LA' .or. which .eq. 'SA' ) then +c + thres1 = workl(ritz) +c + if (msglvl .gt. 2) then + call igraphdvout(logfil, 1, [thres1], ndigit, + & '_seupd: Threshold eigenvalue used for re-ordering') + end if +c + else if (which .eq. 'BE') then +c +c %------------------------------------------------% +c | Ritz values returned from _saup2 have been | +c | sorted in increasing order. Thus two | +c | "threshold" values (one for the small end, one | +c | for the large end) are in the middle. | +c %------------------------------------------------% +c + ism = max(nev,nconv) / 2 + ilg = ism + 1 + thres1 = workl(ism) + thres2 = workl(ilg) +c + if (msglvl .gt. 2) then + kv(1) = thres1 + kv(2) = thres2 + call igraphdvout(logfil, 2, kv, ndigit, + & '_seupd: Threshold eigenvalues used for re-ordering') + end if +c + end if +c +c %----------------------------------------------------------% +c | Check to see if all converged Ritz values appear within | +c | the first NCONV diagonal elements returned from _seigt. | +c | This is done in the following way: | +c | | +c | 1) For each Ritz value obtained from _seigt, compare it | +c | with the threshold Ritz value computed above to | +c | determine whether it is a wanted one. | +c | | +c | 2) If it is wanted, then check the corresponding Ritz | +c | estimate to see if it has converged. If it has, set | +c | correponding entry in the logical array SELECT to | +c | .TRUE.. | +c | | +c | If SELECT(j) = .TRUE. and j > NCONV, then there is a | +c | converged Ritz value that does not appear at the top of | +c | the diagonal matrix computed by _seigt in _saup2. | +c | Reordering is needed. | +c %----------------------------------------------------------% +c + reord = .false. + ktrord = 0 + do 10 j = 0, ncv-1 + select(j+1) = .false. + if (which .eq. 'LM') then + if (abs(workl(irz+j)) .ge. abs(thres1)) then + tempbnd = max( eps23, abs(workl(irz+j)) ) + if (workl(ibd+j) .le. tol*tempbnd) then + select(j+1) = .true. + end if + end if + else if (which .eq. 'SM') then + if (abs(workl(irz+j)) .le. abs(thres1)) then + tempbnd = max( eps23, abs(workl(irz+j)) ) + if (workl(ibd+j) .le. tol*tempbnd) then + select(j+1) = .true. + end if + end if + else if (which .eq. 'LA') then + if (workl(irz+j) .ge. thres1) then + tempbnd = max( eps23, abs(workl(irz+j)) ) + if (workl(ibd+j) .le. tol*tempbnd) then + select(j+1) = .true. + end if + end if + else if (which .eq. 'SA') then + if (workl(irz+j) .le. thres1) then + tempbnd = max( eps23, abs(workl(irz+j)) ) + if (workl(ibd+j) .le. tol*tempbnd) then + select(j+1) = .true. + end if + end if + else if (which .eq. 'BE') then + if ( workl(irz+j) .le. thres1 .or. + & workl(irz+j) .ge. thres2 ) then + tempbnd = max( eps23, abs(workl(irz+j)) ) + if (workl(ibd+j) .le. tol*tempbnd) then + select(j+1) = .true. + end if + end if + end if + if (j+1 .gt. nconv ) reord = select(j+1) .or. reord + if (select(j+1)) ktrord = ktrord + 1 + 10 continue + +c %-------------------------------------------% +c | If KTRORD .ne. NCONV, something is wrong. | +c %-------------------------------------------% +c + if (msglvl .gt. 2) then + call igraphivout(logfil, 1, [ktrord], ndigit, + & '_seupd: Number of specified eigenvalues') + call igraphivout(logfil, 1, [nconv], ndigit, + & '_seupd: Number of "converged" eigenvalues') + end if +c +c %-----------------------------------------------------------% +c | Call LAPACK routine _steqr to compute the eigenvalues and | +c | eigenvectors of the final symmetric tridiagonal matrix H. | +c | Initialize the eigenvector matrix Q to the identity. | +c %-----------------------------------------------------------% +c + call dcopy (ncv-1, workl(ih+1), 1, workl(ihb), 1) + call dcopy (ncv, workl(ih+ldh), 1, workl(ihd), 1) +c + call dsteqr ('Identity', ncv, workl(ihd), workl(ihb), + & workl(iq), ldq, workl(iw), ierr) +c + if (ierr .ne. 0) then + info = -8 + go to 9000 + end if +c + if (msglvl .gt. 1) then + call dcopy (ncv, workl(iq+ncv-1), ldq, workl(iw), 1) + call igraphdvout (logfil, ncv, workl(ihd), ndigit, + & '_seupd: NCV Ritz values of the final H matrix') + call igraphdvout (logfil, ncv, workl(iw), ndigit, + & '_seupd: last row of the eigenvector matrix for H') + end if +c + if (reord) then +c +c %---------------------------------------------% +c | Reordered the eigenvalues and eigenvectors | +c | computed by _steqr so that the "converged" | +c | eigenvalues appear in the first NCONV | +c | positions of workl(ihd), and the associated | +c | eigenvectors appear in the first NCONV | +c | columns. | +c %---------------------------------------------% +c + leftptr = 1 + rghtptr = ncv +c + if (ncv .eq. 1) go to 30 +c + 20 if (select(leftptr)) then +c +c %-------------------------------------------% +c | Search, from the left, for the first Ritz | +c | value that has not converged. | +c %-------------------------------------------% +c + leftptr = leftptr + 1 +c + else if ( .not. select(rghtptr)) then +c +c %----------------------------------------------% +c | Search, from the right, the first Ritz value | +c | that has converged. | +c %----------------------------------------------% +c + rghtptr = rghtptr - 1 +c + else +c +c %----------------------------------------------% +c | Swap the Ritz value on the left that has not | +c | converged with the Ritz value on the right | +c | that has converged. Swap the associated | +c | eigenvector of the tridiagonal matrix H as | +c | well. | +c %----------------------------------------------% +c + temp = workl(ihd+leftptr-1) + workl(ihd+leftptr-1) = workl(ihd+rghtptr-1) + workl(ihd+rghtptr-1) = temp + call dcopy(ncv, workl(iq+ncv*(leftptr-1)), 1, + & workl(iw), 1) + call dcopy(ncv, workl(iq+ncv*(rghtptr-1)), 1, + & workl(iq+ncv*(leftptr-1)), 1) + call dcopy(ncv, workl(iw), 1, + & workl(iq+ncv*(rghtptr-1)), 1) + leftptr = leftptr + 1 + rghtptr = rghtptr - 1 +c + end if +c + if (leftptr .lt. rghtptr) go to 20 +c + 30 end if +c + if (msglvl .gt. 2) then + call igraphdvout (logfil, ncv, workl(ihd), ndigit, + & '_seupd: The eigenvalues of H--reordered') + end if +c +c %----------------------------------------% +c | Load the converged Ritz values into D. | +c %----------------------------------------% +c + call dcopy(nconv, workl(ihd), 1, d, 1) +c + else +c +c %-----------------------------------------------------% +c | Ritz vectors not required. Load Ritz values into D. | +c %-----------------------------------------------------% +c + call dcopy (nconv, workl(ritz), 1, d, 1) + call dcopy (ncv, workl(ritz), 1, workl(ihd), 1) +c + end if +c +c %------------------------------------------------------------------% +c | Transform the Ritz values and possibly vectors and corresponding | +c | Ritz estimates of OP to those of A*x=lambda*B*x. The Ritz values | +c | (and corresponding data) are returned in ascending order. | +c %------------------------------------------------------------------% +c + if (type .eq. 'REGULR') then +c +c %---------------------------------------------------------% +c | Ascending sort of wanted Ritz values, vectors and error | +c | bounds. Not necessary if only Ritz values are desired. | +c %---------------------------------------------------------% +c + if (rvec) then + call igraphdsesrt ('LA', rvec , nconv, d, ncv, workl(iq), + & ldq) + else + call dcopy (ncv, workl(bounds), 1, workl(ihb), 1) + end if +c + else +c +c %-------------------------------------------------------------% +c | * Make a copy of all the Ritz values. | +c | * Transform the Ritz values back to the original system. | +c | For TYPE = 'SHIFTI' the transformation is | +c | lambda = 1/theta + sigma | +c | For TYPE = 'BUCKLE' the transformation is | +c | lambda = sigma * theta / ( theta - 1 ) | +c | For TYPE = 'CAYLEY' the transformation is | +c | lambda = sigma * (theta + 1) / (theta - 1 ) | +c | where the theta are the Ritz values returned by igraphdsaupd. | +c | NOTES: | +c | *The Ritz vectors are not affected by the transformation. | +c | They are only reordered. | +c %-------------------------------------------------------------% +c + call dcopy (ncv, workl(ihd), 1, workl(iw), 1) + if (type .eq. 'SHIFTI') then + do 40 k=1, ncv + workl(ihd+k-1) = one / workl(ihd+k-1) + sigma + 40 continue + else if (type .eq. 'BUCKLE') then + do 50 k=1, ncv + workl(ihd+k-1) = sigma * workl(ihd+k-1) / + & (workl(ihd+k-1) - one) + 50 continue + else if (type .eq. 'CAYLEY') then + do 60 k=1, ncv + workl(ihd+k-1) = sigma * (workl(ihd+k-1) + one) / + & (workl(ihd+k-1) - one) + 60 continue + end if +c +c %-------------------------------------------------------------% +c | * Store the wanted NCONV lambda values into D. | +c | * Sort the NCONV wanted lambda in WORKL(IHD:IHD+NCONV-1) | +c | into ascending order and apply sort to the NCONV theta | +c | values in the transformed system. We'll need this to | +c | compute Ritz estimates in the original system. | +c | * Finally sort the lambda's into ascending order and apply | +c | to Ritz vectors if wanted. Else just sort lambda's into | +c | ascending order. | +c | NOTES: | +c | *workl(iw:iw+ncv-1) contain the theta ordered so that they | +c | match the ordering of the lambda. We'll use them again for | +c | Ritz vector purification. | +c %-------------------------------------------------------------% +c + call dcopy (nconv, workl(ihd), 1, d, 1) + call igraphdsortr ('LA', .true., nconv, workl(ihd), workl(iw)) + if (rvec) then + call igraphdsesrt ('LA', rvec , nconv, d, ncv, workl(iq), + & ldq) + else + call dcopy (ncv, workl(bounds), 1, workl(ihb), 1) + call dscal (ncv, bnorm2/rnorm, workl(ihb), 1) + call igraphdsortr ('LA', .true., nconv, d, workl(ihb)) + end if +c + end if +c +c %------------------------------------------------% +c | Compute the Ritz vectors. Transform the wanted | +c | eigenvectors of the symmetric tridiagonal H by | +c | the Lanczos basis matrix V. | +c %------------------------------------------------% +c + if (rvec .and. howmny .eq. 'A') then +c +c %----------------------------------------------------------% +c | Compute the QR factorization of the matrix representing | +c | the wanted invariant subspace located in the first NCONV | +c | columns of workl(iq,ldq). | +c %----------------------------------------------------------% +c + call dgeqr2 (ncv, nconv, workl(iq), ldq, workl(iw+ncv), + & workl(ihb), ierr) +c +c +c %--------------------------------------------------------% +c | * Postmultiply V by Q. | +c | * Copy the first NCONV columns of VQ into Z. | +c | The N by NCONV matrix Z is now a matrix representation | +c | of the approximate invariant subspace associated with | +c | the Ritz values in workl(ihd). | +c %--------------------------------------------------------% +c + call dorm2r ('Right', 'Notranspose', n, ncv, nconv, workl(iq), + & ldq, workl(iw+ncv), v, ldv, workd(n+1), ierr) + call dlacpy ('All', n, nconv, v, ldv, z, ldz) +c +c %-----------------------------------------------------% +c | In order to compute the Ritz estimates for the Ritz | +c | values in both systems, need the last row of the | +c | eigenvector matrix. Remember, it's in factored form | +c %-----------------------------------------------------% +c + do 65 j = 1, ncv-1 + workl(ihb+j-1) = zero + 65 continue + workl(ihb+ncv-1) = one + call dorm2r ('Left', 'Transpose', ncv, 1, nconv, workl(iq), + & ldq, workl(iw+ncv), workl(ihb), ncv, temp, ierr) +c + else if (rvec .and. howmny .eq. 'S') then +c +c Not yet implemented. See remark 2 above. +c + end if +c + if (type .eq. 'REGULR' .and. rvec) then +c + do 70 j=1, ncv + workl(ihb+j-1) = rnorm * abs( workl(ihb+j-1) ) + 70 continue +c + else if (type .ne. 'REGULR' .and. rvec) then +c +c %-------------------------------------------------% +c | * Determine Ritz estimates of the theta. | +c | If RVEC = .true. then compute Ritz estimates | +c | of the theta. | +c | If RVEC = .false. then copy Ritz estimates | +c | as computed by igraphdsaupd. | +c | * Determine Ritz estimates of the lambda. | +c %-------------------------------------------------% +c + call dscal (ncv, bnorm2, workl(ihb), 1) + if (type .eq. 'SHIFTI') then +c + do 80 k=1, ncv + workl(ihb+k-1) = abs( workl(ihb+k-1) ) / workl(iw+k-1)**2 + 80 continue +c + else if (type .eq. 'BUCKLE') then +c + do 90 k=1, ncv + workl(ihb+k-1) = sigma * abs( workl(ihb+k-1) ) / + & ( workl(iw+k-1)-one )**2 + 90 continue +c + else if (type .eq. 'CAYLEY') then +c + do 100 k=1, ncv + workl(ihb+k-1) = abs( workl(ihb+k-1) / + & workl(iw+k-1)*(workl(iw+k-1)-one) ) + 100 continue +c + end if +c + end if +c + if (type .ne. 'REGULR' .and. msglvl .gt. 1) then + call igraphdvout (logfil, nconv, d, ndigit, + & '_seupd: Untransformed converged Ritz values') + call igraphdvout (logfil, nconv, workl(ihb), ndigit, + & '_seupd: Ritz estimates of the untransformed Ritz values') + else if (msglvl .gt. 1) then + call igraphdvout (logfil, nconv, d, ndigit, + & '_seupd: Converged Ritz values') + call igraphdvout (logfil, nconv, workl(ihb), ndigit, + & '_seupd: Associated Ritz estimates') + end if +c +c %-------------------------------------------------% +c | Ritz vector purification step. Formally perform | +c | one of inverse subspace iteration. Only used | +c | for MODE = 3,4,5. See reference 7 | +c %-------------------------------------------------% +c + if (rvec .and. (type .eq. 'SHIFTI' .or. type .eq. 'CAYLEY')) then +c + do 110 k=0, nconv-1 + workl(iw+k) = workl(iq+k*ldq+ncv-1) / workl(iw+k) + 110 continue +c + else if (rvec .and. type .eq. 'BUCKLE') then +c + do 120 k=0, nconv-1 + workl(iw+k) = workl(iq+k*ldq+ncv-1) / (workl(iw+k)-one) + 120 continue +c + end if +c + if (type .ne. 'REGULR') + & call dger (n, nconv, one, resid, 1, workl(iw), 1, z, ldz) +c + 9000 continue +c + return +c +c %---------------% +c | End of igraphdseupd | +c %---------------% +c + end diff --git a/src/rigraph/vendor/arpack/dsgets.f b/src/rigraph/vendor/arpack/dsgets.f new file mode 100644 index 0000000..2b0794f --- /dev/null +++ b/src/rigraph/vendor/arpack/dsgets.f @@ -0,0 +1,220 @@ +c----------------------------------------------------------------------- +c\BeginDoc +c +c\Name: igraphdsgets +c +c\Description: +c Given the eigenvalues of the symmetric tridiagonal matrix H, +c computes the NP shifts AMU that are zeros of the polynomial of +c degree NP which filters out components of the unwanted eigenvectors +c corresponding to the AMU's based on some given criteria. +c +c NOTE: This is called even in the case of user specified shifts in +c order to sort the eigenvalues, and error bounds of H for later use. +c +c\Usage: +c call igraphdsgets +c ( ISHIFT, WHICH, KEV, NP, RITZ, BOUNDS, SHIFTS ) +c +c\Arguments +c ISHIFT Integer. (INPUT) +c Method for selecting the implicit shifts at each iteration. +c ISHIFT = 0: user specified shifts +c ISHIFT = 1: exact shift with respect to the matrix H. +c +c WHICH Character*2. (INPUT) +c Shift selection criteria. +c 'LM' -> KEV eigenvalues of largest magnitude are retained. +c 'SM' -> KEV eigenvalues of smallest magnitude are retained. +c 'LA' -> KEV eigenvalues of largest value are retained. +c 'SA' -> KEV eigenvalues of smallest value are retained. +c 'BE' -> KEV eigenvalues, half from each end of the spectrum. +c If KEV is odd, compute one more from the high end. +c +c KEV Integer. (INPUT) +c KEV+NP is the size of the matrix H. +c +c NP Integer. (INPUT) +c Number of implicit shifts to be computed. +c +c RITZ Double precision array of length KEV+NP. (INPUT/OUTPUT) +c On INPUT, RITZ contains the eigenvalues of H. +c On OUTPUT, RITZ are sorted so that the unwanted eigenvalues +c are in the first NP locations and the wanted part is in +c the last KEV locations. When exact shifts are selected, the +c unwanted part corresponds to the shifts to be applied. +c +c BOUNDS Double precision array of length KEV+NP. (INPUT/OUTPUT) +c Error bounds corresponding to the ordering in RITZ. +c +c SHIFTS Double precision array of length NP. (INPUT/OUTPUT) +c On INPUT: contains the user specified shifts if ISHIFT = 0. +c On OUTPUT: contains the shifts sorted into decreasing order +c of magnitude with respect to the Ritz estimates contained in +c BOUNDS. If ISHIFT = 0, SHIFTS is not modified on exit. +c +c\EndDoc +c +c----------------------------------------------------------------------- +c +c\BeginLib +c +c\Local variables: +c xxxxxx real +c +c\Routines called: +c igraphdsortr ARPACK utility sorting routine. +c igraphivout ARPACK utility routine that prints integers. +c igraphsecond ARPACK utility routine for timing. +c igraphdvout ARPACK utility routine that prints vectors. +c dcopy Level 1 BLAS that copies one vector to another. +c dswap Level 1 BLAS that swaps the contents of two vectors. +c +c\Author +c Danny Sorensen Phuong Vu +c Richard Lehoucq CRPC / Rice University +c Dept. of Computational & Houston, Texas +c Applied Mathematics +c Rice University +c Houston, Texas +c +c\Revision history: +c xx/xx/93: Version ' 2.1' +c +c\SCCS Information: @(#) +c FILE: sgets.F SID: 2.4 DATE OF SID: 4/19/96 RELEASE: 2 +c +c\Remarks +c +c\EndLib +c +c----------------------------------------------------------------------- +c + subroutine igraphdsgets ( ishift, which, kev, np, ritz, bounds, + & shifts ) +c +c %----------------------------------------------------% +c | Include files for debugging and timing information | +c %----------------------------------------------------% +c + include 'debug.h' + include 'stat.h' +c +c %------------------% +c | Scalar Arguments | +c %------------------% +c + character*2 which + integer ishift, kev, np +c +c %-----------------% +c | Array Arguments | +c %-----------------% +c + Double precision + & bounds(kev+np), ritz(kev+np), shifts(np) +c +c %------------% +c | Parameters | +c %------------% +c + Double precision + & one, zero + parameter (one = 1.0D+0, zero = 0.0D+0) +c +c %---------------% +c | Local Scalars | +c %---------------% +c + integer kevd2, msglvl +c +c %----------------------% +c | External Subroutines | +c %----------------------% +c + external dswap, dcopy, igraphdsortr, igraphsecond +c +c %---------------------% +c | Intrinsic Functions | +c %---------------------% +c + intrinsic max, min +c +c %-----------------------% +c | Executable Statements | +c %-----------------------% +c +c %-------------------------------% +c | Initialize timing statistics | +c | & message level for debugging | +c %-------------------------------% +c + call igraphsecond (t0) + msglvl = msgets +c + if (which .eq. 'BE') then +c +c %-----------------------------------------------------% +c | Both ends of the spectrum are requested. | +c | Sort the eigenvalues into algebraically increasing | +c | order first then swap high end of the spectrum next | +c | to low end in appropriate locations. | +c | NOTE: when np < floor(kev/2) be careful not to swap | +c | overlapping locations. | +c %-----------------------------------------------------% +c + call igraphdsortr ('LA', .true., kev+np, ritz, bounds) + kevd2 = kev / 2 + if ( kev .gt. 1 ) then + call dswap ( min(kevd2,np), ritz, 1, + & ritz( max(kevd2,np)+1 ), 1) + call dswap ( min(kevd2,np), bounds, 1, + & bounds( max(kevd2,np)+1 ), 1) + end if +c + else +c +c %----------------------------------------------------% +c | LM, SM, LA, SA case. | +c | Sort the eigenvalues of H into the desired order | +c | and apply the resulting order to BOUNDS. | +c | The eigenvalues are sorted so that the wanted part | +c | are always in the last KEV locations. | +c %----------------------------------------------------% +c + call igraphdsortr (which, .true., kev+np, ritz, bounds) + end if +c + if (ishift .eq. 1 .and. np .gt. 0) then +c +c %-------------------------------------------------------% +c | Sort the unwanted Ritz values used as shifts so that | +c | the ones with largest Ritz estimates are first. | +c | This will tend to minimize the effects of the | +c | forward instability of the iteration when the shifts | +c | are applied in subroutine igraphdsapps. | +c %-------------------------------------------------------% +c + call igraphdsortr ('SM', .true., np, bounds, ritz) + call dcopy (np, ritz, 1, shifts, 1) + end if +c + call igraphsecond (t1) + tsgets = tsgets + (t1 - t0) +c + if (msglvl .gt. 0) then + call igraphivout (logfil, 1, kev, ndigit, '_sgets: KEV is') + call igraphivout (logfil, 1, np, ndigit, '_sgets: NP is') + call igraphdvout (logfil, kev+np, ritz, ndigit, + & '_sgets: Eigenvalues of current H matrix') + call igraphdvout (logfil, kev+np, bounds, ndigit, + & '_sgets: Associated Ritz estimates') + end if +c + return +c +c %---------------% +c | End of igraphdsgets | +c %---------------% +c + end diff --git a/src/rigraph/vendor/arpack/dsortc.f b/src/rigraph/vendor/arpack/dsortc.f new file mode 100644 index 0000000..a356adc --- /dev/null +++ b/src/rigraph/vendor/arpack/dsortc.f @@ -0,0 +1,344 @@ +c----------------------------------------------------------------------- +c\BeginDoc +c +c\Name: igraphdsortc +c +c\Description: +c Sorts the complex array in XREAL and XIMAG into the order +c specified by WHICH and optionally applies the permutation to the +c real array Y. It is assumed that if an element of XIMAG is +c nonzero, then its negative is also an element. In other words, +c both members of a complex conjugate pair are to be sorted and the +c pairs are kept adjacent to each other. +c +c\Usage: +c call igraphdsortc +c ( WHICH, APPLY, N, XREAL, XIMAG, Y ) +c +c\Arguments +c WHICH Character*2. (Input) +c 'LM' -> sort XREAL,XIMAG into increasing order of magnitude. +c 'SM' -> sort XREAL,XIMAG into decreasing order of magnitude. +c 'LR' -> sort XREAL into increasing order of algebraic. +c 'SR' -> sort XREAL into decreasing order of algebraic. +c 'LI' -> sort XIMAG into increasing order of magnitude. +c 'SI' -> sort XIMAG into decreasing order of magnitude. +c NOTE: If an element of XIMAG is non-zero, then its negative +c is also an element. +c +c APPLY Logical. (Input) +c APPLY = .TRUE. -> apply the sorted order to array Y. +c APPLY = .FALSE. -> do not apply the sorted order to array Y. +c +c N Integer. (INPUT) +c Size of the arrays. +c +c XREAL, Double precision array of length N. (INPUT/OUTPUT) +c XIMAG Real and imaginary part of the array to be sorted. +c +c Y Double precision array of length N. (INPUT/OUTPUT) +c +c\EndDoc +c +c----------------------------------------------------------------------- +c +c\BeginLib +c +c\Author +c Danny Sorensen Phuong Vu +c Richard Lehoucq CRPC / Rice University +c Dept. of Computational & Houston, Texas +c Applied Mathematics +c Rice University +c Houston, Texas +c +c\Revision history: +c xx/xx/92: Version ' 2.1' +c Adapted from the sort routine in LANSO. +c +c\SCCS Information: @(#) +c FILE: sortc.F SID: 2.3 DATE OF SID: 4/20/96 RELEASE: 2 +c +c\EndLib +c +c----------------------------------------------------------------------- +c + subroutine igraphdsortc (which, apply, n, xreal, ximag, y) +c +c %------------------% +c | Scalar Arguments | +c %------------------% +c + character*2 which + logical apply + integer n +c +c %-----------------% +c | Array Arguments | +c %-----------------% +c + Double precision + & xreal(0:n-1), ximag(0:n-1), y(0:n-1) +c +c %---------------% +c | Local Scalars | +c %---------------% +c + integer i, igap, j + Double precision + & temp, temp1, temp2 +c +c %--------------------% +c | External Functions | +c %--------------------% +c + Double precision + & dlapy2 + external dlapy2 +c +c %-----------------------% +c | Executable Statements | +c %-----------------------% +c + igap = n / 2 +c + if (which .eq. 'LM') then +c +c %------------------------------------------------------% +c | Sort XREAL,XIMAG into increasing order of magnitude. | +c %------------------------------------------------------% +c + 10 continue + if (igap .eq. 0) go to 9000 +c + do 30 i = igap, n-1 + j = i-igap + 20 continue +c + if (j.lt.0) go to 30 +c + temp1 = dlapy2(xreal(j),ximag(j)) + temp2 = dlapy2(xreal(j+igap),ximag(j+igap)) +c + if (temp1.gt.temp2) then + temp = xreal(j) + xreal(j) = xreal(j+igap) + xreal(j+igap) = temp +c + temp = ximag(j) + ximag(j) = ximag(j+igap) + ximag(j+igap) = temp +c + if (apply) then + temp = y(j) + y(j) = y(j+igap) + y(j+igap) = temp + end if + else + go to 30 + end if + j = j-igap + go to 20 + 30 continue + igap = igap / 2 + go to 10 +c + else if (which .eq. 'SM') then +c +c %------------------------------------------------------% +c | Sort XREAL,XIMAG into decreasing order of magnitude. | +c %------------------------------------------------------% +c + 40 continue + if (igap .eq. 0) go to 9000 +c + do 60 i = igap, n-1 + j = i-igap + 50 continue +c + if (j .lt. 0) go to 60 +c + temp1 = dlapy2(xreal(j),ximag(j)) + temp2 = dlapy2(xreal(j+igap),ximag(j+igap)) +c + if (temp1.lt.temp2) then + temp = xreal(j) + xreal(j) = xreal(j+igap) + xreal(j+igap) = temp +c + temp = ximag(j) + ximag(j) = ximag(j+igap) + ximag(j+igap) = temp +c + if (apply) then + temp = y(j) + y(j) = y(j+igap) + y(j+igap) = temp + end if + else + go to 60 + endif + j = j-igap + go to 50 + 60 continue + igap = igap / 2 + go to 40 +c + else if (which .eq. 'LR') then +c +c %------------------------------------------------% +c | Sort XREAL into increasing order of algebraic. | +c %------------------------------------------------% +c + 70 continue + if (igap .eq. 0) go to 9000 +c + do 90 i = igap, n-1 + j = i-igap + 80 continue +c + if (j.lt.0) go to 90 +c + if (xreal(j).gt.xreal(j+igap)) then + temp = xreal(j) + xreal(j) = xreal(j+igap) + xreal(j+igap) = temp +c + temp = ximag(j) + ximag(j) = ximag(j+igap) + ximag(j+igap) = temp +c + if (apply) then + temp = y(j) + y(j) = y(j+igap) + y(j+igap) = temp + end if + else + go to 90 + endif + j = j-igap + go to 80 + 90 continue + igap = igap / 2 + go to 70 +c + else if (which .eq. 'SR') then +c +c %------------------------------------------------% +c | Sort XREAL into decreasing order of algebraic. | +c %------------------------------------------------% +c + 100 continue + if (igap .eq. 0) go to 9000 + do 120 i = igap, n-1 + j = i-igap + 110 continue +c + if (j.lt.0) go to 120 +c + if (xreal(j).lt.xreal(j+igap)) then + temp = xreal(j) + xreal(j) = xreal(j+igap) + xreal(j+igap) = temp +c + temp = ximag(j) + ximag(j) = ximag(j+igap) + ximag(j+igap) = temp +c + if (apply) then + temp = y(j) + y(j) = y(j+igap) + y(j+igap) = temp + end if + else + go to 120 + endif + j = j-igap + go to 110 + 120 continue + igap = igap / 2 + go to 100 +c + else if (which .eq. 'LI') then +c +c %------------------------------------------------% +c | Sort XIMAG into increasing order of magnitude. | +c %------------------------------------------------% +c + 130 continue + if (igap .eq. 0) go to 9000 + do 150 i = igap, n-1 + j = i-igap + 140 continue +c + if (j.lt.0) go to 150 +c + if (abs(ximag(j)).gt.abs(ximag(j+igap))) then + temp = xreal(j) + xreal(j) = xreal(j+igap) + xreal(j+igap) = temp +c + temp = ximag(j) + ximag(j) = ximag(j+igap) + ximag(j+igap) = temp +c + if (apply) then + temp = y(j) + y(j) = y(j+igap) + y(j+igap) = temp + end if + else + go to 150 + endif + j = j-igap + go to 140 + 150 continue + igap = igap / 2 + go to 130 +c + else if (which .eq. 'SI') then +c +c %------------------------------------------------% +c | Sort XIMAG into decreasing order of magnitude. | +c %------------------------------------------------% +c + 160 continue + if (igap .eq. 0) go to 9000 + do 180 i = igap, n-1 + j = i-igap + 170 continue +c + if (j.lt.0) go to 180 +c + if (abs(ximag(j)).lt.abs(ximag(j+igap))) then + temp = xreal(j) + xreal(j) = xreal(j+igap) + xreal(j+igap) = temp +c + temp = ximag(j) + ximag(j) = ximag(j+igap) + ximag(j+igap) = temp +c + if (apply) then + temp = y(j) + y(j) = y(j+igap) + y(j+igap) = temp + end if + else + go to 180 + endif + j = j-igap + go to 170 + 180 continue + igap = igap / 2 + go to 160 + end if +c + 9000 continue + return +c +c %---------------% +c | End of igraphdsortc | +c %---------------% +c + end diff --git a/src/rigraph/vendor/arpack/dsortr.f b/src/rigraph/vendor/arpack/dsortr.f new file mode 100644 index 0000000..d75bd61 --- /dev/null +++ b/src/rigraph/vendor/arpack/dsortr.f @@ -0,0 +1,218 @@ +c----------------------------------------------------------------------- +c\BeginDoc +c +c\Name: igraphdsortr +c +c\Description: +c Sort the array X1 in the order specified by WHICH and optionally +c applies the permutation to the array X2. +c +c\Usage: +c call igraphdsortr +c ( WHICH, APPLY, N, X1, X2 ) +c +c\Arguments +c WHICH Character*2. (Input) +c 'LM' -> X1 is sorted into increasing order of magnitude. +c 'SM' -> X1 is sorted into decreasing order of magnitude. +c 'LA' -> X1 is sorted into increasing order of algebraic. +c 'SA' -> X1 is sorted into decreasing order of algebraic. +c +c APPLY Logical. (Input) +c APPLY = .TRUE. -> apply the sorted order to X2. +c APPLY = .FALSE. -> do not apply the sorted order to X2. +c +c N Integer. (INPUT) +c Size of the arrays. +c +c X1 Double precision array of length N. (INPUT/OUTPUT) +c The array to be sorted. +c +c X2 Double precision array of length N. (INPUT/OUTPUT) +c Only referenced if APPLY = .TRUE. +c +c\EndDoc +c +c----------------------------------------------------------------------- +c +c\BeginLib +c +c\Author +c Danny Sorensen Phuong Vu +c Richard Lehoucq CRPC / Rice University +c Dept. of Computational & Houston, Texas +c Applied Mathematics +c Rice University +c Houston, Texas +c +c\Revision history: +c 12/16/93: Version ' 2.1'. +c Adapted from the sort routine in LANSO. +c +c\SCCS Information: @(#) +c FILE: sortr.F SID: 2.3 DATE OF SID: 4/19/96 RELEASE: 2 +c +c\EndLib +c +c----------------------------------------------------------------------- +c + subroutine igraphdsortr (which, apply, n, x1, x2) +c +c %------------------% +c | Scalar Arguments | +c %------------------% +c + character*2 which + logical apply + integer n +c +c %-----------------% +c | Array Arguments | +c %-----------------% +c + Double precision + & x1(0:n-1), x2(0:n-1) +c +c %---------------% +c | Local Scalars | +c %---------------% +c + integer i, igap, j + Double precision + & temp +c +c %-----------------------% +c | Executable Statements | +c %-----------------------% +c + igap = n / 2 +c + if (which .eq. 'SA') then +c +c X1 is sorted into decreasing order of algebraic. +c + 10 continue + if (igap .eq. 0) go to 9000 + do 30 i = igap, n-1 + j = i-igap + 20 continue +c + if (j.lt.0) go to 30 +c + if (x1(j).lt.x1(j+igap)) then + temp = x1(j) + x1(j) = x1(j+igap) + x1(j+igap) = temp + if (apply) then + temp = x2(j) + x2(j) = x2(j+igap) + x2(j+igap) = temp + end if + else + go to 30 + endif + j = j-igap + go to 20 + 30 continue + igap = igap / 2 + go to 10 +c + else if (which .eq. 'SM') then +c +c X1 is sorted into decreasing order of magnitude. +c + 40 continue + if (igap .eq. 0) go to 9000 + do 60 i = igap, n-1 + j = i-igap + 50 continue +c + if (j.lt.0) go to 60 +c + if (abs(x1(j)).lt.abs(x1(j+igap))) then + temp = x1(j) + x1(j) = x1(j+igap) + x1(j+igap) = temp + if (apply) then + temp = x2(j) + x2(j) = x2(j+igap) + x2(j+igap) = temp + end if + else + go to 60 + endif + j = j-igap + go to 50 + 60 continue + igap = igap / 2 + go to 40 +c + else if (which .eq. 'LA') then +c +c X1 is sorted into increasing order of algebraic. +c + 70 continue + if (igap .eq. 0) go to 9000 + do 90 i = igap, n-1 + j = i-igap + 80 continue +c + if (j.lt.0) go to 90 +c + if (x1(j).gt.x1(j+igap)) then + temp = x1(j) + x1(j) = x1(j+igap) + x1(j+igap) = temp + if (apply) then + temp = x2(j) + x2(j) = x2(j+igap) + x2(j+igap) = temp + end if + else + go to 90 + endif + j = j-igap + go to 80 + 90 continue + igap = igap / 2 + go to 70 +c + else if (which .eq. 'LM') then +c +c X1 is sorted into increasing order of magnitude. +c + 100 continue + if (igap .eq. 0) go to 9000 + do 120 i = igap, n-1 + j = i-igap + 110 continue +c + if (j.lt.0) go to 120 +c + if (abs(x1(j)).gt.abs(x1(j+igap))) then + temp = x1(j) + x1(j) = x1(j+igap) + x1(j+igap) = temp + if (apply) then + temp = x2(j) + x2(j) = x2(j+igap) + x2(j+igap) = temp + end if + else + go to 120 + endif + j = j-igap + go to 110 + 120 continue + igap = igap / 2 + go to 100 + end if +c + 9000 continue + return +c +c %---------------% +c | End of igraphdsortr | +c %---------------% +c + end diff --git a/src/rigraph/vendor/arpack/dstatn.f b/src/rigraph/vendor/arpack/dstatn.f new file mode 100644 index 0000000..afd0a57 --- /dev/null +++ b/src/rigraph/vendor/arpack/dstatn.f @@ -0,0 +1,61 @@ +c +c %---------------------------------------------% +c | Initialize statistic and timing information | +c | for nonsymmetric Arnoldi code. | +c %---------------------------------------------% +c +c\Author +c Danny Sorensen Phuong Vu +c Richard Lehoucq CRPC / Rice University +c Dept. of Computational & Houston, Texas +c Applied Mathematics +c Rice University +c Houston, Texas +c +c\SCCS Information: @(#) +c FILE: statn.F SID: 2.4 DATE OF SID: 4/20/96 RELEASE: 2 +c + subroutine igraphdstatn +c +c %--------------------------------% +c | See stat.doc for documentation | +c %--------------------------------% +c + include 'stat.h' +c +c %-----------------------% +c | Executable Statements | +c %-----------------------% +c + nopx = 0 + nbx = 0 + nrorth = 0 + nitref = 0 + nrstrt = 0 +c + tnaupd = 0.0D+0 + tnaup2 = 0.0D+0 + tnaitr = 0.0D+0 + tneigh = 0.0D+0 + tngets = 0.0D+0 + tnapps = 0.0D+0 + tnconv = 0.0D+0 + titref = 0.0D+0 + tgetv0 = 0.0D+0 + trvec = 0.0D+0 +c +c %----------------------------------------------------% +c | User time including reverse communication overhead | +c %----------------------------------------------------% +c + tmvopx = 0.0D+0 + tmvbx = 0.0D+0 +c + return +c +c +c %---------------% +c | End of igraphdstatn | +c %---------------% +c + end diff --git a/src/rigraph/vendor/arpack/dstats.f b/src/rigraph/vendor/arpack/dstats.f new file mode 100644 index 0000000..545ed19 --- /dev/null +++ b/src/rigraph/vendor/arpack/dstats.f @@ -0,0 +1,47 @@ +c +c\SCCS Information: @(#) +c FILE: stats.F SID: 2.1 DATE OF SID: 4/19/96 RELEASE: 2 +c %---------------------------------------------% +c | Initialize statistic and timing information | +c | for symmetric Arnoldi code. | +c %---------------------------------------------% + + subroutine igraphdstats + +c %--------------------------------% +c | See stat.doc for documentation | +c %--------------------------------% + include 'stat.h' + +c %-----------------------% +c | Executable Statements | +c %-----------------------% + + nopx = 0 + nbx = 0 + nrorth = 0 + nitref = 0 + nrstrt = 0 + + tsaupd = 0.0D+0 + tsaup2 = 0.0D+0 + tsaitr = 0.0D+0 + tseigt = 0.0D+0 + tsgets = 0.0D+0 + tsapps = 0.0D+0 + tsconv = 0.0D+0 + titref = 0.0D+0 + tgetv0 = 0.0D+0 + trvec = 0.0D+0 + +c %----------------------------------------------------% +c | User time including reverse communication overhead | +c %----------------------------------------------------% + tmvopx = 0.0D+0 + tmvbx = 0.0D+0 + + return +c +c End of igraphdstats +c + end diff --git a/src/rigraph/vendor/arpack/dstqrb.f b/src/rigraph/vendor/arpack/dstqrb.f new file mode 100644 index 0000000..eff1369 --- /dev/null +++ b/src/rigraph/vendor/arpack/dstqrb.f @@ -0,0 +1,594 @@ +c----------------------------------------------------------------------- +c\BeginDoc +c +c\Name: igraphdstqrb +c +c\Description: +c Computes all eigenvalues and the last component of the eigenvectors +c of a symmetric tridiagonal matrix using the implicit QL or QR method. +c +c This is mostly a modification of the LAPACK routine dsteqr. +c See Remarks. +c +c\Usage: +c call igraphdstqrb +c ( N, D, E, Z, WORK, INFO ) +c +c\Arguments +c N Integer. (INPUT) +c The number of rows and columns in the matrix. N >= 0. +c +c D Double precision array, dimension (N). (INPUT/OUTPUT) +c On entry, D contains the diagonal elements of the +c tridiagonal matrix. +c On exit, D contains the eigenvalues, in ascending order. +c If an error exit is made, the eigenvalues are correct +c for indices 1,2,...,INFO-1, but they are unordered and +c may not be the smallest eigenvalues of the matrix. +c +c E Double precision array, dimension (N-1). (INPUT/OUTPUT) +c On entry, E contains the subdiagonal elements of the +c tridiagonal matrix in positions 1 through N-1. +c On exit, E has been destroyed. +c +c Z Double precision array, dimension (N). (OUTPUT) +c On exit, Z contains the last row of the orthonormal +c eigenvector matrix of the symmetric tridiagonal matrix. +c If an error exit is made, Z contains the last row of the +c eigenvector matrix associated with the stored eigenvalues. +c +c WORK Double precision array, dimension (max(1,2*N-2)). (WORKSPACE) +c Workspace used in accumulating the transformation for +c computing the last components of the eigenvectors. +c +c INFO Integer. (OUTPUT) +c = 0: normal return. +c < 0: if INFO = -i, the i-th argument had an illegal value. +c > 0: if INFO = +i, the i-th eigenvalue has not converged +c after a total of 30*N iterations. +c +c\Remarks +c 1. None. +c +c----------------------------------------------------------------------- +c +c\BeginLib +c +c\Local variables: +c xxxxxx real +c +c\Routines called: +c daxpy Level 1 BLAS that computes a vector triad. +c dcopy Level 1 BLAS that copies one vector to another. +c dswap Level 1 BLAS that swaps the contents of two vectors. +c lsame LAPACK character comparison routine. +c dlae2 LAPACK routine that computes the eigenvalues of a 2-by-2 +c symmetric matrix. +c dlaev2 LAPACK routine that eigendecomposition of a 2-by-2 symmetric +c matrix. +c dlamch LAPACK routine that determines machine constants. +c dlanst LAPACK routine that computes the norm of a matrix. +c dlapy2 LAPACK routine to compute sqrt(x**2+y**2) carefully. +c dlartg LAPACK Givens rotation construction routine. +c dlascl LAPACK routine for careful scaling of a matrix. +c dlaset LAPACK matrix initialization routine. +c dlasr LAPACK routine that applies an orthogonal transformation to +c a matrix. +c dlasrt LAPACK sorting routine. +c dsteqr LAPACK routine that computes eigenvalues and eigenvectors +c of a symmetric tridiagonal matrix. +c xerbla LAPACK error handler routine. +c +c\Authors +c Danny Sorensen Phuong Vu +c Richard Lehoucq CRPC / Rice University +c Dept. of Computational & Houston, Texas +c Applied Mathematics +c Rice University +c Houston, Texas +c +c\SCCS Information: @(#) +c FILE: stqrb.F SID: 2.5 DATE OF SID: 8/27/96 RELEASE: 2 +c +c\Remarks +c 1. Starting with version 2.5, this routine is a modified version +c of LAPACK version 2.0 subroutine SSTEQR. No lines are deleted, +c only commeted out and new lines inserted. +c All lines commented out have "c$$$" at the beginning. +c Note that the LAPACK version 1.0 subroutine SSTEQR contained +c bugs. +c +c\EndLib +c +c----------------------------------------------------------------------- +c + subroutine igraphdstqrb ( n, d, e, z, work, info ) +c +c %------------------% +c | Scalar Arguments | +c %------------------% +c + integer info, n +c +c %-----------------% +c | Array Arguments | +c %-----------------% +c + Double precision + & d( n ), e( n-1 ), z( n ), work( 2*n-2 ) +c +c .. parameters .. + Double precision + & zero, one, two, three + parameter ( zero = 0.0D+0, one = 1.0D+0, + & two = 2.0D+0, three = 3.0D+0 ) + integer maxit + parameter ( maxit = 30 ) +c .. +c .. local scalars .. + integer i, icompz, ii, iscale, j, jtot, k, l, l1, lend, + & lendm1, lendp1, lendsv, lm1, lsv, m, mm, mm1, + & nm1, nmaxit + Double precision + & anorm, b, c, eps, eps2, f, g, p, r, rt1, rt2, + & s, safmax, safmin, ssfmax, ssfmin, tst +c .. +c .. external functions .. + logical lsame + Double precision + & dlamch, dlanst, dlapy2 + external lsame, dlamch, dlanst, dlapy2 +c .. +c .. external subroutines .. + external dlae2, dlaev2, dlartg, dlascl, dlaset, dlasr, + & dlasrt, dswap, xerbla +c .. +c .. intrinsic functions .. + intrinsic abs, max, sign, sqrt +c .. +c .. executable statements .. +c +c test the input parameters. +c + info = 0 +c +c$$$ IF( LSAME( COMPZ, 'N' ) ) THEN +c$$$ ICOMPZ = 0 +c$$$ ELSE IF( LSAME( COMPZ, 'V' ) ) THEN +c$$$ ICOMPZ = 1 +c$$$ ELSE IF( LSAME( COMPZ, 'I' ) ) THEN +c$$$ ICOMPZ = 2 +c$$$ ELSE +c$$$ ICOMPZ = -1 +c$$$ END IF +c$$$ IF( ICOMPZ.LT.0 ) THEN +c$$$ INFO = -1 +c$$$ ELSE IF( N.LT.0 ) THEN +c$$$ INFO = -2 +c$$$ ELSE IF( ( LDZ.LT.1 ) .OR. ( ICOMPZ.GT.0 .AND. LDZ.LT.MAX( 1, +c$$$ $ N ) ) ) THEN +c$$$ INFO = -6 +c$$$ END IF +c$$$ IF( INFO.NE.0 ) THEN +c$$$ CALL XERBLA( 'SSTEQR', -INFO ) +c$$$ RETURN +c$$$ END IF +c +c *** New starting with version 2.5 *** +c + icompz = 2 +c ************************************* +c +c quick return if possible +c + if( n.eq.0 ) + $ return +c + if( n.eq.1 ) then + if( icompz.eq.2 ) z( 1 ) = one + return + end if +c +c determine the unit roundoff and over/underflow thresholds. +c + eps = dlamch( 'e' ) + eps2 = eps**2 + safmin = dlamch( 's' ) + safmax = one / safmin + ssfmax = sqrt( safmax ) / three + ssfmin = sqrt( safmin ) / eps2 +c +c compute the eigenvalues and eigenvectors of the tridiagonal +c matrix. +c +c$$ if( icompz.eq.2 ) +c$$$ $ call dlaset( 'full', n, n, zero, one, z, ldz ) +c +c *** New starting with version 2.5 *** +c + if ( icompz .eq. 2 ) then + do 5 j = 1, n-1 + z(j) = zero + 5 continue + z( n ) = one + end if +c ************************************* +c + nmaxit = n*maxit + jtot = 0 +c +c determine where the matrix splits and choose ql or qr iteration +c for each block, according to whether top or bottom diagonal +c element is smaller. +c + l1 = 1 + nm1 = n - 1 +c + 10 continue + if( l1.gt.n ) + $ go to 160 + if( l1.gt.1 ) + $ e( l1-1 ) = zero + if( l1.le.nm1 ) then + do 20 m = l1, nm1 + tst = abs( e( m ) ) + if( tst.eq.zero ) + $ go to 30 + if( tst.le.( sqrt( abs( d( m ) ) )*sqrt( abs( d( m+ + $ 1 ) ) ) )*eps ) then + e( m ) = zero + go to 30 + end if + 20 continue + end if + m = n +c + 30 continue + l = l1 + lsv = l + lend = m + lendsv = lend + l1 = m + 1 + if( lend.eq.l ) + $ go to 10 +c +c scale submatrix in rows and columns l to lend +c + anorm = dlanst( 'i', lend-l+1, d( l ), e( l ) ) + iscale = 0 + if( anorm.eq.zero ) + $ go to 10 + if( anorm.gt.ssfmax ) then + iscale = 1 + call dlascl( 'g', 0, 0, anorm, ssfmax, lend-l+1, 1, d( l ), n, + $ info ) + call dlascl( 'g', 0, 0, anorm, ssfmax, lend-l, 1, e( l ), n, + $ info ) + else if( anorm.lt.ssfmin ) then + iscale = 2 + call dlascl( 'g', 0, 0, anorm, ssfmin, lend-l+1, 1, d( l ), n, + $ info ) + call dlascl( 'g', 0, 0, anorm, ssfmin, lend-l, 1, e( l ), n, + $ info ) + end if +c +c choose between ql and qr iteration +c + if( abs( d( lend ) ).lt.abs( d( l ) ) ) then + lend = lsv + l = lendsv + end if +c + if( lend.gt.l ) then +c +c ql iteration +c +c look for small subdiagonal element. +c + 40 continue + if( l.ne.lend ) then + lendm1 = lend - 1 + do 50 m = l, lendm1 + tst = abs( e( m ) )**2 + if( tst.le.( eps2*abs( d( m ) ) )*abs( d( m+1 ) )+ + $ safmin )go to 60 + 50 continue + end if +c + m = lend +c + 60 continue + if( m.lt.lend ) + $ e( m ) = zero + p = d( l ) + if( m.eq.l ) + $ go to 80 +c +c if remaining matrix is 2-by-2, use dlae2 or dlaev2 +c to compute its eigensystem. +c + if( m.eq.l+1 ) then + if( icompz.gt.0 ) then + call dlaev2( d( l ), e( l ), d( l+1 ), rt1, rt2, c, s ) + work( l ) = c + work( n-1+l ) = s +c$$$ call dlasr( 'r', 'v', 'b', n, 2, work( l ), +c$$$ $ work( n-1+l ), z( 1, l ), ldz ) +c +c *** New starting with version 2.5 *** +c + tst = z(l+1) + z(l+1) = c*tst - s*z(l) + z(l) = s*tst + c*z(l) +c ************************************* + else + call dlae2( d( l ), e( l ), d( l+1 ), rt1, rt2 ) + end if + d( l ) = rt1 + d( l+1 ) = rt2 + e( l ) = zero + l = l + 2 + if( l.le.lend ) + $ go to 40 + go to 140 + end if +c + if( jtot.eq.nmaxit ) + $ go to 140 + jtot = jtot + 1 +c +c form shift. +c + g = ( d( l+1 )-p ) / ( two*e( l ) ) + r = dlapy2( g, one ) + g = d( m ) - p + ( e( l ) / ( g+sign( r, g ) ) ) +c + s = one + c = one + p = zero +c +c inner loop +c + mm1 = m - 1 + do 70 i = mm1, l, -1 + f = s*e( i ) + b = c*e( i ) + call dlartg( g, f, c, s, r ) + if( i.ne.m-1 ) + $ e( i+1 ) = r + g = d( i+1 ) - p + r = ( d( i )-g )*s + two*c*b + p = s*r + d( i+1 ) = g + p + g = c*r - b +c +c if eigenvectors are desired, then save rotations. +c + if( icompz.gt.0 ) then + work( i ) = c + work( n-1+i ) = -s + end if +c + 70 continue +c +c if eigenvectors are desired, then apply saved rotations. +c + if( icompz.gt.0 ) then + mm = m - l + 1 +c$$$ call dlasr( 'r', 'v', 'b', n, mm, work( l ), work( n-1+l ), +c$$$ $ z( 1, l ), ldz ) +c +c *** New starting with version 2.5 *** +c + call dlasr( 'r', 'v', 'b', 1, mm, work( l ), + & work( n-1+l ), z( l ), 1 ) +c ************************************* + end if +c + d( l ) = d( l ) - p + e( l ) = g + go to 40 +c +c eigenvalue found. +c + 80 continue + d( l ) = p +c + l = l + 1 + if( l.le.lend ) + $ go to 40 + go to 140 +c + else +c +c qr iteration +c +c look for small superdiagonal element. +c + 90 continue + if( l.ne.lend ) then + lendp1 = lend + 1 + do 100 m = l, lendp1, -1 + tst = abs( e( m-1 ) )**2 + if( tst.le.( eps2*abs( d( m ) ) )*abs( d( m-1 ) )+ + $ safmin )go to 110 + 100 continue + end if +c + m = lend +c + 110 continue + if( m.gt.lend ) + $ e( m-1 ) = zero + p = d( l ) + if( m.eq.l ) + $ go to 130 +c +c if remaining matrix is 2-by-2, use dlae2 or dlaev2 +c to compute its eigensystem. +c + if( m.eq.l-1 ) then + if( icompz.gt.0 ) then + call dlaev2( d( l-1 ), e( l-1 ), d( l ), rt1, rt2, c, s ) +c$$$ work( m ) = c +c$$$ work( n-1+m ) = s +c$$$ call dlasr( 'r', 'v', 'f', n, 2, work( m ), +c$$$ $ work( n-1+m ), z( 1, l-1 ), ldz ) +c +c *** New starting with version 2.5 *** +c + tst = z(l) + z(l) = c*tst - s*z(l-1) + z(l-1) = s*tst + c*z(l-1) +c ************************************* + else + call dlae2( d( l-1 ), e( l-1 ), d( l ), rt1, rt2 ) + end if + d( l-1 ) = rt1 + d( l ) = rt2 + e( l-1 ) = zero + l = l - 2 + if( l.ge.lend ) + $ go to 90 + go to 140 + end if +c + if( jtot.eq.nmaxit ) + $ go to 140 + jtot = jtot + 1 +c +c form shift. +c + g = ( d( l-1 )-p ) / ( two*e( l-1 ) ) + r = dlapy2( g, one ) + g = d( m ) - p + ( e( l-1 ) / ( g+sign( r, g ) ) ) +c + s = one + c = one + p = zero +c +c inner loop +c + lm1 = l - 1 + do 120 i = m, lm1 + f = s*e( i ) + b = c*e( i ) + call dlartg( g, f, c, s, r ) + if( i.ne.m ) + $ e( i-1 ) = r + g = d( i ) - p + r = ( d( i+1 )-g )*s + two*c*b + p = s*r + d( i ) = g + p + g = c*r - b +c +c if eigenvectors are desired, then save rotations. +c + if( icompz.gt.0 ) then + work( i ) = c + work( n-1+i ) = s + end if +c + 120 continue +c +c if eigenvectors are desired, then apply saved rotations. +c + if( icompz.gt.0 ) then + mm = l - m + 1 +c$$$ call dlasr( 'r', 'v', 'f', n, mm, work( m ), work( n-1+m ), +c$$$ $ z( 1, m ), ldz ) +c +c *** New starting with version 2.5 *** +c + call dlasr( 'r', 'v', 'f', 1, mm, work( m ), work( n-1+m ), + & z( m ), 1 ) +c ************************************* + end if +c + d( l ) = d( l ) - p + e( lm1 ) = g + go to 90 +c +c eigenvalue found. +c + 130 continue + d( l ) = p +c + l = l - 1 + if( l.ge.lend ) + $ go to 90 + go to 140 +c + end if +c +c undo scaling if necessary +c + 140 continue + if( iscale.eq.1 ) then + call dlascl( 'g', 0, 0, ssfmax, anorm, lendsv-lsv+1, 1, + $ d( lsv ), n, info ) + call dlascl( 'g', 0, 0, ssfmax, anorm, lendsv-lsv, 1, e( lsv ), + $ n, info ) + else if( iscale.eq.2 ) then + call dlascl( 'g', 0, 0, ssfmin, anorm, lendsv-lsv+1, 1, + $ d( lsv ), n, info ) + call dlascl( 'g', 0, 0, ssfmin, anorm, lendsv-lsv, 1, e( lsv ), + $ n, info ) + end if +c +c check for no convergence to an eigenvalue after a total +c of n*maxit iterations. +c + if( jtot.lt.nmaxit ) + $ go to 10 + do 150 i = 1, n - 1 + if( e( i ).ne.zero ) + $ info = info + 1 + 150 continue + go to 190 +c +c order eigenvalues and eigenvectors. +c + 160 continue + if( icompz.eq.0 ) then +c +c use quick sort +c + call dlasrt( 'i', n, d, info ) +c + else +c +c use selection sort to minimize swaps of eigenvectors +c + do 180 ii = 2, n + i = ii - 1 + k = i + p = d( i ) + do 170 j = ii, n + if( d( j ).lt.p ) then + k = j + p = d( j ) + end if + 170 continue + if( k.ne.i ) then + d( k ) = d( i ) + d( i ) = p +c$$$ call dswap( n, z( 1, i ), 1, z( 1, k ), 1 ) +c *** New starting with version 2.5 *** +c + p = z(k) + z(k) = z(i) + z(i) = p +c ************************************* + end if + 180 continue + end if +c + 190 continue + return +c +c %---------------% +c | End of igraphdstqrb | +c %---------------% +c + end diff --git a/src/rigraph/vendor/arpack/dvout.f b/src/rigraph/vendor/arpack/dvout.f new file mode 100644 index 0000000..8bd7b1b --- /dev/null +++ b/src/rigraph/vendor/arpack/dvout.f @@ -0,0 +1,122 @@ +*----------------------------------------------------------------------- +* 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 IGRAPHDVOUT( LOUT, N, SX, IDIGIT, IFMT ) +* ... +* ... SPECIFICATIONS FOR ARGUMENTS +* ... +* ... SPECIFICATIONS FOR LOCAL VARIABLES +* .. Scalar Arguments .. + CHARACTER*( * ) IFMT + INTEGER IDIGIT, LOUT, N +* .. +* .. Array Arguments .. + DOUBLE PRECISION SX( * ) +* .. +* .. Local Scalars .. + CHARACTER*80 LINE + INTEGER I, K1, K2, LLL, NDIGIT +* .. +* .. Intrinsic Functions .. + INTRINSIC LEN, MIN, MIN0 +* .. +* .. Executable Statements .. +* ... +* ... FIRST EXECUTABLE STATEMENT +* +* +c$$$ LLL = MIN( LEN( IFMT ), 80 ) +c$$$ DO 10 I = 1, LLL +c$$$ LINE( I: I ) = '-' +c$$$ 10 CONTINUE +c$$$* +c$$$ DO 20 I = LLL + 1, 80 +c$$$ LINE( I: I ) = ' ' +c$$$ 20 CONTINUE +c$$$* +c$$$ WRITE( LOUT, FMT = 9999 )IFMT, LINE( 1: LLL ) +c$$$ 9999 FORMAT( / 1X, A, / 1X, A ) +c$$$* +c$$$ IF( N.LE.0 ) +c$$$ $ RETURN +c$$$ NDIGIT = IDIGIT +c$$$ IF( IDIGIT.EQ.0 ) +c$$$ $ NDIGIT = 4 +c$$$* +c$$$*======================================================================= +c$$$* CODE FOR OUTPUT USING 72 COLUMNS FORMAT +c$$$*======================================================================= +c$$$* +c$$$ IF( IDIGIT.LT.0 ) THEN +c$$$ NDIGIT = -IDIGIT +c$$$ IF( NDIGIT.LE.4 ) THEN +c$$$ DO 30 K1 = 1, N, 5 +c$$$ K2 = MIN0( N, K1+4 ) +c$$$ WRITE( LOUT, FMT = 9998 )K1, K2, ( SX( I ), I = K1, K2 ) +c$$$ 30 CONTINUE +c$$$ ELSE IF( NDIGIT.LE.6 ) THEN +c$$$ DO 40 K1 = 1, N, 4 +c$$$ K2 = MIN0( N, K1+3 ) +c$$$ WRITE( LOUT, FMT = 9997 )K1, K2, ( SX( I ), I = K1, K2 ) +c$$$ 40 CONTINUE +c$$$ ELSE IF( NDIGIT.LE.10 ) THEN +c$$$ DO 50 K1 = 1, N, 3 +c$$$ K2 = MIN0( N, K1+2 ) +c$$$ WRITE( LOUT, FMT = 9996 )K1, K2, ( SX( I ), I = K1, K2 ) +c$$$ 50 CONTINUE +c$$$ ELSE +c$$$ DO 60 K1 = 1, N, 2 +c$$$ K2 = MIN0( N, K1+1 ) +c$$$ WRITE( LOUT, FMT = 9995 )K1, K2, ( SX( I ), I = K1, K2 ) +c$$$ 60 CONTINUE +c$$$ END IF +c$$$* +c$$$*======================================================================= +c$$$* CODE FOR OUTPUT USING 132 COLUMNS FORMAT +c$$$*======================================================================= +c$$$* +c$$$ ELSE +c$$$ IF( NDIGIT.LE.4 ) THEN +c$$$ DO 70 K1 = 1, N, 10 +c$$$ K2 = MIN0( N, K1+9 ) +c$$$ WRITE( LOUT, FMT = 9998 )K1, K2, ( SX( I ), I = K1, K2 ) +c$$$ 70 CONTINUE +c$$$ ELSE IF( NDIGIT.LE.6 ) THEN +c$$$ DO 80 K1 = 1, N, 8 +c$$$ K2 = MIN0( N, K1+7 ) +c$$$ WRITE( LOUT, FMT = 9997 )K1, K2, ( SX( I ), I = K1, K2 ) +c$$$ 80 CONTINUE +c$$$ ELSE IF( NDIGIT.LE.10 ) THEN +c$$$ DO 90 K1 = 1, N, 6 +c$$$ K2 = MIN0( N, K1+5 ) +c$$$ WRITE( LOUT, FMT = 9996 )K1, K2, ( SX( I ), I = K1, K2 ) +c$$$ 90 CONTINUE +c$$$ ELSE +c$$$ DO 100 K1 = 1, N, 5 +c$$$ K2 = MIN0( N, K1+4 ) +c$$$ WRITE( LOUT, FMT = 9995 )K1, K2, ( SX( I ), I = K1, K2 ) +c$$$ 100 CONTINUE +c$$$ END IF +c$$$ END IF +c$$$ WRITE( LOUT, FMT = 9994 ) +c$$$ RETURN +c$$$ 9998 FORMAT( 1X, I4, ' - ', I4, ':', 1P, 10D12.3 ) +c$$$ 9997 FORMAT( 1X, I4, ' - ', I4, ':', 1X, 1P, 8D14.5 ) +c$$$ 9996 FORMAT( 1X, I4, ' - ', I4, ':', 1X, 1P, 6D18.9 ) +c$$$ 9995 FORMAT( 1X, I4, ' - ', I4, ':', 1X, 1P, 5D24.13 ) +c$$$ 9994 FORMAT( 1X, ' ' ) + END diff --git a/src/rigraph/vendor/arpack/ivout.f b/src/rigraph/vendor/arpack/ivout.f new file mode 100644 index 0000000..3f6089c --- /dev/null +++ b/src/rigraph/vendor/arpack/ivout.f @@ -0,0 +1,120 @@ +C----------------------------------------------------------------------- +C Routine: IVOUT +C +C Purpose: Integer vector output routine. +C +C Usage: CALL IVOUT (LOUT, N, IX, IDIGIT, IFMT) +C +C Arguments +C N - Length of array IX. (Input) +C IX - Integer array to be printed. (Input) +C IFMT - Format to be used in printing array IX. (Input) +C IDIGIT - Print up to ABS(IDIGIT) decimal digits / number. (Input) +C If IDIGIT .LT. 0, printing is done with 72 columns. +C If IDIGIT .GT. 0, printing is done with 132 columns. +C +C----------------------------------------------------------------------- +C + SUBROUTINE IGRAPHIVOUT (LOUT, N, IX, IDIGIT, IFMT) +C ... +C ... SPECIFICATIONS FOR ARGUMENTS + INTEGER IX(*), N, IDIGIT, LOUT + CHARACTER IFMT*(*) +C ... +C ... SPECIFICATIONS FOR LOCAL VARIABLES + INTEGER I, NDIGIT, K1, K2, LLL + CHARACTER*80 LINE +* ... +* ... SPECIFICATIONS INTRINSICS + INTRINSIC MIN +* +C +c$$$ LLL = MIN ( LEN ( IFMT ), 80 ) +c$$$ DO 1 I = 1, LLL +c$$$ LINE(I:I) = '-' +c$$$ 1 CONTINUE +c$$$C +c$$$ DO 2 I = LLL+1, 80 +c$$$ LINE(I:I) = ' ' +c$$$ 2 CONTINUE +c$$$C +c$$$ WRITE ( LOUT, 2000 ) IFMT, LINE(1:LLL) +c$$$ 2000 FORMAT ( /1X, A /1X, A ) +c$$$C +c$$$ IF (N .LE. 0) RETURN +c$$$ NDIGIT = IDIGIT +c$$$ IF (IDIGIT .EQ. 0) NDIGIT = 4 +c$$$C +c$$$C======================================================================= +c$$$C CODE FOR OUTPUT USING 72 COLUMNS FORMAT +c$$$C======================================================================= +c$$$C +c$$$ IF (IDIGIT .LT. 0) THEN +c$$$C +c$$$ NDIGIT = -IDIGIT +c$$$ IF (NDIGIT .LE. 4) THEN +c$$$ DO 10 K1 = 1, N, 10 +c$$$ K2 = MIN0(N,K1+9) +c$$$ WRITE(LOUT,1000) K1,K2,(IX(I),I=K1,K2) +c$$$ 10 CONTINUE +c$$$C +c$$$ ELSE IF (NDIGIT .LE. 6) THEN +c$$$ DO 30 K1 = 1, N, 7 +c$$$ K2 = MIN0(N,K1+6) +c$$$ WRITE(LOUT,1001) K1,K2,(IX(I),I=K1,K2) +c$$$ 30 CONTINUE +c$$$C +c$$$ ELSE IF (NDIGIT .LE. 10) THEN +c$$$ DO 50 K1 = 1, N, 5 +c$$$ K2 = MIN0(N,K1+4) +c$$$ WRITE(LOUT,1002) K1,K2,(IX(I),I=K1,K2) +c$$$ 50 CONTINUE +c$$$C +c$$$ ELSE +c$$$ DO 70 K1 = 1, N, 3 +c$$$ K2 = MIN0(N,K1+2) +c$$$ WRITE(LOUT,1003) K1,K2,(IX(I),I=K1,K2) +c$$$ 70 CONTINUE +c$$$ END IF +c$$$C +c$$$C======================================================================= +c$$$C CODE FOR OUTPUT USING 132 COLUMNS FORMAT +c$$$C======================================================================= +c$$$C +c$$$ ELSE +c$$$C +c$$$ IF (NDIGIT .LE. 4) THEN +c$$$ DO 90 K1 = 1, N, 20 +c$$$ K2 = MIN0(N,K1+19) +c$$$ WRITE(LOUT,1000) K1,K2,(IX(I),I=K1,K2) +c$$$ 90 CONTINUE +c$$$C +c$$$ ELSE IF (NDIGIT .LE. 6) THEN +c$$$ DO 110 K1 = 1, N, 15 +c$$$ K2 = MIN0(N,K1+14) +c$$$ WRITE(LOUT,1001) K1,K2,(IX(I),I=K1,K2) +c$$$ 110 CONTINUE +c$$$C +c$$$ ELSE IF (NDIGIT .LE. 10) THEN +c$$$ DO 130 K1 = 1, N, 10 +c$$$ K2 = MIN0(N,K1+9) +c$$$ WRITE(LOUT,1002) K1,K2,(IX(I),I=K1,K2) +c$$$ 130 CONTINUE +c$$$C +c$$$ ELSE +c$$$ DO 150 K1 = 1, N, 7 +c$$$ K2 = MIN0(N,K1+6) +c$$$ WRITE(LOUT,1003) K1,K2,(IX(I),I=K1,K2) +c$$$ 150 CONTINUE +c$$$ END IF +c$$$ END IF +c$$$ WRITE (LOUT,1004) +c$$$C +c$$$ 1000 FORMAT(1X,I4,' - ',I4,':',20(1X,I5)) +c$$$ 1001 FORMAT(1X,I4,' - ',I4,':',15(1X,I7)) +c$$$ 1002 FORMAT(1X,I4,' - ',I4,':',10(1X,I11)) +c$$$ 1003 FORMAT(1X,I4,' - ',I4,':',7(1X,I15)) +c$$$ 1004 FORMAT(1X,' ') +c$$$C + RETURN + END diff --git a/src/rigraph/vendor/arpack/second.f b/src/rigraph/vendor/arpack/second.f new file mode 100644 index 0000000..37023c3 --- /dev/null +++ b/src/rigraph/vendor/arpack/second.f @@ -0,0 +1,35 @@ + SUBROUTINE IGRAPHSECOND( T ) +* + REAL T +* +* -- 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 igraphseconds. +* This version gets the time from the system function ETIME. +* +* .. Local Scalars .. + REAL T1 +* .. +* .. Local Arrays .. + REAL TARRAY( 2 ) +* .. +* .. External Functions .. + REAL ETIME +* .. +* .. Executable Statements .. +* + TARRAY( 1 ) = 0.0 + T1 = ETIME( TARRAY ) + T = TARRAY( 1 ) + + RETURN +* +* End of SECOND +* + END diff --git a/src/rigraph/vendor/arpack/stat.h b/src/rigraph/vendor/arpack/stat.h new file mode 100644 index 0000000..ae407cb --- /dev/null +++ b/src/rigraph/vendor/arpack/stat.h @@ -0,0 +1,21 @@ +c %--------------------------------% +c | See stat.doc for documentation | +c %--------------------------------% +c +c\SCCS Information: @(#) +c FILE: stat.h SID: 2.2 DATE OF SID: 11/16/95 RELEASE: 2 +c + real t0, t1, t2, t3, t4, t5 +c save t0, t1, t2, t3, t4, t5 +c + integer nopx, nbx, nrorth, nitref, nrstrt + real tsaupd, tsaup2, tsaitr, tseigt, tsgets, tsapps, tsconv, + & tnaupd, tnaup2, tnaitr, tneigh, tngets, tnapps, tnconv, + & tcaupd, tcaup2, tcaitr, tceigh, tcgets, tcapps, tcconv, + & tmvopx, tmvbx, tgetv0, titref, trvec + common /timing/ + & nopx, nbx, nrorth, nitref, nrstrt, + & tsaupd, tsaup2, tsaitr, tseigt, tsgets, tsapps, tsconv, + & tnaupd, tnaup2, tnaitr, tneigh, tngets, tnapps, tnconv, + & tcaupd, tcaup2, tcaitr, tceigh, tcgets, tcapps, tcconv, + & tmvopx, tmvbx, tgetv0, titref, trvec diff --git a/src/rigraph/vendor/arpack/wrap.f b/src/rigraph/vendor/arpack/wrap.f new file mode 100644 index 0000000..39c4720 --- /dev/null +++ b/src/rigraph/vendor/arpack/wrap.f @@ -0,0 +1,151 @@ +c----------------------------------------------------------------------- +c Wrapper functions, so we don't need to pass logicals from +c C to Fortran, because that generates LTO warnings, as the compiler +c apparently cannot match a Fortran logical to a C type. +c----------------------------------------------------------------------- +c + subroutine igraphxdsortr (which, apply, n, x1, x2) +c + character*2 which + integer apply + integer n + Double precision + & x1(0:n-1), x2(0:n-1) +c + logical applyx +c + if (apply .eq. 1) then + applyx = .true. + else + applyx = .false. + end if +c + call igraphdsortr(which, applyx, n, x1, x2) +c + return +c + end +c +c----------------------------------------------------------------------- +c + subroutine igraphxdsortc (which, apply, n, xreal, ximag, y) +c + character*2 which + integer apply + integer n +c + Double precision + & xreal(0:n-1), ximag(0:n-1), y(0:n-1) +c + logical applyx +c + if (apply .eq. 1) then + applyx = .true. + else + applyx = .false. + end if +c + call igraphdsortc(which, applyx, n, xreal, ximag, y) +c + return +c + end +c +c----------------------------------------------------------------------- +c + subroutine igraphxdneupd (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) +c + character bmat, howmny, which*2 + integer rvec + integer info, ldz, ldv, lworkl, n, ncv, nev + Double precision + & sigmar, sigmai, tol +c + integer iparam(11), ipntr(14) + integer select(ncv) + Double precision + & dr(nev+1), di(nev+1), resid(n), v(ldv,ncv), z(ldz,*), + & workd(3*n), workl(lworkl), workev(3*ncv) +c + logical rvecx + integer i + logical selectx(ncv) +c + if (rvec .eq. 1) then + rvecx = .true. + else + rvecx = .false. + end if +c + i = 1 + 100 if (i .gt. ncv) then + go to 110 + end if + if (select(i) .eq. 1) then + selectx(i) = .true. + else + selectx(i) = .false. + end if + i = i + 1 + go to 100 + 110 continue +c + call igraphdneupd(rvecx, howmny, selectx, dr, di, z, ldz, + & sigmar, sigmai, workev, bmat, n, which, nev, tol, + & resid, ncv, v, ldv, iparam, ipntr, workd, + & workl, lworkl, info) +c + return +c + end +c +c----------------------------------------------------------------------- +c + subroutine igraphxdseupd (rvec, howmny, select, d, z, ldz, + & sigma, bmat, n, which, nev, tol, resid, ncv, v, ldv, iparam, + & ipntr, workd, workl, lworkl, info ) +c + character bmat, howmny, which*2 + integer rvec, select(ncv) + integer info, ldz, ldv, lworkl, n, ncv, nev + Double precision + & sigma, tol +c + integer iparam(7), ipntr(11) + Double precision + & d(nev), resid(n), v(ldv,ncv), z(ldz, nev), + & workd(2*n), workl(lworkl) +c + logical rvecx + integer i + logical selectx(ncv) +c + if (rvec .eq. 1) then + rvecx = .true. + else + rvecx = .false. + end if +c + i = 1 + 100 if (i .gt. ncv) then + go to 110 + end if + if (select(i) .eq. 1) then + selectx(i) = .true. + else + selectx(i) = .false. + end if + i = i + 1 + go to 100 + 110 continue +c + call igraphdseupd(rvecx, howmny, selectx, d, z, ldz, + & sigma, bmat, n, which, nev, tol, resid, ncv, v, ldv, iparam, + & ipntr, workd, workl, lworkl, info ) +c + return +c + end diff --git a/src/rigraph/vendor/cs/SuiteSparse_config.h b/src/rigraph/vendor/cs/SuiteSparse_config.h new file mode 100644 index 0000000..bd0cced --- /dev/null +++ b/src/rigraph/vendor/cs/SuiteSparse_config.h @@ -0,0 +1,221 @@ +/* ========================================================================== */ +/* === SuiteSparse_config =================================================== */ +/* ========================================================================== */ + +/* Configuration file for SuiteSparse: a Suite of Sparse matrix packages + * (AMD, COLAMD, CCOLAMD, CAMD, CHOLMOD, UMFPACK, CXSparse, and others). + * + * SuiteSparse_config.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 SuiteSparse_config.mk: + * + * CFLAGS = -O -D'SuiteSparse_long=long long' \ + * -D'SuiteSparse_long_max=9223372036854775801' -D'SuiteSparse_long_idd="lld"' + * + * This file defines SuiteSparse_long as either long (on all but _WIN64) or + * __int64 on Windows 64. The intent is that a SuiteSparse_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) 2012, Timothy A. Davis. No licensing restrictions apply + * to this file or to the SuiteSparse_config directory. + * Author: Timothy A. Davis. + */ + +#ifndef SUITESPARSE_CONFIG_H +#define SUITESPARSE_CONFIG_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +/* ========================================================================== */ +/* === SuiteSparse_long ===================================================== */ +/* ========================================================================== */ + +#ifndef SuiteSparse_long + +#ifdef _WIN64 + +#define SuiteSparse_long __int64 +#define SuiteSparse_long_max _I64_MAX +#define SuiteSparse_long_idd "I64d" + +#else + +#define SuiteSparse_long long +#define SuiteSparse_long_max LONG_MAX +#define SuiteSparse_long_idd "ld" + +#endif +#define SuiteSparse_long_id "%" SuiteSparse_long_idd +#endif + +/* Disable unneeded parts for igraph */ +#if 0 /* start comment */ + +/* ========================================================================== */ +/* === SuiteSparse_config parameters and functions ========================== */ +/* ========================================================================== */ + +/* SuiteSparse-wide parameters are placed in this struct. It is meant to be + an extern, globally-accessible struct. It is not meant to be updated + frequently by multiple threads. Rather, if an application needs to modify + SuiteSparse_config, it should do it once at the beginning of the application, + before multiple threads are launched. + + The intent of these function pointers is that they not be used in your + application directly, except to assign them to the desired user-provided + functions. Rather, you should use the + */ + +struct SuiteSparse_config_struct +{ + void *(*malloc_func) (size_t) ; /* pointer to malloc */ + void *(*calloc_func) (size_t, size_t) ; /* pointer to calloc */ + void *(*realloc_func) (void *, size_t) ; /* pointer to realloc */ + void (*free_func) (void *) ; /* pointer to free */ + int (*printf_func) (const char *, ...) ; /* pointer to printf */ + double (*hypot_func) (double, double) ; /* pointer to hypot */ + int (*divcomplex_func) (double, double, double, double, double *, double *); +} ; + +extern struct SuiteSparse_config_struct SuiteSparse_config ; + +void SuiteSparse_start ( void ) ; /* called to start SuiteSparse */ + +void SuiteSparse_finish ( void ) ; /* called to finish SuiteSparse */ + +void *SuiteSparse_malloc /* pointer to allocated block of memory */ +( + size_t nitems, /* number of items to malloc (>=1 is enforced) */ + size_t size_of_item /* sizeof each item */ +) ; + +void *SuiteSparse_calloc /* pointer to allocated block of memory */ +( + size_t nitems, /* number of items to calloc (>=1 is enforced) */ + size_t size_of_item /* sizeof each item */ +) ; + +void *SuiteSparse_realloc /* pointer to reallocated block of memory, or + to original block if the realloc failed. */ +( + size_t nitems_new, /* new number of items in the object */ + size_t nitems_old, /* old number of items in the object */ + size_t size_of_item, /* sizeof each item */ + void *p, /* old object to reallocate */ + int *ok /* 1 if successful, 0 otherwise */ +) ; + +void *SuiteSparse_free /* always returns NULL */ +( + void *p /* block to free */ +) ; + +void SuiteSparse_tic /* start the timer */ +( + double tic [2] /* output, contents undefined on input */ +) ; + +double SuiteSparse_toc /* return time in seconds since last tic */ +( + double tic [2] /* input: from last call to SuiteSparse_tic */ +) ; + +double SuiteSparse_time /* returns current wall clock time in seconds */ +( + void +) ; + +/* returns sqrt (x^2 + y^2), computed reliably */ +double SuiteSparse_hypot (double x, double y) ; + +/* complex division of c = a/b */ +int SuiteSparse_divcomplex +( + double ar, double ai, /* real and imaginary parts of a */ + double br, double bi, /* real and imaginary parts of b */ + double *cr, double *ci /* real and imaginary parts of c */ +) ; + +/* determine which timer to use, if any */ +#ifndef NTIMER +#ifdef _POSIX_C_SOURCE +#if _POSIX_C_SOURCE >= 199309L +#define SUITESPARSE_TIMER_ENABLED +#endif +#endif +#endif + +/* SuiteSparse printf macro */ +#define SUITESPARSE_PRINTF(params) \ +{ \ + if (SuiteSparse_config.printf_func != NULL) \ + { \ + (void) (SuiteSparse_config.printf_func) params ; \ + } \ +} + +/* ========================================================================== */ +/* === 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, which is also the version number of SuiteSparse_config. + */ + +int SuiteSparse_version /* returns SUITESPARSE_VERSION */ +( + /* output, not defined on input. Not used if NULL. Returns + the three version codes in version [0..2]: + version [0] is SUITESPARSE_MAIN_VERSION + version [1] is SUITESPARSE_SUB_VERSION + version [2] is SUITESPARSE_SUBSUB_VERSION + */ + int version [3] +) ; + +/* Versions prior to 4.2.0 do not have the above function. The following + code fragment will work with any version of SuiteSparse: + + #ifdef SUITESPARSE_HAS_VERSION_FUNCTION + v = SuiteSparse_version (NULL) ; + #else + v = SUITESPARSE_VERSION ; + #endif +*/ +#define SUITESPARSE_HAS_VERSION_FUNCTION + +#endif /* end comment */ + +#define SUITESPARSE_DATE "Mar 3, 2021" +#define SUITESPARSE_VER_CODE(main,sub) ((main) * 1000 + (sub)) +#define SUITESPARSE_MAIN_VERSION 5 +#define SUITESPARSE_SUB_VERSION 9 +#define SUITESPARSE_SUBSUB_VERSION 0 +#define SUITESPARSE_VERSION \ + SUITESPARSE_VER_CODE(SUITESPARSE_MAIN_VERSION,SUITESPARSE_SUB_VERSION) + +#ifdef __cplusplus +} +#endif +#endif diff --git a/src/rigraph/vendor/cs/cs.h b/src/rigraph/vendor/cs/cs.h new file mode 100644 index 0000000..0e58521 --- /dev/null +++ b/src/rigraph/vendor/cs/cs.h @@ -0,0 +1,758 @@ +/* ========================================================================== */ +/* CXSparse/Include/cs.h file */ +/* ========================================================================== */ + +/* This is the CXSparse/Include/cs.h file. It has the same name (cs.h) as + the CSparse/Include/cs.h file. The 'make install' for SuiteSparse installs + CXSparse, and this file, instead of CSparse. The two packages have the same + cs.h include filename, because CXSparse is a superset of CSparse. Any user + program that uses CSparse can rely on CXSparse instead, with no change to the + user code. The #include "cs.h" line will work for both versions, in user + code, and the function names and user-visible typedefs from CSparse all + appear in CXSparse. For experimenting and changing the package itself, I + recommend using CSparse since it's simpler and easier to modify. For + using the package in production codes, I recommend CXSparse since it has + more features (support for complex matrices, and both int and long + versions). + */ + +/* ========================================================================== */ + +#ifndef _CXS_H +#define _CXS_H +#include +#include +#include +#include +#ifdef MATLAB_MEX_FILE +#include "mex.h" +#endif + + +#ifdef __cplusplus +#ifndef NCOMPLEX +#include +typedef std::complex cs_complex_t ; +#endif +extern "C" { +#else +#ifndef NCOMPLEX +#include +#define cs_complex_t double _Complex +#endif +#endif + +#define CS_VER 3 /* CXSparse Version */ +#define CS_SUBVER 2 +#define CS_SUBSUB 0 +#define CS_DATE "Sept 12, 2017" /* CSparse release date */ +#define CS_COPYRIGHT "Copyright (c) Timothy A. Davis, 2006-2016" +#define CXSPARSE + +#include "SuiteSparse_config.h" +#define cs_long_t SuiteSparse_long +#define cs_long_t_id SuiteSparse_long_id +#define cs_long_t_max SuiteSparse_long_max + +/* -------------------------------------------------------------------------- */ +/* 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/cs_long_t version of CXSparse */ +/* -------------------------------------------------------------------------- */ + +/* --- primary CSparse routines and data structures ------------------------- */ + +typedef struct cs_dl_sparse /* matrix in compressed-column or triplet form */ +{ + cs_long_t nzmax ; /* maximum number of entries */ + cs_long_t m ; /* number of rows */ + cs_long_t n ; /* number of columns */ + cs_long_t *p ; /* column pointers (size n+1) or col indlces (size nzmax) */ + cs_long_t *i ; /* row indices, size nzmax */ + double *x ; /* numerical values, size nzmax */ + cs_long_t 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) ; +cs_long_t cs_dl_cholsol (cs_long_t order, const cs_dl *A, double *b) ; +cs_long_t cs_dl_dupl (cs_dl *A) ; +cs_long_t cs_dl_entry (cs_dl *T, cs_long_t i, cs_long_t j, double x) ; +cs_long_t cs_dl_lusol (cs_long_t order, const cs_dl *A, double *b, double tol) ; +cs_long_t 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) ; +cs_long_t cs_dl_qrsol (cs_long_t order, const cs_dl *A, double *b) ; +cs_dl *cs_dl_transpose (const cs_dl *A, cs_long_t values) ; +cs_dl *cs_dl_compress (const cs_dl *T) ; +double cs_dl_norm (const cs_dl *A) ; +/*cs_long_t cs_dl_print (const cs_dl *A, cs_long_t brief) ;*/ +cs_dl *cs_dl_load (FILE *f) ; + +/* utilities */ +void *cs_dl_calloc (cs_long_t n, size_t size) ; +void *cs_dl_free (void *p) ; +void *cs_dl_realloc (void *p, cs_long_t n, size_t size, cs_long_t *ok) ; +cs_dl *cs_dl_spalloc (cs_long_t m, cs_long_t n, cs_long_t nzmax, cs_long_t values, + cs_long_t t) ; +cs_dl *cs_dl_spfree (cs_dl *A) ; +cs_long_t cs_dl_sprealloc (cs_dl *A, cs_long_t nzmax) ; +void *cs_dl_malloc (cs_long_t n, size_t size) ; + +/* --- secondary CSparse routines and data structures ----------------------- */ + +typedef struct cs_dl_symbolic /* symbolic Cholesky, LU, or QR analysis */ +{ + cs_long_t *pinv ; /* inverse row perm. for QR, fill red. perm for Chol */ + cs_long_t *q ; /* fill-reducing column permutation for LU and QR */ + cs_long_t *parent ; /* elimination tree for Cholesky and QR */ + cs_long_t *cp ; /* column pointers for Cholesky, row counts for QR */ + cs_long_t *leftmost ; /* leftmost[i] = min(find(A(i,:))), for QR */ + cs_long_t 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 */ + cs_long_t *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 */ +{ + cs_long_t *p ; /* size m, row permutation */ + cs_long_t *q ; /* size n, column permutation */ + cs_long_t *r ; /* size nb+1, block k is rows r[k] to r[k+1]-1 in A(p,q) */ + cs_long_t *s ; /* size nb+1, block k is cols s[k] to s[k+1]-1 in A(p,q) */ + cs_long_t nb ; /* # of blocks in fine dmperm decomposition */ + cs_long_t rr [5] ; /* coarse row decomposition */ + cs_long_t cc [5] ; /* coarse column decomposition */ +} cs_dld ; + +cs_long_t *cs_dl_amd (cs_long_t 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, cs_long_t seed) ; +cs_long_t cs_dl_droptol (cs_dl *A, double tol) ; +cs_long_t cs_dl_dropzeros (cs_dl *A) ; +cs_long_t cs_dl_happly (const cs_dl *V, cs_long_t i, double beta, double *x) ; +cs_long_t cs_dl_ipvec (const cs_long_t *p, const double *b, double *x, cs_long_t n) ; +cs_long_t cs_dl_lsolve (const cs_dl *L, double *x) ; +cs_long_t 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 cs_long_t *pinv, const cs_long_t *q, + cs_long_t values) ; +cs_long_t *cs_dl_pinv (const cs_long_t *p, cs_long_t n) ; +cs_long_t cs_dl_pvec (const cs_long_t *p, const double *b, double *x, cs_long_t n) ; +cs_dln *cs_dl_qr (const cs_dl *A, const cs_dls *S) ; +cs_dls *cs_dl_schol (cs_long_t order, const cs_dl *A) ; +cs_dls *cs_dl_sqr (cs_long_t order, const cs_dl *A, cs_long_t qr) ; +cs_dl *cs_dl_symperm (const cs_dl *A, const cs_long_t *pinv, cs_long_t values) ; +cs_long_t cs_dl_usolve (const cs_dl *U, double *x) ; +cs_long_t cs_dl_utsolve (const cs_dl *U, double *x) ; +cs_long_t cs_dl_updown (cs_dl *L, cs_long_t sigma, const cs_dl *C, + const cs_long_t *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 -------------------------------------------- */ + +cs_long_t *cs_dl_counts (const cs_dl *A, const cs_long_t *parent, + const cs_long_t *post, cs_long_t ata) ; +double cs_dl_cumsum (cs_long_t *p, cs_long_t *c, cs_long_t n) ; +cs_long_t cs_dl_dfs (cs_long_t j, cs_dl *G, cs_long_t top, cs_long_t *xi, + cs_long_t *pstack, const cs_long_t *pinv) ; +cs_long_t *cs_dl_etree (const cs_dl *A, cs_long_t ata) ; +cs_long_t cs_dl_fkeep (cs_dl *A, + cs_long_t (*fkeep) (cs_long_t, cs_long_t, double, void *), void *other) ; +double cs_dl_house (double *x, double *beta, cs_long_t n) ; +cs_long_t *cs_dl_maxtrans (const cs_dl *A, cs_long_t seed) ; +cs_long_t *cs_dl_post (const cs_long_t *parent, cs_long_t n) ; +cs_dld *cs_dl_scc (cs_dl *A) ; +cs_long_t cs_dl_scatter (const cs_dl *A, cs_long_t j, double beta, cs_long_t *w, + double *x, cs_long_t mark,cs_dl *C, cs_long_t nz) ; +cs_long_t cs_dl_tdfs (cs_long_t j, cs_long_t k, cs_long_t *head, const cs_long_t *next, + cs_long_t *post, cs_long_t *stack) ; +cs_long_t cs_dl_leaf (cs_long_t i, cs_long_t j, const cs_long_t *first, + cs_long_t *maxfirst, cs_long_t *prevleaf, cs_long_t *ancestor, cs_long_t *jleaf) ; +cs_long_t cs_dl_reach (cs_dl *G, const cs_dl *B, cs_long_t k, cs_long_t *xi, + const cs_long_t *pinv) ; +cs_long_t cs_dl_spsolve (cs_dl *L, const cs_dl *B, cs_long_t k, cs_long_t *xi, + double *x, const cs_long_t *pinv, cs_long_t lo) ; +cs_long_t cs_dl_ereach (const cs_dl *A, cs_long_t k, const cs_long_t *parent, + cs_long_t *s, cs_long_t *w) ; +cs_long_t *cs_dl_randperm (cs_long_t n, cs_long_t seed) ; + +/* utilities */ +cs_dld *cs_dl_dalloc (cs_long_t m, cs_long_t n) ; +cs_dl *cs_dl_done (cs_dl *C, void *w, void *x, cs_long_t ok) ; +cs_long_t *cs_dl_idone (cs_long_t *p, cs_dl *C, void *w, cs_long_t ok) ; +cs_dln *cs_dl_ndone (cs_dln *N, cs_dl *C, void *w, void *x, cs_long_t ok) ; +cs_dld *cs_dl_ddone (cs_dld *D, cs_dl *C, void *w, cs_long_t 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/cs_long_t version of CXSparse */ +/* -------------------------------------------------------------------------- */ + +/* --- primary CSparse routines and data structures ------------------------- */ + +typedef struct cs_cl_sparse /* matrix in compressed-column or triplet form */ +{ + cs_long_t nzmax ; /* maximum number of entries */ + cs_long_t m ; /* number of rows */ + cs_long_t n ; /* number of columns */ + cs_long_t *p ; /* column pointers (size n+1) or col indlces (size nzmax) */ + cs_long_t *i ; /* row indices, size nzmax */ + cs_complex_t *x ; /* numerical values, size nzmax */ + cs_long_t 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) ; +cs_long_t cs_cl_cholsol (cs_long_t order, const cs_cl *A, cs_complex_t *b) ; +cs_long_t cs_cl_dupl (cs_cl *A) ; +cs_long_t cs_cl_entry (cs_cl *T, cs_long_t i, cs_long_t j, cs_complex_t x) ; +cs_long_t cs_cl_lusol (cs_long_t order, const cs_cl *A, cs_complex_t *b, + double tol) ; +cs_long_t 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) ; +cs_long_t cs_cl_qrsol (cs_long_t order, const cs_cl *A, cs_complex_t *b) ; +cs_cl *cs_cl_transpose (const cs_cl *A, cs_long_t values) ; +cs_cl *cs_cl_compress (const cs_cl *T) ; +double cs_cl_norm (const cs_cl *A) ; +/*cs_long_t cs_cl_print (const cs_cl *A, cs_long_t brief) ;*/ +cs_cl *cs_cl_load (FILE *f) ; + +/* utilities */ +void *cs_cl_calloc (cs_long_t n, size_t size) ; +void *cs_cl_free (void *p) ; +void *cs_cl_realloc (void *p, cs_long_t n, size_t size, cs_long_t *ok) ; +cs_cl *cs_cl_spalloc (cs_long_t m, cs_long_t n, cs_long_t nzmax, cs_long_t values, + cs_long_t t) ; +cs_cl *cs_cl_spfree (cs_cl *A) ; +cs_long_t cs_cl_sprealloc (cs_cl *A, cs_long_t nzmax) ; +void *cs_cl_malloc (cs_long_t n, size_t size) ; + +/* --- secondary CSparse routines and data structures ----------------------- */ + +typedef struct cs_cl_symbolic /* symbolic Cholesky, LU, or QR analysis */ +{ + cs_long_t *pinv ; /* inverse row perm. for QR, fill red. perm for Chol */ + cs_long_t *q ; /* fill-reducing column permutation for LU and QR */ + cs_long_t *parent ; /* elimination tree for Cholesky and QR */ + cs_long_t *cp ; /* column pointers for Cholesky, row counts for QR */ + cs_long_t *leftmost ; /* leftmost[i] = min(find(A(i,:))), for QR */ + cs_long_t 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 */ + cs_long_t *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 */ +{ + cs_long_t *p ; /* size m, row permutation */ + cs_long_t *q ; /* size n, column permutation */ + cs_long_t *r ; /* size nb+1, block k is rows r[k] to r[k+1]-1 in A(p,q) */ + cs_long_t *s ; /* size nb+1, block k is cols s[k] to s[k+1]-1 in A(p,q) */ + cs_long_t nb ; /* # of blocks in fine dmperm decomposition */ + cs_long_t rr [5] ; /* coarse row decomposition */ + cs_long_t cc [5] ; /* coarse column decomposition */ +} cs_cld ; + +cs_long_t *cs_cl_amd (cs_long_t 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, cs_long_t seed) ; +cs_long_t cs_cl_droptol (cs_cl *A, double tol) ; +cs_long_t cs_cl_dropzeros (cs_cl *A) ; +cs_long_t cs_cl_happly (const cs_cl *V, cs_long_t i, double beta, cs_complex_t *x) ; +cs_long_t cs_cl_ipvec (const cs_long_t *p, const cs_complex_t *b, + cs_complex_t *x, cs_long_t n) ; +cs_long_t cs_cl_lsolve (const cs_cl *L, cs_complex_t *x) ; +cs_long_t 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 cs_long_t *pinv, const cs_long_t *q, + cs_long_t values) ; +cs_long_t *cs_cl_pinv (const cs_long_t *p, cs_long_t n) ; +cs_long_t cs_cl_pvec (const cs_long_t *p, const cs_complex_t *b, + cs_complex_t *x, cs_long_t n) ; +cs_cln *cs_cl_qr (const cs_cl *A, const cs_cls *S) ; +cs_cls *cs_cl_schol (cs_long_t order, const cs_cl *A) ; +cs_cls *cs_cl_sqr (cs_long_t order, const cs_cl *A, cs_long_t qr) ; +cs_cl *cs_cl_symperm (const cs_cl *A, const cs_long_t *pinv, cs_long_t values) ; +cs_long_t cs_cl_usolve (const cs_cl *U, cs_complex_t *x) ; +cs_long_t cs_cl_utsolve (const cs_cl *U, cs_complex_t *x) ; +cs_long_t cs_cl_updown (cs_cl *L, cs_long_t sigma, const cs_cl *C, + const cs_long_t *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 -------------------------------------------- */ + +cs_long_t *cs_cl_counts (const cs_cl *A, const cs_long_t *parent, + const cs_long_t *post, cs_long_t ata) ; +double cs_cl_cumsum (cs_long_t *p, cs_long_t *c, cs_long_t n) ; +cs_long_t cs_cl_dfs (cs_long_t j, cs_cl *G, cs_long_t top, cs_long_t *xi, + cs_long_t *pstack, const cs_long_t *pinv) ; +cs_long_t *cs_cl_etree (const cs_cl *A, cs_long_t ata) ; +cs_long_t cs_cl_fkeep (cs_cl *A, + cs_long_t (*fkeep) (cs_long_t, cs_long_t, cs_complex_t, void *), void *other) ; +cs_complex_t cs_cl_house (cs_complex_t *x, double *beta, cs_long_t n) ; +cs_long_t *cs_cl_maxtrans (const cs_cl *A, cs_long_t seed) ; +cs_long_t *cs_cl_post (const cs_long_t *parent, cs_long_t n) ; +cs_cld *cs_cl_scc (cs_cl *A) ; +cs_long_t cs_cl_scatter (const cs_cl *A, cs_long_t j, cs_complex_t beta, + cs_long_t *w, cs_complex_t *x, cs_long_t mark,cs_cl *C, cs_long_t nz) ; +cs_long_t cs_cl_tdfs (cs_long_t j, cs_long_t k, cs_long_t *head, const cs_long_t *next, + cs_long_t *post, cs_long_t *stack) ; +cs_long_t cs_cl_leaf (cs_long_t i, cs_long_t j, const cs_long_t *first, + cs_long_t *maxfirst, cs_long_t *prevleaf, cs_long_t *ancestor, cs_long_t *jleaf) ; +cs_long_t cs_cl_reach (cs_cl *G, const cs_cl *B, cs_long_t k, cs_long_t *xi, + const cs_long_t *pinv) ; +cs_long_t cs_cl_spsolve (cs_cl *L, const cs_cl *B, cs_long_t k, cs_long_t *xi, + cs_complex_t *x, const cs_long_t *pinv, cs_long_t lo) ; +cs_long_t cs_cl_ereach (const cs_cl *A, cs_long_t k, const cs_long_t *parent, + cs_long_t *s, cs_long_t *w) ; +cs_long_t *cs_cl_randperm (cs_long_t n, cs_long_t seed) ; + +/* utilities */ +cs_cld *cs_cl_dalloc (cs_long_t m, cs_long_t n) ; +cs_cl *cs_cl_done (cs_cl *C, void *w, void *x, cs_long_t ok) ; +cs_long_t *cs_cl_idone (cs_long_t *p, cs_cl *C, void *w, cs_long_t ok) ; +cs_cln *cs_cl_ndone (cs_cln *N, cs_cl *C, void *w, void *x, cs_long_t ok) ; +cs_cld *cs_cl_ddone (cs_cld *D, cs_cl *C, void *w, cs_long_t ok) ; + +#endif + +/* -------------------------------------------------------------------------- */ +/* Macros for constructing each version of CSparse */ +/* -------------------------------------------------------------------------- */ + +#ifdef CS_LONG +#define CS_INT cs_long_t +#define CS_INT_MAX cs_long_t_max +#define CS_ID cs_long_t_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, cs_long_t real) ; +cs_cl *cs_l_complex (cs_dl *A, cs_long_t real) ; +#endif + +#ifdef __cplusplus +} +#endif +#endif diff --git a/src/rigraph/vendor/cs/cs_add.c b/src/rigraph/vendor/cs/cs_add.c new file mode 100644 index 0000000..44b0d3f --- /dev/null +++ b/src/rigraph/vendor/cs/cs_add.c @@ -0,0 +1,28 @@ +#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/src/rigraph/vendor/cs/cs_amd.c b/src/rigraph/vendor/cs/cs_amd.c new file mode 100644 index 0000000..3f5c702 --- /dev/null +++ b/src/rigraph/vendor/cs/cs_amd.c @@ -0,0 +1,364 @@ +#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 ; + 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 = ((h<0) ? (-h):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/src/rigraph/vendor/cs/cs_chol.c b/src/rigraph/vendor/cs/cs_chol.c new file mode 100644 index 0000000..535809a --- /dev/null +++ b/src/rigraph/vendor/cs/cs_chol.c @@ -0,0 +1,59 @@ +#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/src/rigraph/vendor/cs/cs_cholsol.c b/src/rigraph/vendor/cs/cs_cholsol.c new file mode 100644 index 0000000..6e7dc4f --- /dev/null +++ b/src/rigraph/vendor/cs/cs_cholsol.c @@ -0,0 +1,26 @@ +#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/src/rigraph/vendor/cs/cs_compress.c b/src/rigraph/vendor/cs/cs_compress.c new file mode 100644 index 0000000..dc62eba --- /dev/null +++ b/src/rigraph/vendor/cs/cs_compress.c @@ -0,0 +1,22 @@ +#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/src/rigraph/vendor/cs/cs_counts.c b/src/rigraph/vendor/cs/cs_counts.c new file mode 100644 index 0000000..a1b3de0 --- /dev/null +++ b/src/rigraph/vendor/cs/cs_counts.c @@ -0,0 +1,61 @@ +#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/src/rigraph/vendor/cs/cs_cumsum.c b/src/rigraph/vendor/cs/cs_cumsum.c new file mode 100644 index 0000000..e839497 --- /dev/null +++ b/src/rigraph/vendor/cs/cs_cumsum.c @@ -0,0 +1,17 @@ +#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/src/rigraph/vendor/cs/cs_dfs.c b/src/rigraph/vendor/cs/cs_dfs.c new file mode 100644 index 0000000..6c7115d --- /dev/null +++ b/src/rigraph/vendor/cs/cs_dfs.c @@ -0,0 +1,36 @@ +#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/src/rigraph/vendor/cs/cs_dmperm.c b/src/rigraph/vendor/cs/cs_dmperm.c new file mode 100644 index 0000000..e213845 --- /dev/null +++ b/src/rigraph/vendor/cs/cs_dmperm.c @@ -0,0 +1,144 @@ +#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/src/rigraph/vendor/cs/cs_droptol.c b/src/rigraph/vendor/cs/cs_droptol.c new file mode 100644 index 0000000..59b8df2 --- /dev/null +++ b/src/rigraph/vendor/cs/cs_droptol.c @@ -0,0 +1,9 @@ +#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/src/rigraph/vendor/cs/cs_dropzeros.c b/src/rigraph/vendor/cs/cs_dropzeros.c new file mode 100644 index 0000000..d93f605 --- /dev/null +++ b/src/rigraph/vendor/cs/cs_dropzeros.c @@ -0,0 +1,9 @@ +#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/src/rigraph/vendor/cs/cs_dupl.c b/src/rigraph/vendor/cs/cs_dupl.c new file mode 100644 index 0000000..fdf2e1e --- /dev/null +++ b/src/rigraph/vendor/cs/cs_dupl.c @@ -0,0 +1,34 @@ +#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/src/rigraph/vendor/cs/cs_entry.c b/src/rigraph/vendor/cs/cs_entry.c new file mode 100644 index 0000000..f712ba7 --- /dev/null +++ b/src/rigraph/vendor/cs/cs_entry.c @@ -0,0 +1,13 @@ +#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/src/rigraph/vendor/cs/cs_ereach.c b/src/rigraph/vendor/cs/cs_ereach.c new file mode 100644 index 0000000..9edad52 --- /dev/null +++ b/src/rigraph/vendor/cs/cs_ereach.c @@ -0,0 +1,23 @@ +#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/src/rigraph/vendor/cs/cs_etree.c b/src/rigraph/vendor/cs/cs_etree.c new file mode 100644 index 0000000..5620928 --- /dev/null +++ b/src/rigraph/vendor/cs/cs_etree.c @@ -0,0 +1,30 @@ +#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/src/rigraph/vendor/cs/cs_fkeep.c b/src/rigraph/vendor/cs/cs_fkeep.c new file mode 100644 index 0000000..09219e8 --- /dev/null +++ b/src/rigraph/vendor/cs/cs_fkeep.c @@ -0,0 +1,25 @@ +#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/src/rigraph/vendor/cs/cs_gaxpy.c b/src/rigraph/vendor/cs/cs_gaxpy.c new file mode 100644 index 0000000..db93cbc --- /dev/null +++ b/src/rigraph/vendor/cs/cs_gaxpy.c @@ -0,0 +1,17 @@ +#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/src/rigraph/vendor/cs/cs_happly.c b/src/rigraph/vendor/cs/cs_happly.c new file mode 100644 index 0000000..98a306c --- /dev/null +++ b/src/rigraph/vendor/cs/cs_happly.c @@ -0,0 +1,19 @@ +#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/src/rigraph/vendor/cs/cs_house.c b/src/rigraph/vendor/cs/cs_house.c new file mode 100644 index 0000000..e825c89 --- /dev/null +++ b/src/rigraph/vendor/cs/cs_house.c @@ -0,0 +1,30 @@ +#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/src/rigraph/vendor/cs/cs_ipvec.c b/src/rigraph/vendor/cs/cs_ipvec.c new file mode 100644 index 0000000..4935ace --- /dev/null +++ b/src/rigraph/vendor/cs/cs_ipvec.c @@ -0,0 +1,9 @@ +#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/src/rigraph/vendor/cs/cs_leaf.c b/src/rigraph/vendor/cs/cs_leaf.c new file mode 100644 index 0000000..bd93bda --- /dev/null +++ b/src/rigraph/vendor/cs/cs_leaf.c @@ -0,0 +1,22 @@ +#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/src/rigraph/vendor/cs/cs_load.c b/src/rigraph/vendor/cs/cs_load.c new file mode 100644 index 0000000..91e1f37 --- /dev/null +++ b/src/rigraph/vendor/cs/cs_load.c @@ -0,0 +1,26 @@ +#include "cs.h" +/* load a triplet matrix from a file */ +cs *cs_load (FILE *f) +{ + double i, j ; /* use double for integers to avoid csi conflicts */ + 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, "%lg %lg %lg %lg\n", &i, &j, &x, &xi) == 4) +#else + while (fscanf (f, "%lg %lg %lg\n", &i, &j, &x) == 3) +#endif + { +#ifdef CS_COMPLEX + if (!cs_entry (T, (CS_INT) i, (CS_INT) j, x + xi*I)) return (cs_spfree (T)) ; +#else + if (!cs_entry (T, (CS_INT) i, (CS_INT) j, x)) return (cs_spfree (T)) ; +#endif + } + return (T) ; +} diff --git a/src/rigraph/vendor/cs/cs_lsolve.c b/src/rigraph/vendor/cs/cs_lsolve.c new file mode 100644 index 0000000..099b0c5 --- /dev/null +++ b/src/rigraph/vendor/cs/cs_lsolve.c @@ -0,0 +1,18 @@ +#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/src/rigraph/vendor/cs/cs_ltsolve.c b/src/rigraph/vendor/cs/cs_ltsolve.c new file mode 100644 index 0000000..29b1ca2 --- /dev/null +++ b/src/rigraph/vendor/cs/cs_ltsolve.c @@ -0,0 +1,18 @@ +#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/src/rigraph/vendor/cs/cs_lu.c b/src/rigraph/vendor/cs/cs_lu.c new file mode 100644 index 0000000..270172c --- /dev/null +++ b/src/rigraph/vendor/cs/cs_lu.c @@ -0,0 +1,88 @@ +#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)) ; + /* tol=1 for partial pivoting; tol<1 gives preference to diagonal */ + 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/src/rigraph/vendor/cs/cs_lusol.c b/src/rigraph/vendor/cs/cs_lusol.c new file mode 100644 index 0000000..e0727e2 --- /dev/null +++ b/src/rigraph/vendor/cs/cs_lusol.c @@ -0,0 +1,26 @@ +#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/src/rigraph/vendor/cs/cs_malloc.c b/src/rigraph/vendor/cs/cs_malloc.c new file mode 100644 index 0000000..2a3f6da --- /dev/null +++ b/src/rigraph/vendor/cs/cs_malloc.c @@ -0,0 +1,35 @@ +#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/src/rigraph/vendor/cs/cs_maxtrans.c b/src/rigraph/vendor/cs/cs_maxtrans.c new file mode 100644 index 0000000..4947cee --- /dev/null +++ b/src/rigraph/vendor/cs/cs_maxtrans.c @@ -0,0 +1,92 @@ +#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/src/rigraph/vendor/cs/cs_multiply.c b/src/rigraph/vendor/cs/cs_multiply.c new file mode 100644 index 0000000..34e3a36 --- /dev/null +++ b/src/rigraph/vendor/cs/cs_multiply.c @@ -0,0 +1,35 @@ +#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/src/rigraph/vendor/cs/cs_norm.c b/src/rigraph/vendor/cs/cs_norm.c new file mode 100644 index 0000000..0e7b3c6 --- /dev/null +++ b/src/rigraph/vendor/cs/cs_norm.c @@ -0,0 +1,16 @@ +#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/src/rigraph/vendor/cs/cs_permute.c b/src/rigraph/vendor/cs/cs_permute.c new file mode 100644 index 0000000..9adae45 --- /dev/null +++ b/src/rigraph/vendor/cs/cs_permute.c @@ -0,0 +1,25 @@ +#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/src/rigraph/vendor/cs/cs_pinv.c b/src/rigraph/vendor/cs/cs_pinv.c new file mode 100644 index 0000000..de0660e --- /dev/null +++ b/src/rigraph/vendor/cs/cs_pinv.c @@ -0,0 +1,11 @@ +#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/src/rigraph/vendor/cs/cs_post.c b/src/rigraph/vendor/cs/cs_post.c new file mode 100644 index 0000000..0f61203 --- /dev/null +++ b/src/rigraph/vendor/cs/cs_post.c @@ -0,0 +1,24 @@ +#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/src/rigraph/vendor/cs/cs_print.c b/src/rigraph/vendor/cs/cs_print.c new file mode 100644 index 0000000..7e7b16d --- /dev/null +++ b/src/rigraph/vendor/cs/cs_print.c @@ -0,0 +1,55 @@ +#include "cs.h" +/* print a sparse matrix; use %g for integers to avoid differences with CS_INT */ + +/* Disabled for igraph as it prints to stdio */ +#if 0 +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 ("%g-by-%g, nzmax: %g nnz: %g, 1-norm: %g\n", (double) m, + (double) n, (double) nzmax, (double) (Ap [n]), cs_norm (A)) ; + for (j = 0 ; j < n ; j++) + { + printf (" col %g : locations %g to %g\n", (double) j, + (double) (Ap [j]), (double) (Ap [j+1]-1)) ; + for (p = Ap [j] ; p < Ap [j+1] ; p++) + { + printf (" %g : ", (double) (Ai [p])) ; +#ifdef CS_COMPLEX + printf ("(%g, %g)\n", + Ax ? CS_REAL (Ax [p]) : 1, Ax ? CS_IMAG (Ax [p]) : 0) ; +#else + printf ("%g\n", Ax ? Ax [p] : 1) ; +#endif + if (brief && p > 20) { printf (" ...\n") ; return (1) ; } + } + } + } + else + { + printf ("triplet: %g-by-%g, nzmax: %g nnz: %g\n", (double) m, + (double) n, (double) nzmax, (double) nz) ; + for (p = 0 ; p < nz ; p++) + { + + printf (" %g %g : ", (double) (Ai [p]), (double) (Ap [p])) ; +#ifdef CS_COMPLEX + printf ("(%g, %g)\n", + Ax ? CS_REAL (Ax [p]) : 1, Ax ? CS_IMAG (Ax [p]) : 0) ; +#else + printf ("%g\n", Ax ? Ax [p] : 1) ; +#endif + if (brief && p > 20) { printf (" ...\n") ; return (1) ; } + } + } + return (1) ; +} +#endif diff --git a/src/rigraph/vendor/cs/cs_pvec.c b/src/rigraph/vendor/cs/cs_pvec.c new file mode 100644 index 0000000..1254c2a --- /dev/null +++ b/src/rigraph/vendor/cs/cs_pvec.c @@ -0,0 +1,9 @@ +#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/src/rigraph/vendor/cs/cs_qr.c b/src/rigraph/vendor/cs/cs_qr.c new file mode 100644 index 0000000..8bce32e --- /dev/null +++ b/src/rigraph/vendor/cs/cs_qr.c @@ -0,0 +1,74 @@ +#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, 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) ; + 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/src/rigraph/vendor/cs/cs_qrsol.c b/src/rigraph/vendor/cs/cs_qrsol.c new file mode 100644 index 0000000..d817ef2 --- /dev/null +++ b/src/rigraph/vendor/cs/cs_qrsol.c @@ -0,0 +1,53 @@ +#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/src/rigraph/vendor/cs/cs_randperm.c b/src/rigraph/vendor/cs/cs_randperm.c new file mode 100644 index 0000000..4570da0 --- /dev/null +++ b/src/rigraph/vendor/cs/cs_randperm.c @@ -0,0 +1,28 @@ +#include "cs.h" + +#include "igraph_random.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 = 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/src/rigraph/vendor/cs/cs_reach.c b/src/rigraph/vendor/cs/cs_reach.c new file mode 100644 index 0000000..0efb342 --- /dev/null +++ b/src/rigraph/vendor/cs/cs_reach.c @@ -0,0 +1,19 @@ +#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/src/rigraph/vendor/cs/cs_scatter.c b/src/rigraph/vendor/cs/cs_scatter.c new file mode 100644 index 0000000..734fdb2 --- /dev/null +++ b/src/rigraph/vendor/cs/cs_scatter.c @@ -0,0 +1,22 @@ +#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/src/rigraph/vendor/cs/cs_scc.c b/src/rigraph/vendor/cs/cs_scc.c new file mode 100644 index 0000000..cc6d805 --- /dev/null +++ b/src/rigraph/vendor/cs/cs_scc.c @@ -0,0 +1,41 @@ +#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/src/rigraph/vendor/cs/cs_schol.c b/src/rigraph/vendor/cs/cs_schol.c new file mode 100644 index 0000000..7da2a57 --- /dev/null +++ b/src/rigraph/vendor/cs/cs_schol.c @@ -0,0 +1,26 @@ +#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/src/rigraph/vendor/cs/cs_spsolve.c b/src/rigraph/vendor/cs/cs_spsolve.c new file mode 100644 index 0000000..8c6ecce --- /dev/null +++ b/src/rigraph/vendor/cs/cs_spsolve.c @@ -0,0 +1,28 @@ +#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/src/rigraph/vendor/cs/cs_sqr.c b/src/rigraph/vendor/cs/cs_sqr.c new file mode 100644 index 0000000..1b14ca4 --- /dev/null +++ b/src/rigraph/vendor/cs/cs_sqr.c @@ -0,0 +1,87 @@ +#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] ; + 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/src/rigraph/vendor/cs/cs_symperm.c b/src/rigraph/vendor/cs/cs_symperm.c new file mode 100644 index 0000000..7bbd6fe --- /dev/null +++ b/src/rigraph/vendor/cs/cs_symperm.c @@ -0,0 +1,39 @@ +#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/src/rigraph/vendor/cs/cs_tdfs.c b/src/rigraph/vendor/cs/cs_tdfs.c new file mode 100644 index 0000000..f32077b --- /dev/null +++ b/src/rigraph/vendor/cs/cs_tdfs.c @@ -0,0 +1,24 @@ +#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/src/rigraph/vendor/cs/cs_transpose.c b/src/rigraph/vendor/cs/cs_transpose.c new file mode 100644 index 0000000..15a6c00 --- /dev/null +++ b/src/rigraph/vendor/cs/cs_transpose.c @@ -0,0 +1,25 @@ +#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/src/rigraph/vendor/cs/cs_updown.c b/src/rigraph/vendor/cs/cs_updown.c new file mode 100644 index 0000000..e49af83 --- /dev/null +++ b/src/rigraph/vendor/cs/cs_updown.c @@ -0,0 +1,48 @@ +#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/src/rigraph/vendor/cs/cs_usolve.c b/src/rigraph/vendor/cs/cs_usolve.c new file mode 100644 index 0000000..6e89c83 --- /dev/null +++ b/src/rigraph/vendor/cs/cs_usolve.c @@ -0,0 +1,18 @@ +#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/src/rigraph/vendor/cs/cs_util.c b/src/rigraph/vendor/cs/cs_util.c new file mode 100644 index 0000000..bb887ea --- /dev/null +++ b/src/rigraph/vendor/cs/cs_util.c @@ -0,0 +1,120 @@ +#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 ; + nzmax = CS_MAX (nzmax, 1) ; + 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 *) 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 ((csn *) 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 ((css *) 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 ((csd *) cs_free (D)) ; /* free the csd struct and return NULL */ +} + +/* 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_INT *) cs_free (p)) ; /* return result, or 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/src/rigraph/vendor/cs/cs_utsolve.c b/src/rigraph/vendor/cs/cs_utsolve.c new file mode 100644 index 0000000..d879fae --- /dev/null +++ b/src/rigraph/vendor/cs/cs_utsolve.c @@ -0,0 +1,18 @@ +#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/src/rigraph/vendor/mini-gmp/mini-gmp.c b/src/rigraph/vendor/mini-gmp/mini-gmp.c new file mode 100644 index 0000000..76a691b --- /dev/null +++ b/src/rigraph/vendor/mini-gmp/mini-gmp.c @@ -0,0 +1,4578 @@ +/* mini-gmp, a minimalistic implementation of a GNU GMP subset. + + Contributed to the GNU project by Niels Möller + +Copyright 1991-1997, 1999-2019 Free Software Foundation, Inc. + +This file is part of the GNU MP Library. + +The GNU MP Library is free software; you can redistribute it and/or modify +it under the terms of either: + + * the GNU Lesser General Public License as published by the Free + Software Foundation; either version 3 of the License, or (at your + option) any later version. + +or + + * the GNU General Public License as published by the Free Software + Foundation; either version 2 of the License, or (at your option) any + later version. + +or both in parallel, as here. + +The GNU MP Library is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received copies of the GNU General Public License and the +GNU Lesser General Public License along with the GNU MP Library. If not, +see https://www.gnu.org/licenses/. */ + +/* NOTE: All functions in this file which are not declared in + mini-gmp.h are internal, and are not intended to be compatible + with GMP or with future versions of mini-gmp. */ + +/* Much of the material copied from GMP files, including: gmp-impl.h, + longlong.h, mpn/generic/add_n.c, mpn/generic/addmul_1.c, + mpn/generic/lshift.c, mpn/generic/mul_1.c, + mpn/generic/mul_basecase.c, mpn/generic/rshift.c, + mpn/generic/sbpi1_div_qr.c, mpn/generic/sub_n.c, + mpn/generic/submul_1.c. */ + +#include +#include +#include +#include +#include +#include + +#include "mini-gmp.h" + +#if !defined(MINI_GMP_DONT_USE_FLOAT_H) +#include +#endif + +#include "igraph_error.h" + + +/* Macros */ +#define GMP_LIMB_BITS (sizeof(mp_limb_t) * CHAR_BIT) + +#define GMP_LIMB_MAX ((mp_limb_t) ~ (mp_limb_t) 0) +#define GMP_LIMB_HIGHBIT ((mp_limb_t) 1 << (GMP_LIMB_BITS - 1)) + +#define GMP_HLIMB_BIT ((mp_limb_t) 1 << (GMP_LIMB_BITS / 2)) +#define GMP_LLIMB_MASK (GMP_HLIMB_BIT - 1) + +#define GMP_ULONG_BITS (sizeof(unsigned long) * CHAR_BIT) +#define GMP_ULONG_HIGHBIT ((unsigned long) 1 << (GMP_ULONG_BITS - 1)) + +#define GMP_ABS(x) ((x) >= 0 ? (x) : -(x)) +#define GMP_NEG_CAST(T,x) (-((T)((x) + 1) - 1)) + +#define GMP_MIN(a, b) ((a) < (b) ? (a) : (b)) +#define GMP_MAX(a, b) ((a) > (b) ? (a) : (b)) + +#define GMP_CMP(a,b) (((a) > (b)) - ((a) < (b))) + +#if defined(DBL_MANT_DIG) && FLT_RADIX == 2 +#define GMP_DBL_MANT_BITS DBL_MANT_DIG +#else +#define GMP_DBL_MANT_BITS (53) +#endif + +/* Return non-zero if xp,xsize and yp,ysize overlap. + If xp+xsize<=yp there's no overlap, or if yp+ysize<=xp there's no + overlap. If both these are false, there's an overlap. */ +#define GMP_MPN_OVERLAP_P(xp, xsize, yp, ysize) \ + ((xp) + (xsize) > (yp) && (yp) + (ysize) > (xp)) + +#define gmp_assert_nocarry(x) do { \ + mp_limb_t __cy = (x); \ + assert (__cy == 0); \ + } while (0) + +#define gmp_clz(count, x) do { \ + mp_limb_t __clz_x = (x); \ + unsigned __clz_c = 0; \ + int LOCAL_SHIFT_BITS = 8; \ + if (GMP_LIMB_BITS > LOCAL_SHIFT_BITS) \ + for (; \ + (__clz_x & ((mp_limb_t) 0xff << (GMP_LIMB_BITS - 8))) == 0; \ + __clz_c += 8) \ + { __clz_x <<= LOCAL_SHIFT_BITS; } \ + for (; (__clz_x & GMP_LIMB_HIGHBIT) == 0; __clz_c++) \ + __clz_x <<= 1; \ + (count) = __clz_c; \ + } while (0) + +#define gmp_ctz(count, x) do { \ + mp_limb_t __ctz_x = (x); \ + unsigned __ctz_c = 0; \ + gmp_clz (__ctz_c, __ctz_x & - __ctz_x); \ + (count) = GMP_LIMB_BITS - 1 - __ctz_c; \ + } while (0) + +#define gmp_add_ssaaaa(sh, sl, ah, al, bh, bl) \ + do { \ + mp_limb_t __x; \ + __x = (al) + (bl); \ + (sh) = (ah) + (bh) + (__x < (al)); \ + (sl) = __x; \ + } while (0) + +#define gmp_sub_ddmmss(sh, sl, ah, al, bh, bl) \ + do { \ + mp_limb_t __x; \ + __x = (al) - (bl); \ + (sh) = (ah) - (bh) - ((al) < (bl)); \ + (sl) = __x; \ + } while (0) + +#define gmp_umul_ppmm(w1, w0, u, v) \ + do { \ + int LOCAL_GMP_LIMB_BITS = GMP_LIMB_BITS; \ + if (sizeof(unsigned int) * CHAR_BIT >= 2 * GMP_LIMB_BITS) \ + { \ + unsigned int __ww = (unsigned int) (u) * (v); \ + w0 = (mp_limb_t) __ww; \ + w1 = (mp_limb_t) (__ww >> LOCAL_GMP_LIMB_BITS); \ + } \ + else if (GMP_ULONG_BITS >= 2 * GMP_LIMB_BITS) \ + { \ + unsigned long int __ww = (unsigned long int) (u) * (v); \ + w0 = (mp_limb_t) __ww; \ + w1 = (mp_limb_t) (__ww >> LOCAL_GMP_LIMB_BITS); \ + } \ + else { \ + mp_limb_t __x0, __x1, __x2, __x3; \ + unsigned __ul, __vl, __uh, __vh; \ + mp_limb_t __u = (u), __v = (v); \ + \ + __ul = __u & GMP_LLIMB_MASK; \ + __uh = __u >> (GMP_LIMB_BITS / 2); \ + __vl = __v & GMP_LLIMB_MASK; \ + __vh = __v >> (GMP_LIMB_BITS / 2); \ + \ + __x0 = (mp_limb_t) __ul * __vl; \ + __x1 = (mp_limb_t) __ul * __vh; \ + __x2 = (mp_limb_t) __uh * __vl; \ + __x3 = (mp_limb_t) __uh * __vh; \ + \ + __x1 += __x0 >> (GMP_LIMB_BITS / 2);/* this can't give carry */ \ + __x1 += __x2; /* but this indeed can */ \ + if (__x1 < __x2) /* did we get it? */ \ + __x3 += GMP_HLIMB_BIT; /* yes, add it in the proper pos. */ \ + \ + (w1) = __x3 + (__x1 >> (GMP_LIMB_BITS / 2)); \ + (w0) = (__x1 << (GMP_LIMB_BITS / 2)) + (__x0 & GMP_LLIMB_MASK); \ + } \ + } while (0) + +#define gmp_udiv_qrnnd_preinv(q, r, nh, nl, d, di) \ + do { \ + mp_limb_t _qh, _ql, _r, _mask; \ + gmp_umul_ppmm (_qh, _ql, (nh), (di)); \ + gmp_add_ssaaaa (_qh, _ql, _qh, _ql, (nh) + 1, (nl)); \ + _r = (nl) - _qh * (d); \ + _mask = -(mp_limb_t) (_r > _ql); /* both > and >= are OK */ \ + _qh += _mask; \ + _r += _mask & (d); \ + if (_r >= (d)) \ + { \ + _r -= (d); \ + _qh++; \ + } \ + \ + (r) = _r; \ + (q) = _qh; \ + } while (0) + +#define gmp_udiv_qr_3by2(q, r1, r0, n2, n1, n0, d1, d0, dinv) \ + do { \ + mp_limb_t _q0, _t1, _t0, _mask; \ + gmp_umul_ppmm ((q), _q0, (n2), (dinv)); \ + gmp_add_ssaaaa ((q), _q0, (q), _q0, (n2), (n1)); \ + \ + /* Compute the two most significant limbs of n - q'd */ \ + (r1) = (n1) - (d1) * (q); \ + gmp_sub_ddmmss ((r1), (r0), (r1), (n0), (d1), (d0)); \ + gmp_umul_ppmm (_t1, _t0, (d0), (q)); \ + gmp_sub_ddmmss ((r1), (r0), (r1), (r0), _t1, _t0); \ + (q)++; \ + \ + /* Conditionally adjust q and the remainders */ \ + _mask = - (mp_limb_t) ((r1) >= _q0); \ + (q) += _mask; \ + gmp_add_ssaaaa ((r1), (r0), (r1), (r0), _mask & (d1), _mask & (d0)); \ + if ((r1) >= (d1)) \ + { \ + if ((r1) > (d1) || (r0) >= (d0)) \ + { \ + (q)++; \ + gmp_sub_ddmmss ((r1), (r0), (r1), (r0), (d1), (d0)); \ + } \ + } \ + } while (0) + +/* Swap macros. */ +#define MP_LIMB_T_SWAP(x, y) \ + do { \ + mp_limb_t __mp_limb_t_swap__tmp = (x); \ + (x) = (y); \ + (y) = __mp_limb_t_swap__tmp; \ + } while (0) +#define MP_SIZE_T_SWAP(x, y) \ + do { \ + mp_size_t __mp_size_t_swap__tmp = (x); \ + (x) = (y); \ + (y) = __mp_size_t_swap__tmp; \ + } while (0) +#define MP_BITCNT_T_SWAP(x,y) \ + do { \ + mp_bitcnt_t __mp_bitcnt_t_swap__tmp = (x); \ + (x) = (y); \ + (y) = __mp_bitcnt_t_swap__tmp; \ + } while (0) +#define MP_PTR_SWAP(x, y) \ + do { \ + mp_ptr __mp_ptr_swap__tmp = (x); \ + (x) = (y); \ + (y) = __mp_ptr_swap__tmp; \ + } while (0) +#define MP_SRCPTR_SWAP(x, y) \ + do { \ + mp_srcptr __mp_srcptr_swap__tmp = (x); \ + (x) = (y); \ + (y) = __mp_srcptr_swap__tmp; \ + } while (0) + +#define MPN_PTR_SWAP(xp,xs, yp,ys) \ + do { \ + MP_PTR_SWAP (xp, yp); \ + MP_SIZE_T_SWAP (xs, ys); \ + } while(0) +#define MPN_SRCPTR_SWAP(xp,xs, yp,ys) \ + do { \ + MP_SRCPTR_SWAP (xp, yp); \ + MP_SIZE_T_SWAP (xs, ys); \ + } while(0) + +#define MPZ_PTR_SWAP(x, y) \ + do { \ + mpz_ptr __mpz_ptr_swap__tmp = (x); \ + (x) = (y); \ + (y) = __mpz_ptr_swap__tmp; \ + } while (0) +#define MPZ_SRCPTR_SWAP(x, y) \ + do { \ + mpz_srcptr __mpz_srcptr_swap__tmp = (x); \ + (x) = (y); \ + (y) = __mpz_srcptr_swap__tmp; \ + } while (0) + +const int mp_bits_per_limb = GMP_LIMB_BITS; + + +/* Memory allocation and other helper functions. */ +static void +gmp_die (const char *msg) +{ + /* + fprintf (stderr, "%s\n", msg); + abort(); + */ + IGRAPH_FATAL(msg); +} + +static void * +gmp_default_alloc (size_t size) +{ + void *p; + + assert (size > 0); + + p = malloc (size); + if (!p) + gmp_die("gmp_default_alloc: Virtual memory exhausted."); + + return p; +} + +static void * +gmp_default_realloc (void *old, size_t unused_old_size, size_t new_size) +{ + void * p; + + p = realloc (old, new_size); + + if (!p) + gmp_die("gmp_default_realloc: Virtual memory exhausted."); + + return p; +} + +static void +gmp_default_free (void *p, size_t unused_size) +{ + free (p); +} + +static void * (*gmp_allocate_func) (size_t) = gmp_default_alloc; +static void * (*gmp_reallocate_func) (void *, size_t, size_t) = gmp_default_realloc; +static void (*gmp_free_func) (void *, size_t) = gmp_default_free; + +void +mp_get_memory_functions (void *(**alloc_func) (size_t), + void *(**realloc_func) (void *, size_t, size_t), + void (**free_func) (void *, size_t)) +{ + if (alloc_func) + *alloc_func = gmp_allocate_func; + + if (realloc_func) + *realloc_func = gmp_reallocate_func; + + if (free_func) + *free_func = gmp_free_func; +} + +void +mp_set_memory_functions (void *(*alloc_func) (size_t), + void *(*realloc_func) (void *, size_t, size_t), + void (*free_func) (void *, size_t)) +{ + if (!alloc_func) + alloc_func = gmp_default_alloc; + if (!realloc_func) + realloc_func = gmp_default_realloc; + if (!free_func) + free_func = gmp_default_free; + + gmp_allocate_func = alloc_func; + gmp_reallocate_func = realloc_func; + gmp_free_func = free_func; +} + +#define gmp_xalloc(size) ((*gmp_allocate_func)((size))) +#define gmp_free(p) ((*gmp_free_func) ((p), 0)) + +static mp_ptr +gmp_xalloc_limbs (mp_size_t size) +{ + return (mp_ptr) gmp_xalloc (size * sizeof (mp_limb_t)); +} + +static mp_ptr +gmp_xrealloc_limbs (mp_ptr old, mp_size_t size) +{ + assert (size > 0); + return (mp_ptr) (*gmp_reallocate_func) (old, 0, size * sizeof (mp_limb_t)); +} + + +/* MPN interface */ + +void +mpn_copyi (mp_ptr d, mp_srcptr s, mp_size_t n) +{ + mp_size_t i; + for (i = 0; i < n; i++) + d[i] = s[i]; +} + +void +mpn_copyd (mp_ptr d, mp_srcptr s, mp_size_t n) +{ + while (--n >= 0) + d[n] = s[n]; +} + +int +mpn_cmp (mp_srcptr ap, mp_srcptr bp, mp_size_t n) +{ + while (--n >= 0) + { + if (ap[n] != bp[n]) + return ap[n] > bp[n] ? 1 : -1; + } + return 0; +} + +static int +mpn_cmp4 (mp_srcptr ap, mp_size_t an, mp_srcptr bp, mp_size_t bn) +{ + if (an != bn) + return an < bn ? -1 : 1; + else + return mpn_cmp (ap, bp, an); +} + +static mp_size_t +mpn_normalized_size (mp_srcptr xp, mp_size_t n) +{ + while (n > 0 && xp[n-1] == 0) + --n; + return n; +} + +int +mpn_zero_p(mp_srcptr rp, mp_size_t n) +{ + return mpn_normalized_size (rp, n) == 0; +} + +void +mpn_zero (mp_ptr rp, mp_size_t n) +{ + while (--n >= 0) + rp[n] = 0; +} + +mp_limb_t +mpn_add_1 (mp_ptr rp, mp_srcptr ap, mp_size_t n, mp_limb_t b) +{ + mp_size_t i; + + assert (n > 0); + i = 0; + do + { + mp_limb_t r = ap[i] + b; + /* Carry out */ + b = (r < b); + rp[i] = r; + } + while (++i < n); + + return b; +} + +mp_limb_t +mpn_add_n (mp_ptr rp, mp_srcptr ap, mp_srcptr bp, mp_size_t n) +{ + mp_size_t i; + mp_limb_t cy; + + for (i = 0, cy = 0; i < n; i++) + { + mp_limb_t a, b, r; + a = ap[i]; b = bp[i]; + r = a + cy; + cy = (r < cy); + r += b; + cy += (r < b); + rp[i] = r; + } + return cy; +} + +mp_limb_t +mpn_add (mp_ptr rp, mp_srcptr ap, mp_size_t an, mp_srcptr bp, mp_size_t bn) +{ + mp_limb_t cy; + + assert (an >= bn); + + cy = mpn_add_n (rp, ap, bp, bn); + if (an > bn) + cy = mpn_add_1 (rp + bn, ap + bn, an - bn, cy); + return cy; +} + +mp_limb_t +mpn_sub_1 (mp_ptr rp, mp_srcptr ap, mp_size_t n, mp_limb_t b) +{ + mp_size_t i; + + assert (n > 0); + + i = 0; + do + { + mp_limb_t a = ap[i]; + /* Carry out */ + mp_limb_t cy = a < b; + rp[i] = a - b; + b = cy; + } + while (++i < n); + + return b; +} + +mp_limb_t +mpn_sub_n (mp_ptr rp, mp_srcptr ap, mp_srcptr bp, mp_size_t n) +{ + mp_size_t i; + mp_limb_t cy; + + for (i = 0, cy = 0; i < n; i++) + { + mp_limb_t a, b; + a = ap[i]; b = bp[i]; + b += cy; + cy = (b < cy); + cy += (a < b); + rp[i] = a - b; + } + return cy; +} + +mp_limb_t +mpn_sub (mp_ptr rp, mp_srcptr ap, mp_size_t an, mp_srcptr bp, mp_size_t bn) +{ + mp_limb_t cy; + + assert (an >= bn); + + cy = mpn_sub_n (rp, ap, bp, bn); + if (an > bn) + cy = mpn_sub_1 (rp + bn, ap + bn, an - bn, cy); + return cy; +} + +mp_limb_t +mpn_mul_1 (mp_ptr rp, mp_srcptr up, mp_size_t n, mp_limb_t vl) +{ + mp_limb_t ul, cl, hpl, lpl; + + assert (n >= 1); + + cl = 0; + do + { + ul = *up++; + gmp_umul_ppmm (hpl, lpl, ul, vl); + + lpl += cl; + cl = (lpl < cl) + hpl; + + *rp++ = lpl; + } + while (--n != 0); + + return cl; +} + +mp_limb_t +mpn_addmul_1 (mp_ptr rp, mp_srcptr up, mp_size_t n, mp_limb_t vl) +{ + mp_limb_t ul, cl, hpl, lpl, rl; + + assert (n >= 1); + + cl = 0; + do + { + ul = *up++; + gmp_umul_ppmm (hpl, lpl, ul, vl); + + lpl += cl; + cl = (lpl < cl) + hpl; + + rl = *rp; + lpl = rl + lpl; + cl += lpl < rl; + *rp++ = lpl; + } + while (--n != 0); + + return cl; +} + +mp_limb_t +mpn_submul_1 (mp_ptr rp, mp_srcptr up, mp_size_t n, mp_limb_t vl) +{ + mp_limb_t ul, cl, hpl, lpl, rl; + + assert (n >= 1); + + cl = 0; + do + { + ul = *up++; + gmp_umul_ppmm (hpl, lpl, ul, vl); + + lpl += cl; + cl = (lpl < cl) + hpl; + + rl = *rp; + lpl = rl - lpl; + cl += lpl > rl; + *rp++ = lpl; + } + while (--n != 0); + + return cl; +} + +mp_limb_t +mpn_mul (mp_ptr rp, mp_srcptr up, mp_size_t un, mp_srcptr vp, mp_size_t vn) +{ + assert (un >= vn); + assert (vn >= 1); + assert (!GMP_MPN_OVERLAP_P(rp, un + vn, up, un)); + assert (!GMP_MPN_OVERLAP_P(rp, un + vn, vp, vn)); + + /* We first multiply by the low order limb. This result can be + stored, not added, to rp. We also avoid a loop for zeroing this + way. */ + + rp[un] = mpn_mul_1 (rp, up, un, vp[0]); + + /* Now accumulate the product of up[] and the next higher limb from + vp[]. */ + + while (--vn >= 1) + { + rp += 1, vp += 1; + rp[un] = mpn_addmul_1 (rp, up, un, vp[0]); + } + return rp[un]; +} + +void +mpn_mul_n (mp_ptr rp, mp_srcptr ap, mp_srcptr bp, mp_size_t n) +{ + mpn_mul (rp, ap, n, bp, n); +} + +void +mpn_sqr (mp_ptr rp, mp_srcptr ap, mp_size_t n) +{ + mpn_mul (rp, ap, n, ap, n); +} + +mp_limb_t +mpn_lshift (mp_ptr rp, mp_srcptr up, mp_size_t n, unsigned int cnt) +{ + mp_limb_t high_limb, low_limb; + unsigned int tnc; + mp_limb_t retval; + + assert (n >= 1); + assert (cnt >= 1); + assert (cnt < GMP_LIMB_BITS); + + up += n; + rp += n; + + tnc = GMP_LIMB_BITS - cnt; + low_limb = *--up; + retval = low_limb >> tnc; + high_limb = (low_limb << cnt); + + while (--n != 0) + { + low_limb = *--up; + *--rp = high_limb | (low_limb >> tnc); + high_limb = (low_limb << cnt); + } + *--rp = high_limb; + + return retval; +} + +mp_limb_t +mpn_rshift (mp_ptr rp, mp_srcptr up, mp_size_t n, unsigned int cnt) +{ + mp_limb_t high_limb, low_limb; + unsigned int tnc; + mp_limb_t retval; + + assert (n >= 1); + assert (cnt >= 1); + assert (cnt < GMP_LIMB_BITS); + + tnc = GMP_LIMB_BITS - cnt; + high_limb = *up++; + retval = (high_limb << tnc); + low_limb = high_limb >> cnt; + + while (--n != 0) + { + high_limb = *up++; + *rp++ = low_limb | (high_limb << tnc); + low_limb = high_limb >> cnt; + } + *rp = low_limb; + + return retval; +} + +static mp_bitcnt_t +mpn_common_scan (mp_limb_t limb, mp_size_t i, mp_srcptr up, mp_size_t un, + mp_limb_t ux) +{ + unsigned cnt; + + assert (ux == 0 || ux == GMP_LIMB_MAX); + assert (0 <= i && i <= un ); + + while (limb == 0) + { + i++; + if (i == un) + return (ux == 0 ? ~(mp_bitcnt_t) 0 : un * GMP_LIMB_BITS); + limb = ux ^ up[i]; + } + gmp_ctz (cnt, limb); + return (mp_bitcnt_t) i * GMP_LIMB_BITS + cnt; +} + +mp_bitcnt_t +mpn_scan1 (mp_srcptr ptr, mp_bitcnt_t bit) +{ + mp_size_t i; + i = bit / GMP_LIMB_BITS; + + return mpn_common_scan ( ptr[i] & (GMP_LIMB_MAX << (bit % GMP_LIMB_BITS)), + i, ptr, i, 0); +} + +mp_bitcnt_t +mpn_scan0 (mp_srcptr ptr, mp_bitcnt_t bit) +{ + mp_size_t i; + i = bit / GMP_LIMB_BITS; + + return mpn_common_scan (~ptr[i] & (GMP_LIMB_MAX << (bit % GMP_LIMB_BITS)), + i, ptr, i, GMP_LIMB_MAX); +} + +void +mpn_com (mp_ptr rp, mp_srcptr up, mp_size_t n) +{ + while (--n >= 0) + *rp++ = ~ *up++; +} + +mp_limb_t +mpn_neg (mp_ptr rp, mp_srcptr up, mp_size_t n) +{ + while (*up == 0) + { + *rp = 0; + if (!--n) + return 0; + ++up; ++rp; + } + *rp = - *up; + mpn_com (++rp, ++up, --n); + return 1; +} + + +/* MPN division interface. */ + +/* The 3/2 inverse is defined as + + m = floor( (B^3-1) / (B u1 + u0)) - B +*/ +mp_limb_t +mpn_invert_3by2 (mp_limb_t u1, mp_limb_t u0) +{ + mp_limb_t r, m; + + { + mp_limb_t p, ql; + unsigned ul, uh, qh; + + /* For notation, let b denote the half-limb base, so that B = b^2. + Split u1 = b uh + ul. */ + ul = u1 & GMP_LLIMB_MASK; + uh = u1 >> (GMP_LIMB_BITS / 2); + + /* Approximation of the high half of quotient. Differs from the 2/1 + inverse of the half limb uh, since we have already subtracted + u0. */ + qh = (u1 ^ GMP_LIMB_MAX) / uh; + + /* Adjust to get a half-limb 3/2 inverse, i.e., we want + + qh' = floor( (b^3 - 1) / u) - b = floor ((b^3 - b u - 1) / u + = floor( (b (~u) + b-1) / u), + + and the remainder + + r = b (~u) + b-1 - qh (b uh + ul) + = b (~u - qh uh) + b-1 - qh ul + + Subtraction of qh ul may underflow, which implies adjustments. + But by normalization, 2 u >= B > qh ul, so we need to adjust by + at most 2. + */ + + r = ((~u1 - (mp_limb_t) qh * uh) << (GMP_LIMB_BITS / 2)) | GMP_LLIMB_MASK; + + p = (mp_limb_t) qh * ul; + /* Adjustment steps taken from udiv_qrnnd_c */ + if (r < p) + { + qh--; + r += u1; + if (r >= u1) /* i.e. we didn't get carry when adding to r */ + if (r < p) + { + qh--; + r += u1; + } + } + r -= p; + + /* Low half of the quotient is + + ql = floor ( (b r + b-1) / u1). + + This is a 3/2 division (on half-limbs), for which qh is a + suitable inverse. */ + + p = (r >> (GMP_LIMB_BITS / 2)) * qh + r; + /* Unlike full-limb 3/2, we can add 1 without overflow. For this to + work, it is essential that ql is a full mp_limb_t. */ + ql = (p >> (GMP_LIMB_BITS / 2)) + 1; + + /* By the 3/2 trick, we don't need the high half limb. */ + r = (r << (GMP_LIMB_BITS / 2)) + GMP_LLIMB_MASK - ql * u1; + + if (r >= (GMP_LIMB_MAX & (p << (GMP_LIMB_BITS / 2)))) + { + ql--; + r += u1; + } + m = ((mp_limb_t) qh << (GMP_LIMB_BITS / 2)) + ql; + if (r >= u1) + { + m++; + r -= u1; + } + } + + /* Now m is the 2/1 inverse of u1. If u0 > 0, adjust it to become a + 3/2 inverse. */ + if (u0 > 0) + { + mp_limb_t th, tl; + r = ~r; + r += u0; + if (r < u0) + { + m--; + if (r >= u1) + { + m--; + r -= u1; + } + r -= u1; + } + gmp_umul_ppmm (th, tl, u0, m); + r += th; + if (r < th) + { + m--; + m -= ((r > u1) | ((r == u1) & (tl > u0))); + } + } + + return m; +} + +struct gmp_div_inverse +{ + /* Normalization shift count. */ + unsigned shift; + /* Normalized divisor (d0 unused for mpn_div_qr_1) */ + mp_limb_t d1, d0; + /* Inverse, for 2/1 or 3/2. */ + mp_limb_t di; +}; + +static void +mpn_div_qr_1_invert (struct gmp_div_inverse *inv, mp_limb_t d) +{ + unsigned shift; + + assert (d > 0); + gmp_clz (shift, d); + inv->shift = shift; + inv->d1 = d << shift; + inv->di = mpn_invert_limb (inv->d1); +} + +static void +mpn_div_qr_2_invert (struct gmp_div_inverse *inv, + mp_limb_t d1, mp_limb_t d0) +{ + unsigned shift; + + assert (d1 > 0); + gmp_clz (shift, d1); + inv->shift = shift; + if (shift > 0) + { + d1 = (d1 << shift) | (d0 >> (GMP_LIMB_BITS - shift)); + d0 <<= shift; + } + inv->d1 = d1; + inv->d0 = d0; + inv->di = mpn_invert_3by2 (d1, d0); +} + +static void +mpn_div_qr_invert (struct gmp_div_inverse *inv, + mp_srcptr dp, mp_size_t dn) +{ + assert (dn > 0); + + if (dn == 1) + mpn_div_qr_1_invert (inv, dp[0]); + else if (dn == 2) + mpn_div_qr_2_invert (inv, dp[1], dp[0]); + else + { + unsigned shift; + mp_limb_t d1, d0; + + d1 = dp[dn-1]; + d0 = dp[dn-2]; + assert (d1 > 0); + gmp_clz (shift, d1); + inv->shift = shift; + if (shift > 0) + { + d1 = (d1 << shift) | (d0 >> (GMP_LIMB_BITS - shift)); + d0 = (d0 << shift) | (dp[dn-3] >> (GMP_LIMB_BITS - shift)); + } + inv->d1 = d1; + inv->d0 = d0; + inv->di = mpn_invert_3by2 (d1, d0); + } +} + +/* Not matching current public gmp interface, rather corresponding to + the sbpi1_div_* functions. */ +static mp_limb_t +mpn_div_qr_1_preinv (mp_ptr qp, mp_srcptr np, mp_size_t nn, + const struct gmp_div_inverse *inv) +{ + mp_limb_t d, di; + mp_limb_t r; + mp_ptr tp = NULL; + + if (inv->shift > 0) + { + /* Shift, reusing qp area if possible. In-place shift if qp == np. */ + tp = qp ? qp : gmp_xalloc_limbs (nn); + r = mpn_lshift (tp, np, nn, inv->shift); + np = tp; + } + else + r = 0; + + d = inv->d1; + di = inv->di; + while (--nn >= 0) + { + mp_limb_t q; + + gmp_udiv_qrnnd_preinv (q, r, r, np[nn], d, di); + if (qp) + qp[nn] = q; + } + if ((inv->shift > 0) && (tp != qp)) + gmp_free (tp); + + return r >> inv->shift; +} + +static void +mpn_div_qr_2_preinv (mp_ptr qp, mp_ptr np, mp_size_t nn, + const struct gmp_div_inverse *inv) +{ + unsigned shift; + mp_size_t i; + mp_limb_t d1, d0, di, r1, r0; + + assert (nn >= 2); + shift = inv->shift; + d1 = inv->d1; + d0 = inv->d0; + di = inv->di; + + if (shift > 0) + r1 = mpn_lshift (np, np, nn, shift); + else + r1 = 0; + + r0 = np[nn - 1]; + + i = nn - 2; + do + { + mp_limb_t n0, q; + n0 = np[i]; + gmp_udiv_qr_3by2 (q, r1, r0, r1, r0, n0, d1, d0, di); + + if (qp) + qp[i] = q; + } + while (--i >= 0); + + if (shift > 0) + { + assert ((r0 & (GMP_LIMB_MAX >> (GMP_LIMB_BITS - shift))) == 0); + r0 = (r0 >> shift) | (r1 << (GMP_LIMB_BITS - shift)); + r1 >>= shift; + } + + np[1] = r1; + np[0] = r0; +} + +static void +mpn_div_qr_pi1 (mp_ptr qp, + mp_ptr np, mp_size_t nn, mp_limb_t n1, + mp_srcptr dp, mp_size_t dn, + mp_limb_t dinv) +{ + mp_size_t i; + + mp_limb_t d1, d0; + mp_limb_t cy, cy1; + mp_limb_t q; + + assert (dn > 2); + assert (nn >= dn); + + d1 = dp[dn - 1]; + d0 = dp[dn - 2]; + + assert ((d1 & GMP_LIMB_HIGHBIT) != 0); + /* Iteration variable is the index of the q limb. + * + * We divide + * by + */ + + i = nn - dn; + do + { + mp_limb_t n0 = np[dn-1+i]; + + if (n1 == d1 && n0 == d0) + { + q = GMP_LIMB_MAX; + mpn_submul_1 (np+i, dp, dn, q); + n1 = np[dn-1+i]; /* update n1, last loop's value will now be invalid */ + } + else + { + gmp_udiv_qr_3by2 (q, n1, n0, n1, n0, np[dn-2+i], d1, d0, dinv); + + cy = mpn_submul_1 (np + i, dp, dn-2, q); + + cy1 = n0 < cy; + n0 = n0 - cy; + cy = n1 < cy1; + n1 = n1 - cy1; + np[dn-2+i] = n0; + + if (cy != 0) + { + n1 += d1 + mpn_add_n (np + i, np + i, dp, dn - 1); + q--; + } + } + + if (qp) + qp[i] = q; + } + while (--i >= 0); + + np[dn - 1] = n1; +} + +static void +mpn_div_qr_preinv (mp_ptr qp, mp_ptr np, mp_size_t nn, + mp_srcptr dp, mp_size_t dn, + const struct gmp_div_inverse *inv) +{ + assert (dn > 0); + assert (nn >= dn); + + if (dn == 1) + np[0] = mpn_div_qr_1_preinv (qp, np, nn, inv); + else if (dn == 2) + mpn_div_qr_2_preinv (qp, np, nn, inv); + else + { + mp_limb_t nh; + unsigned shift; + + assert (inv->d1 == dp[dn-1]); + assert (inv->d0 == dp[dn-2]); + assert ((inv->d1 & GMP_LIMB_HIGHBIT) != 0); + + shift = inv->shift; + if (shift > 0) + nh = mpn_lshift (np, np, nn, shift); + else + nh = 0; + + mpn_div_qr_pi1 (qp, np, nn, nh, dp, dn, inv->di); + + if (shift > 0) + gmp_assert_nocarry (mpn_rshift (np, np, dn, shift)); + } +} + +static void +mpn_div_qr (mp_ptr qp, mp_ptr np, mp_size_t nn, mp_srcptr dp, mp_size_t dn) +{ + struct gmp_div_inverse inv; + mp_ptr tp = NULL; + + assert (dn > 0); + assert (nn >= dn); + + mpn_div_qr_invert (&inv, dp, dn); + if (dn > 2 && inv.shift > 0) + { + tp = gmp_xalloc_limbs (dn); + gmp_assert_nocarry (mpn_lshift (tp, dp, dn, inv.shift)); + dp = tp; + } + mpn_div_qr_preinv (qp, np, nn, dp, dn, &inv); + if (tp) + gmp_free (tp); +} + + +/* MPN base conversion. */ +static unsigned +mpn_base_power_of_two_p (unsigned b) +{ + switch (b) + { + case 2: return 1; + case 4: return 2; + case 8: return 3; + case 16: return 4; + case 32: return 5; + case 64: return 6; + case 128: return 7; + case 256: return 8; + default: return 0; + } +} + +struct mpn_base_info +{ + /* bb is the largest power of the base which fits in one limb, and + exp is the corresponding exponent. */ + unsigned exp; + mp_limb_t bb; +}; + +static void +mpn_get_base_info (struct mpn_base_info *info, mp_limb_t b) +{ + mp_limb_t m; + mp_limb_t p; + unsigned exp; + + m = GMP_LIMB_MAX / b; + for (exp = 1, p = b; p <= m; exp++) + p *= b; + + info->exp = exp; + info->bb = p; +} + +static mp_bitcnt_t +mpn_limb_size_in_base_2 (mp_limb_t u) +{ + unsigned shift; + + assert (u > 0); + gmp_clz (shift, u); + return GMP_LIMB_BITS - shift; +} + +static size_t +mpn_get_str_bits (unsigned char *sp, unsigned bits, mp_srcptr up, mp_size_t un) +{ + unsigned char mask; + size_t sn, j; + mp_size_t i; + unsigned shift; + + sn = ((un - 1) * GMP_LIMB_BITS + mpn_limb_size_in_base_2 (up[un-1]) + + bits - 1) / bits; + + mask = (1U << bits) - 1; + + for (i = 0, j = sn, shift = 0; j-- > 0;) + { + unsigned char digit = up[i] >> shift; + + shift += bits; + + if (shift >= GMP_LIMB_BITS && ++i < un) + { + shift -= GMP_LIMB_BITS; + digit |= up[i] << (bits - shift); + } + sp[j] = digit & mask; + } + return sn; +} + +/* We generate digits from the least significant end, and reverse at + the end. */ +static size_t +mpn_limb_get_str (unsigned char *sp, mp_limb_t w, + const struct gmp_div_inverse *binv) +{ + mp_size_t i; + for (i = 0; w > 0; i++) + { + mp_limb_t h, l, r; + + h = w >> (GMP_LIMB_BITS - binv->shift); + l = w << binv->shift; + + gmp_udiv_qrnnd_preinv (w, r, h, l, binv->d1, binv->di); + assert ((r & (GMP_LIMB_MAX >> (GMP_LIMB_BITS - binv->shift))) == 0); + r >>= binv->shift; + + sp[i] = r; + } + return i; +} + +static size_t +mpn_get_str_other (unsigned char *sp, + int base, const struct mpn_base_info *info, + mp_ptr up, mp_size_t un) +{ + struct gmp_div_inverse binv; + size_t sn; + size_t i; + + mpn_div_qr_1_invert (&binv, base); + + sn = 0; + + if (un > 1) + { + struct gmp_div_inverse bbinv; + mpn_div_qr_1_invert (&bbinv, info->bb); + + do + { + mp_limb_t w; + size_t done; + w = mpn_div_qr_1_preinv (up, up, un, &bbinv); + un -= (up[un-1] == 0); + done = mpn_limb_get_str (sp + sn, w, &binv); + + for (sn += done; done < info->exp; done++) + sp[sn++] = 0; + } + while (un > 1); + } + sn += mpn_limb_get_str (sp + sn, up[0], &binv); + + /* Reverse order */ + for (i = 0; 2*i + 1 < sn; i++) + { + unsigned char t = sp[i]; + sp[i] = sp[sn - i - 1]; + sp[sn - i - 1] = t; + } + + return sn; +} + +size_t +mpn_get_str (unsigned char *sp, int base, mp_ptr up, mp_size_t un) +{ + unsigned bits; + + assert (un > 0); + assert (up[un-1] > 0); + + bits = mpn_base_power_of_two_p (base); + if (bits) + return mpn_get_str_bits (sp, bits, up, un); + else + { + struct mpn_base_info info; + + mpn_get_base_info (&info, base); + return mpn_get_str_other (sp, base, &info, up, un); + } +} + +static mp_size_t +mpn_set_str_bits (mp_ptr rp, const unsigned char *sp, size_t sn, + unsigned bits) +{ + mp_size_t rn; + size_t j; + unsigned shift; + + for (j = sn, rn = 0, shift = 0; j-- > 0; ) + { + if (shift == 0) + { + rp[rn++] = sp[j]; + shift += bits; + } + else + { + rp[rn-1] |= (mp_limb_t) sp[j] << shift; + shift += bits; + if (shift >= GMP_LIMB_BITS) + { + shift -= GMP_LIMB_BITS; + if (shift > 0) + rp[rn++] = (mp_limb_t) sp[j] >> (bits - shift); + } + } + } + rn = mpn_normalized_size (rp, rn); + return rn; +} + +/* Result is usually normalized, except for all-zero input, in which + case a single zero limb is written at *RP, and 1 is returned. */ +static mp_size_t +mpn_set_str_other (mp_ptr rp, const unsigned char *sp, size_t sn, + mp_limb_t b, const struct mpn_base_info *info) +{ + mp_size_t rn; + mp_limb_t w; + unsigned k; + size_t j; + + assert (sn > 0); + + k = 1 + (sn - 1) % info->exp; + + j = 0; + w = sp[j++]; + while (--k != 0) + w = w * b + sp[j++]; + + rp[0] = w; + + for (rn = 1; j < sn;) + { + mp_limb_t cy; + + w = sp[j++]; + for (k = 1; k < info->exp; k++) + w = w * b + sp[j++]; + + cy = mpn_mul_1 (rp, rp, rn, info->bb); + cy += mpn_add_1 (rp, rp, rn, w); + if (cy > 0) + rp[rn++] = cy; + } + assert (j == sn); + + return rn; +} + +mp_size_t +mpn_set_str (mp_ptr rp, const unsigned char *sp, size_t sn, int base) +{ + unsigned bits; + + if (sn == 0) + return 0; + + bits = mpn_base_power_of_two_p (base); + if (bits) + return mpn_set_str_bits (rp, sp, sn, bits); + else + { + struct mpn_base_info info; + + mpn_get_base_info (&info, base); + return mpn_set_str_other (rp, sp, sn, base, &info); + } +} + + +/* MPZ interface */ +void +mpz_init (mpz_t r) +{ + static const mp_limb_t dummy_limb = GMP_LIMB_MAX & 0xc1a0; + + r->_mp_alloc = 0; + r->_mp_size = 0; + r->_mp_d = (mp_ptr) &dummy_limb; +} + +/* The utility of this function is a bit limited, since many functions + assigns the result variable using mpz_swap. */ +void +mpz_init2 (mpz_t r, mp_bitcnt_t bits) +{ + mp_size_t rn; + + bits -= (bits != 0); /* Round down, except if 0 */ + rn = 1 + bits / GMP_LIMB_BITS; + + r->_mp_alloc = rn; + r->_mp_size = 0; + r->_mp_d = gmp_xalloc_limbs (rn); +} + +void +mpz_clear (mpz_t r) +{ + if (r->_mp_alloc) + gmp_free (r->_mp_d); +} + +static mp_ptr +mpz_realloc (mpz_t r, mp_size_t size) +{ + size = GMP_MAX (size, 1); + + if (r->_mp_alloc) + r->_mp_d = gmp_xrealloc_limbs (r->_mp_d, size); + else + r->_mp_d = gmp_xalloc_limbs (size); + r->_mp_alloc = size; + + if (GMP_ABS (r->_mp_size) > size) + r->_mp_size = 0; + + return r->_mp_d; +} + +/* Realloc for an mpz_t WHAT if it has less than NEEDED limbs. */ +#define MPZ_REALLOC(z,n) ((n) > (z)->_mp_alloc \ + ? mpz_realloc(z,n) \ + : (z)->_mp_d) + +/* MPZ assignment and basic conversions. */ +void +mpz_set_si (mpz_t r, signed long int x) +{ + if (x >= 0) + mpz_set_ui (r, x); + else /* (x < 0) */ + if (GMP_LIMB_BITS < GMP_ULONG_BITS) + { + mpz_set_ui (r, GMP_NEG_CAST (unsigned long int, x)); + mpz_neg (r, r); + } + else + { + r->_mp_size = -1; + MPZ_REALLOC (r, 1)[0] = GMP_NEG_CAST (unsigned long int, x); + } +} + +void +mpz_set_ui (mpz_t r, unsigned long int x) +{ + if (x > 0) + { + r->_mp_size = 1; + MPZ_REALLOC (r, 1)[0] = x; + if (GMP_LIMB_BITS < GMP_ULONG_BITS) + { + int LOCAL_GMP_LIMB_BITS = GMP_LIMB_BITS; + while (x >>= LOCAL_GMP_LIMB_BITS) + { + ++ r->_mp_size; + MPZ_REALLOC (r, r->_mp_size)[r->_mp_size - 1] = x; + } + } + } + else + r->_mp_size = 0; +} + +void +mpz_set (mpz_t r, const mpz_t x) +{ + /* Allow the NOP r == x */ + if (r != x) + { + mp_size_t n; + mp_ptr rp; + + n = GMP_ABS (x->_mp_size); + rp = MPZ_REALLOC (r, n); + + mpn_copyi (rp, x->_mp_d, n); + r->_mp_size = x->_mp_size; + } +} + +void +mpz_init_set_si (mpz_t r, signed long int x) +{ + mpz_init (r); + mpz_set_si (r, x); +} + +void +mpz_init_set_ui (mpz_t r, unsigned long int x) +{ + mpz_init (r); + mpz_set_ui (r, x); +} + +void +mpz_init_set (mpz_t r, const mpz_t x) +{ + mpz_init (r); + mpz_set (r, x); +} + +int +mpz_fits_slong_p (const mpz_t u) +{ + return (LONG_MAX + LONG_MIN == 0 || mpz_cmp_ui (u, LONG_MAX) <= 0) && + mpz_cmpabs_ui (u, GMP_NEG_CAST (unsigned long int, LONG_MIN)) <= 0; +} + +static int +mpn_absfits_ulong_p (mp_srcptr up, mp_size_t un) +{ + int ulongsize = GMP_ULONG_BITS / GMP_LIMB_BITS; + mp_limb_t ulongrem = 0; + + if (GMP_ULONG_BITS % GMP_LIMB_BITS != 0) + ulongrem = (mp_limb_t) (ULONG_MAX >> GMP_LIMB_BITS * ulongsize) + 1; + + return un <= ulongsize || (up[ulongsize] < ulongrem && un == ulongsize + 1); +} + +int +mpz_fits_ulong_p (const mpz_t u) +{ + mp_size_t us = u->_mp_size; + + return us >= 0 && mpn_absfits_ulong_p (u->_mp_d, us); +} + +long int +mpz_get_si (const mpz_t u) +{ + unsigned long r = mpz_get_ui (u); + unsigned long c = -LONG_MAX - LONG_MIN; + + if (u->_mp_size < 0) + /* This expression is necessary to properly handle -LONG_MIN */ + return -(long) c - (long) ((r - c) & LONG_MAX); + else + return (long) (r & LONG_MAX); +} + +unsigned long int +mpz_get_ui (const mpz_t u) +{ + if (GMP_LIMB_BITS < GMP_ULONG_BITS) + { + int LOCAL_GMP_LIMB_BITS = GMP_LIMB_BITS; + unsigned long r = 0; + mp_size_t n = GMP_ABS (u->_mp_size); + n = GMP_MIN (n, 1 + (mp_size_t) (GMP_ULONG_BITS - 1) / GMP_LIMB_BITS); + while (--n >= 0) + r = (r << LOCAL_GMP_LIMB_BITS) + u->_mp_d[n]; + return r; + } + + return u->_mp_size == 0 ? 0 : u->_mp_d[0]; +} + +size_t +mpz_size (const mpz_t u) +{ + return GMP_ABS (u->_mp_size); +} + +mp_limb_t +mpz_getlimbn (const mpz_t u, mp_size_t n) +{ + if (n >= 0 && n < GMP_ABS (u->_mp_size)) + return u->_mp_d[n]; + else + return 0; +} + +void +mpz_realloc2 (mpz_t x, mp_bitcnt_t n) +{ + mpz_realloc (x, 1 + (n - (n != 0)) / GMP_LIMB_BITS); +} + +mp_srcptr +mpz_limbs_read (mpz_srcptr x) +{ + return x->_mp_d; +} + +mp_ptr +mpz_limbs_modify (mpz_t x, mp_size_t n) +{ + assert (n > 0); + return MPZ_REALLOC (x, n); +} + +mp_ptr +mpz_limbs_write (mpz_t x, mp_size_t n) +{ + return mpz_limbs_modify (x, n); +} + +void +mpz_limbs_finish (mpz_t x, mp_size_t xs) +{ + mp_size_t xn; + xn = mpn_normalized_size (x->_mp_d, GMP_ABS (xs)); + x->_mp_size = xs < 0 ? -xn : xn; +} + +static mpz_srcptr +mpz_roinit_normal_n (mpz_t x, mp_srcptr xp, mp_size_t xs) +{ + x->_mp_alloc = 0; + x->_mp_d = (mp_ptr) xp; + x->_mp_size = xs; + return x; +} + +mpz_srcptr +mpz_roinit_n (mpz_t x, mp_srcptr xp, mp_size_t xs) +{ + mpz_roinit_normal_n (x, xp, xs); + mpz_limbs_finish (x, xs); + return x; +} + + +/* Conversions and comparison to double. */ +void +mpz_set_d (mpz_t r, double x) +{ + int sign; + mp_ptr rp; + mp_size_t rn, i; + double B; + double Bi; + mp_limb_t f; + + /* x != x is true when x is a NaN, and x == x * 0.5 is true when x is + zero or infinity. */ + if (x != x || x == x * 0.5) + { + r->_mp_size = 0; + return; + } + + sign = x < 0.0 ; + if (sign) + x = - x; + + if (x < 1.0) + { + r->_mp_size = 0; + return; + } + B = 4.0 * (double) (GMP_LIMB_HIGHBIT >> 1); + Bi = 1.0 / B; + for (rn = 1; x >= B; rn++) + x *= Bi; + + rp = MPZ_REALLOC (r, rn); + + f = (mp_limb_t) x; + x -= f; + assert (x < 1.0); + i = rn-1; + rp[i] = f; + while (--i >= 0) + { + x = B * x; + f = (mp_limb_t) x; + x -= f; + assert (x < 1.0); + rp[i] = f; + } + + r->_mp_size = sign ? - rn : rn; +} + +void +mpz_init_set_d (mpz_t r, double x) +{ + mpz_init (r); + mpz_set_d (r, x); +} + +double +mpz_get_d (const mpz_t u) +{ + int m; + mp_limb_t l; + mp_size_t un; + double x; + double B = 4.0 * (double) (GMP_LIMB_HIGHBIT >> 1); + + un = GMP_ABS (u->_mp_size); + + if (un == 0) + return 0.0; + + l = u->_mp_d[--un]; + gmp_clz (m, l); + m = m + GMP_DBL_MANT_BITS - GMP_LIMB_BITS; + if (m < 0) + l &= GMP_LIMB_MAX << -m; + + for (x = l; --un >= 0;) + { + x = B*x; + if (m > 0) { + l = u->_mp_d[un]; + m -= GMP_LIMB_BITS; + if (m < 0) + l &= GMP_LIMB_MAX << -m; + x += l; + } + } + + if (u->_mp_size < 0) + x = -x; + + return x; +} + +int +mpz_cmpabs_d (const mpz_t x, double d) +{ + mp_size_t xn; + double B, Bi; + mp_size_t i; + + xn = x->_mp_size; + d = GMP_ABS (d); + + if (xn != 0) + { + xn = GMP_ABS (xn); + + B = 4.0 * (double) (GMP_LIMB_HIGHBIT >> 1); + Bi = 1.0 / B; + + /* Scale d so it can be compared with the top limb. */ + for (i = 1; i < xn; i++) + d *= Bi; + + if (d >= B) + return -1; + + /* Compare floor(d) to top limb, subtract and cancel when equal. */ + for (i = xn; i-- > 0;) + { + mp_limb_t f, xl; + + f = (mp_limb_t) d; + xl = x->_mp_d[i]; + if (xl > f) + return 1; + else if (xl < f) + return -1; + d = B * (d - f); + } + } + return - (d > 0.0); +} + +int +mpz_cmp_d (const mpz_t x, double d) +{ + if (x->_mp_size < 0) + { + if (d >= 0.0) + return -1; + else + return -mpz_cmpabs_d (x, d); + } + else + { + if (d < 0.0) + return 1; + else + return mpz_cmpabs_d (x, d); + } +} + + +/* MPZ comparisons and the like. */ +int +mpz_sgn (const mpz_t u) +{ + return GMP_CMP (u->_mp_size, 0); +} + +int +mpz_cmp_si (const mpz_t u, long v) +{ + mp_size_t usize = u->_mp_size; + + if (v >= 0) + return mpz_cmp_ui (u, v); + else if (usize >= 0) + return 1; + else + return - mpz_cmpabs_ui (u, GMP_NEG_CAST (unsigned long int, v)); +} + +int +mpz_cmp_ui (const mpz_t u, unsigned long v) +{ + mp_size_t usize = u->_mp_size; + + if (usize < 0) + return -1; + else + return mpz_cmpabs_ui (u, v); +} + +int +mpz_cmp (const mpz_t a, const mpz_t b) +{ + mp_size_t asize = a->_mp_size; + mp_size_t bsize = b->_mp_size; + + if (asize != bsize) + return (asize < bsize) ? -1 : 1; + else if (asize >= 0) + return mpn_cmp (a->_mp_d, b->_mp_d, asize); + else + return mpn_cmp (b->_mp_d, a->_mp_d, -asize); +} + +int +mpz_cmpabs_ui (const mpz_t u, unsigned long v) +{ + mp_size_t un = GMP_ABS (u->_mp_size); + + if (! mpn_absfits_ulong_p (u->_mp_d, un)) + return 1; + else + { + unsigned long uu = mpz_get_ui (u); + return GMP_CMP(uu, v); + } +} + +int +mpz_cmpabs (const mpz_t u, const mpz_t v) +{ + return mpn_cmp4 (u->_mp_d, GMP_ABS (u->_mp_size), + v->_mp_d, GMP_ABS (v->_mp_size)); +} + +void +mpz_abs (mpz_t r, const mpz_t u) +{ + mpz_set (r, u); + r->_mp_size = GMP_ABS (r->_mp_size); +} + +void +mpz_neg (mpz_t r, const mpz_t u) +{ + mpz_set (r, u); + r->_mp_size = -r->_mp_size; +} + +void +mpz_swap (mpz_t u, mpz_t v) +{ + MP_SIZE_T_SWAP (u->_mp_size, v->_mp_size); + MP_SIZE_T_SWAP (u->_mp_alloc, v->_mp_alloc); + MP_PTR_SWAP (u->_mp_d, v->_mp_d); +} + + +/* MPZ addition and subtraction */ + + +void +mpz_add_ui (mpz_t r, const mpz_t a, unsigned long b) +{ + mpz_t bb; + mpz_init_set_ui (bb, b); + mpz_add (r, a, bb); + mpz_clear (bb); +} + +void +mpz_sub_ui (mpz_t r, const mpz_t a, unsigned long b) +{ + mpz_ui_sub (r, b, a); + mpz_neg (r, r); +} + +void +mpz_ui_sub (mpz_t r, unsigned long a, const mpz_t b) +{ + mpz_neg (r, b); + mpz_add_ui (r, r, a); +} + +static mp_size_t +mpz_abs_add (mpz_t r, const mpz_t a, const mpz_t b) +{ + mp_size_t an = GMP_ABS (a->_mp_size); + mp_size_t bn = GMP_ABS (b->_mp_size); + mp_ptr rp; + mp_limb_t cy; + + if (an < bn) + { + MPZ_SRCPTR_SWAP (a, b); + MP_SIZE_T_SWAP (an, bn); + } + + rp = MPZ_REALLOC (r, an + 1); + cy = mpn_add (rp, a->_mp_d, an, b->_mp_d, bn); + + rp[an] = cy; + + return an + cy; +} + +static mp_size_t +mpz_abs_sub (mpz_t r, const mpz_t a, const mpz_t b) +{ + mp_size_t an = GMP_ABS (a->_mp_size); + mp_size_t bn = GMP_ABS (b->_mp_size); + int cmp; + mp_ptr rp; + + cmp = mpn_cmp4 (a->_mp_d, an, b->_mp_d, bn); + if (cmp > 0) + { + rp = MPZ_REALLOC (r, an); + gmp_assert_nocarry (mpn_sub (rp, a->_mp_d, an, b->_mp_d, bn)); + return mpn_normalized_size (rp, an); + } + else if (cmp < 0) + { + rp = MPZ_REALLOC (r, bn); + gmp_assert_nocarry (mpn_sub (rp, b->_mp_d, bn, a->_mp_d, an)); + return -mpn_normalized_size (rp, bn); + } + else + return 0; +} + +void +mpz_add (mpz_t r, const mpz_t a, const mpz_t b) +{ + mp_size_t rn; + + if ( (a->_mp_size ^ b->_mp_size) >= 0) + rn = mpz_abs_add (r, a, b); + else + rn = mpz_abs_sub (r, a, b); + + r->_mp_size = a->_mp_size >= 0 ? rn : - rn; +} + +void +mpz_sub (mpz_t r, const mpz_t a, const mpz_t b) +{ + mp_size_t rn; + + if ( (a->_mp_size ^ b->_mp_size) >= 0) + rn = mpz_abs_sub (r, a, b); + else + rn = mpz_abs_add (r, a, b); + + r->_mp_size = a->_mp_size >= 0 ? rn : - rn; +} + + +/* MPZ multiplication */ +void +mpz_mul_si (mpz_t r, const mpz_t u, long int v) +{ + if (v < 0) + { + mpz_mul_ui (r, u, GMP_NEG_CAST (unsigned long int, v)); + mpz_neg (r, r); + } + else + mpz_mul_ui (r, u, v); +} + +void +mpz_mul_ui (mpz_t r, const mpz_t u, unsigned long int v) +{ + mpz_t vv; + mpz_init_set_ui (vv, v); + mpz_mul (r, u, vv); + mpz_clear (vv); + return; +} + +void +mpz_mul (mpz_t r, const mpz_t u, const mpz_t v) +{ + int sign; + mp_size_t un, vn, rn; + mpz_t t; + mp_ptr tp; + + un = u->_mp_size; + vn = v->_mp_size; + + if (un == 0 || vn == 0) + { + r->_mp_size = 0; + return; + } + + sign = (un ^ vn) < 0; + + un = GMP_ABS (un); + vn = GMP_ABS (vn); + + mpz_init2 (t, (un + vn) * GMP_LIMB_BITS); + + tp = t->_mp_d; + if (un >= vn) + mpn_mul (tp, u->_mp_d, un, v->_mp_d, vn); + else + mpn_mul (tp, v->_mp_d, vn, u->_mp_d, un); + + rn = un + vn; + rn -= tp[rn-1] == 0; + + t->_mp_size = sign ? - rn : rn; + mpz_swap (r, t); + mpz_clear (t); +} + +void +mpz_mul_2exp (mpz_t r, const mpz_t u, mp_bitcnt_t bits) +{ + mp_size_t un, rn; + mp_size_t limbs; + unsigned shift; + mp_ptr rp; + + un = GMP_ABS (u->_mp_size); + if (un == 0) + { + r->_mp_size = 0; + return; + } + + limbs = bits / GMP_LIMB_BITS; + shift = bits % GMP_LIMB_BITS; + + rn = un + limbs + (shift > 0); + rp = MPZ_REALLOC (r, rn); + if (shift > 0) + { + mp_limb_t cy = mpn_lshift (rp + limbs, u->_mp_d, un, shift); + rp[rn-1] = cy; + rn -= (cy == 0); + } + else + mpn_copyd (rp + limbs, u->_mp_d, un); + + mpn_zero (rp, limbs); + + r->_mp_size = (u->_mp_size < 0) ? - rn : rn; +} + +void +mpz_addmul_ui (mpz_t r, const mpz_t u, unsigned long int v) +{ + mpz_t t; + mpz_init_set_ui (t, v); + mpz_mul (t, u, t); + mpz_add (r, r, t); + mpz_clear (t); +} + +void +mpz_submul_ui (mpz_t r, const mpz_t u, unsigned long int v) +{ + mpz_t t; + mpz_init_set_ui (t, v); + mpz_mul (t, u, t); + mpz_sub (r, r, t); + mpz_clear (t); +} + +void +mpz_addmul (mpz_t r, const mpz_t u, const mpz_t v) +{ + mpz_t t; + mpz_init (t); + mpz_mul (t, u, v); + mpz_add (r, r, t); + mpz_clear (t); +} + +void +mpz_submul (mpz_t r, const mpz_t u, const mpz_t v) +{ + mpz_t t; + mpz_init (t); + mpz_mul (t, u, v); + mpz_sub (r, r, t); + mpz_clear (t); +} + + +/* MPZ division */ +enum mpz_div_round_mode { GMP_DIV_FLOOR, GMP_DIV_CEIL, GMP_DIV_TRUNC }; + +/* Allows q or r to be zero. Returns 1 iff remainder is non-zero. */ +static int +mpz_div_qr (mpz_t q, mpz_t r, + const mpz_t n, const mpz_t d, enum mpz_div_round_mode mode) +{ + mp_size_t ns, ds, nn, dn, qs; + ns = n->_mp_size; + ds = d->_mp_size; + + if (ds == 0) + gmp_die("mpz_div_qr: Divide by zero."); + + if (ns == 0) + { + if (q) + q->_mp_size = 0; + if (r) + r->_mp_size = 0; + return 0; + } + + nn = GMP_ABS (ns); + dn = GMP_ABS (ds); + + qs = ds ^ ns; + + if (nn < dn) + { + if (mode == GMP_DIV_CEIL && qs >= 0) + { + /* q = 1, r = n - d */ + if (r) + mpz_sub (r, n, d); + if (q) + mpz_set_ui (q, 1); + } + else if (mode == GMP_DIV_FLOOR && qs < 0) + { + /* q = -1, r = n + d */ + if (r) + mpz_add (r, n, d); + if (q) + mpz_set_si (q, -1); + } + else + { + /* q = 0, r = d */ + if (r) + mpz_set (r, n); + if (q) + q->_mp_size = 0; + } + return 1; + } + else + { + mp_ptr np, qp; + mp_size_t qn, rn; + mpz_t tq, tr; + + mpz_init_set (tr, n); + np = tr->_mp_d; + + qn = nn - dn + 1; + + if (q) + { + mpz_init2 (tq, qn * GMP_LIMB_BITS); + qp = tq->_mp_d; + } + else + qp = NULL; + + mpn_div_qr (qp, np, nn, d->_mp_d, dn); + + if (qp) + { + qn -= (qp[qn-1] == 0); + + tq->_mp_size = qs < 0 ? -qn : qn; + } + rn = mpn_normalized_size (np, dn); + tr->_mp_size = ns < 0 ? - rn : rn; + + if (mode == GMP_DIV_FLOOR && qs < 0 && rn != 0) + { + if (q) + mpz_sub_ui (tq, tq, 1); + if (r) + mpz_add (tr, tr, d); + } + else if (mode == GMP_DIV_CEIL && qs >= 0 && rn != 0) + { + if (q) + mpz_add_ui (tq, tq, 1); + if (r) + mpz_sub (tr, tr, d); + } + + if (q) + { + mpz_swap (tq, q); + mpz_clear (tq); + } + if (r) + mpz_swap (tr, r); + + mpz_clear (tr); + + return rn != 0; + } +} + +void +mpz_cdiv_qr (mpz_t q, mpz_t r, const mpz_t n, const mpz_t d) +{ + mpz_div_qr (q, r, n, d, GMP_DIV_CEIL); +} + +void +mpz_fdiv_qr (mpz_t q, mpz_t r, const mpz_t n, const mpz_t d) +{ + mpz_div_qr (q, r, n, d, GMP_DIV_FLOOR); +} + +void +mpz_tdiv_qr (mpz_t q, mpz_t r, const mpz_t n, const mpz_t d) +{ + mpz_div_qr (q, r, n, d, GMP_DIV_TRUNC); +} + +void +mpz_cdiv_q (mpz_t q, const mpz_t n, const mpz_t d) +{ + mpz_div_qr (q, NULL, n, d, GMP_DIV_CEIL); +} + +void +mpz_fdiv_q (mpz_t q, const mpz_t n, const mpz_t d) +{ + mpz_div_qr (q, NULL, n, d, GMP_DIV_FLOOR); +} + +void +mpz_tdiv_q (mpz_t q, const mpz_t n, const mpz_t d) +{ + mpz_div_qr (q, NULL, n, d, GMP_DIV_TRUNC); +} + +void +mpz_cdiv_r (mpz_t r, const mpz_t n, const mpz_t d) +{ + mpz_div_qr (NULL, r, n, d, GMP_DIV_CEIL); +} + +void +mpz_fdiv_r (mpz_t r, const mpz_t n, const mpz_t d) +{ + mpz_div_qr (NULL, r, n, d, GMP_DIV_FLOOR); +} + +void +mpz_tdiv_r (mpz_t r, const mpz_t n, const mpz_t d) +{ + mpz_div_qr (NULL, r, n, d, GMP_DIV_TRUNC); +} + +void +mpz_mod (mpz_t r, const mpz_t n, const mpz_t d) +{ + mpz_div_qr (NULL, r, n, d, d->_mp_size >= 0 ? GMP_DIV_FLOOR : GMP_DIV_CEIL); +} + +static void +mpz_div_q_2exp (mpz_t q, const mpz_t u, mp_bitcnt_t bit_index, + enum mpz_div_round_mode mode) +{ + mp_size_t un, qn; + mp_size_t limb_cnt; + mp_ptr qp; + int adjust; + + un = u->_mp_size; + if (un == 0) + { + q->_mp_size = 0; + return; + } + limb_cnt = bit_index / GMP_LIMB_BITS; + qn = GMP_ABS (un) - limb_cnt; + bit_index %= GMP_LIMB_BITS; + + if (mode == ((un > 0) ? GMP_DIV_CEIL : GMP_DIV_FLOOR)) /* un != 0 here. */ + /* Note: Below, the final indexing at limb_cnt is valid because at + that point we have qn > 0. */ + adjust = (qn <= 0 + || !mpn_zero_p (u->_mp_d, limb_cnt) + || (u->_mp_d[limb_cnt] + & (((mp_limb_t) 1 << bit_index) - 1))); + else + adjust = 0; + + if (qn <= 0) + qn = 0; + else + { + qp = MPZ_REALLOC (q, qn); + + if (bit_index != 0) + { + mpn_rshift (qp, u->_mp_d + limb_cnt, qn, bit_index); + qn -= qp[qn - 1] == 0; + } + else + { + mpn_copyi (qp, u->_mp_d + limb_cnt, qn); + } + } + + q->_mp_size = qn; + + if (adjust) + mpz_add_ui (q, q, 1); + if (un < 0) + mpz_neg (q, q); +} + +static void +mpz_div_r_2exp (mpz_t r, const mpz_t u, mp_bitcnt_t bit_index, + enum mpz_div_round_mode mode) +{ + mp_size_t us, un, rn; + mp_ptr rp; + mp_limb_t mask; + + us = u->_mp_size; + if (us == 0 || bit_index == 0) + { + r->_mp_size = 0; + return; + } + rn = (bit_index + GMP_LIMB_BITS - 1) / GMP_LIMB_BITS; + assert (rn > 0); + + rp = MPZ_REALLOC (r, rn); + un = GMP_ABS (us); + + mask = GMP_LIMB_MAX >> (rn * GMP_LIMB_BITS - bit_index); + + if (rn > un) + { + /* Quotient (with truncation) is zero, and remainder is + non-zero */ + if (mode == ((us > 0) ? GMP_DIV_CEIL : GMP_DIV_FLOOR)) /* us != 0 here. */ + { + /* Have to negate and sign extend. */ + mp_size_t i; + + gmp_assert_nocarry (! mpn_neg (rp, u->_mp_d, un)); + for (i = un; i < rn - 1; i++) + rp[i] = GMP_LIMB_MAX; + + rp[rn-1] = mask; + us = -us; + } + else + { + /* Just copy */ + if (r != u) + mpn_copyi (rp, u->_mp_d, un); + + rn = un; + } + } + else + { + if (r != u) + mpn_copyi (rp, u->_mp_d, rn - 1); + + rp[rn-1] = u->_mp_d[rn-1] & mask; + + if (mode == ((us > 0) ? GMP_DIV_CEIL : GMP_DIV_FLOOR)) /* us != 0 here. */ + { + /* If r != 0, compute 2^{bit_count} - r. */ + mpn_neg (rp, rp, rn); + + rp[rn-1] &= mask; + + /* us is not used for anything else, so we can modify it + here to indicate flipped sign. */ + us = -us; + } + } + rn = mpn_normalized_size (rp, rn); + r->_mp_size = us < 0 ? -rn : rn; +} + +void +mpz_cdiv_q_2exp (mpz_t r, const mpz_t u, mp_bitcnt_t cnt) +{ + mpz_div_q_2exp (r, u, cnt, GMP_DIV_CEIL); +} + +void +mpz_fdiv_q_2exp (mpz_t r, const mpz_t u, mp_bitcnt_t cnt) +{ + mpz_div_q_2exp (r, u, cnt, GMP_DIV_FLOOR); +} + +void +mpz_tdiv_q_2exp (mpz_t r, const mpz_t u, mp_bitcnt_t cnt) +{ + mpz_div_q_2exp (r, u, cnt, GMP_DIV_TRUNC); +} + +void +mpz_cdiv_r_2exp (mpz_t r, const mpz_t u, mp_bitcnt_t cnt) +{ + mpz_div_r_2exp (r, u, cnt, GMP_DIV_CEIL); +} + +void +mpz_fdiv_r_2exp (mpz_t r, const mpz_t u, mp_bitcnt_t cnt) +{ + mpz_div_r_2exp (r, u, cnt, GMP_DIV_FLOOR); +} + +void +mpz_tdiv_r_2exp (mpz_t r, const mpz_t u, mp_bitcnt_t cnt) +{ + mpz_div_r_2exp (r, u, cnt, GMP_DIV_TRUNC); +} + +void +mpz_divexact (mpz_t q, const mpz_t n, const mpz_t d) +{ + gmp_assert_nocarry (mpz_div_qr (q, NULL, n, d, GMP_DIV_TRUNC)); +} + +int +mpz_divisible_p (const mpz_t n, const mpz_t d) +{ + return mpz_div_qr (NULL, NULL, n, d, GMP_DIV_TRUNC) == 0; +} + +int +mpz_congruent_p (const mpz_t a, const mpz_t b, const mpz_t m) +{ + mpz_t t; + int res; + + /* a == b (mod 0) iff a == b */ + if (mpz_sgn (m) == 0) + return (mpz_cmp (a, b) == 0); + + mpz_init (t); + mpz_sub (t, a, b); + res = mpz_divisible_p (t, m); + mpz_clear (t); + + return res; +} + +static unsigned long +mpz_div_qr_ui (mpz_t q, mpz_t r, + const mpz_t n, unsigned long d, enum mpz_div_round_mode mode) +{ + unsigned long ret; + mpz_t rr, dd; + + mpz_init (rr); + mpz_init_set_ui (dd, d); + mpz_div_qr (q, rr, n, dd, mode); + mpz_clear (dd); + ret = mpz_get_ui (rr); + + if (r) + mpz_swap (r, rr); + mpz_clear (rr); + + return ret; +} + +unsigned long +mpz_cdiv_qr_ui (mpz_t q, mpz_t r, const mpz_t n, unsigned long d) +{ + return mpz_div_qr_ui (q, r, n, d, GMP_DIV_CEIL); +} + +unsigned long +mpz_fdiv_qr_ui (mpz_t q, mpz_t r, const mpz_t n, unsigned long d) +{ + return mpz_div_qr_ui (q, r, n, d, GMP_DIV_FLOOR); +} + +unsigned long +mpz_tdiv_qr_ui (mpz_t q, mpz_t r, const mpz_t n, unsigned long d) +{ + return mpz_div_qr_ui (q, r, n, d, GMP_DIV_TRUNC); +} + +unsigned long +mpz_cdiv_q_ui (mpz_t q, const mpz_t n, unsigned long d) +{ + return mpz_div_qr_ui (q, NULL, n, d, GMP_DIV_CEIL); +} + +unsigned long +mpz_fdiv_q_ui (mpz_t q, const mpz_t n, unsigned long d) +{ + return mpz_div_qr_ui (q, NULL, n, d, GMP_DIV_FLOOR); +} + +unsigned long +mpz_tdiv_q_ui (mpz_t q, const mpz_t n, unsigned long d) +{ + return mpz_div_qr_ui (q, NULL, n, d, GMP_DIV_TRUNC); +} + +unsigned long +mpz_cdiv_r_ui (mpz_t r, const mpz_t n, unsigned long d) +{ + return mpz_div_qr_ui (NULL, r, n, d, GMP_DIV_CEIL); +} +unsigned long +mpz_fdiv_r_ui (mpz_t r, const mpz_t n, unsigned long d) +{ + return mpz_div_qr_ui (NULL, r, n, d, GMP_DIV_FLOOR); +} +unsigned long +mpz_tdiv_r_ui (mpz_t r, const mpz_t n, unsigned long d) +{ + return mpz_div_qr_ui (NULL, r, n, d, GMP_DIV_TRUNC); +} + +unsigned long +mpz_cdiv_ui (const mpz_t n, unsigned long d) +{ + return mpz_div_qr_ui (NULL, NULL, n, d, GMP_DIV_CEIL); +} + +unsigned long +mpz_fdiv_ui (const mpz_t n, unsigned long d) +{ + return mpz_div_qr_ui (NULL, NULL, n, d, GMP_DIV_FLOOR); +} + +unsigned long +mpz_tdiv_ui (const mpz_t n, unsigned long d) +{ + return mpz_div_qr_ui (NULL, NULL, n, d, GMP_DIV_TRUNC); +} + +unsigned long +mpz_mod_ui (mpz_t r, const mpz_t n, unsigned long d) +{ + return mpz_div_qr_ui (NULL, r, n, d, GMP_DIV_FLOOR); +} + +void +mpz_divexact_ui (mpz_t q, const mpz_t n, unsigned long d) +{ + gmp_assert_nocarry (mpz_div_qr_ui (q, NULL, n, d, GMP_DIV_TRUNC)); +} + +int +mpz_divisible_ui_p (const mpz_t n, unsigned long d) +{ + return mpz_div_qr_ui (NULL, NULL, n, d, GMP_DIV_TRUNC) == 0; +} + + +/* GCD */ +static mp_limb_t +mpn_gcd_11 (mp_limb_t u, mp_limb_t v) +{ + unsigned shift; + + assert ( (u | v) > 0); + + if (u == 0) + return v; + else if (v == 0) + return u; + + gmp_ctz (shift, u | v); + + u >>= shift; + v >>= shift; + + if ( (u & 1) == 0) + MP_LIMB_T_SWAP (u, v); + + while ( (v & 1) == 0) + v >>= 1; + + while (u != v) + { + if (u > v) + { + u -= v; + do + u >>= 1; + while ( (u & 1) == 0); + } + else + { + v -= u; + do + v >>= 1; + while ( (v & 1) == 0); + } + } + return u << shift; +} + +unsigned long +mpz_gcd_ui (mpz_t g, const mpz_t u, unsigned long v) +{ + mpz_t t; + mpz_init_set_ui(t, v); + mpz_gcd (t, u, t); + if (v > 0) + v = mpz_get_ui (t); + + if (g) + mpz_swap (t, g); + + mpz_clear (t); + + return v; +} + +static mp_bitcnt_t +mpz_make_odd (mpz_t r) +{ + mp_bitcnt_t shift; + + assert (r->_mp_size > 0); + /* Count trailing zeros, equivalent to mpn_scan1, because we know that there is a 1 */ + shift = mpn_common_scan (r->_mp_d[0], 0, r->_mp_d, 0, 0); + mpz_tdiv_q_2exp (r, r, shift); + + return shift; +} + +void +mpz_gcd (mpz_t g, const mpz_t u, const mpz_t v) +{ + mpz_t tu, tv; + mp_bitcnt_t uz, vz, gz; + + if (u->_mp_size == 0) + { + mpz_abs (g, v); + return; + } + if (v->_mp_size == 0) + { + mpz_abs (g, u); + return; + } + + mpz_init (tu); + mpz_init (tv); + + mpz_abs (tu, u); + uz = mpz_make_odd (tu); + mpz_abs (tv, v); + vz = mpz_make_odd (tv); + gz = GMP_MIN (uz, vz); + + if (tu->_mp_size < tv->_mp_size) + mpz_swap (tu, tv); + + mpz_tdiv_r (tu, tu, tv); + if (tu->_mp_size == 0) + { + mpz_swap (g, tv); + } + else + for (;;) + { + int c; + + mpz_make_odd (tu); + c = mpz_cmp (tu, tv); + if (c == 0) + { + mpz_swap (g, tu); + break; + } + if (c < 0) + mpz_swap (tu, tv); + + if (tv->_mp_size == 1) + { + mp_limb_t vl = tv->_mp_d[0]; + mp_limb_t ul = mpz_tdiv_ui (tu, vl); + mpz_set_ui (g, mpn_gcd_11 (ul, vl)); + break; + } + mpz_sub (tu, tu, tv); + } + mpz_clear (tu); + mpz_clear (tv); + mpz_mul_2exp (g, g, gz); +} + +void +mpz_gcdext (mpz_t g, mpz_t s, mpz_t t, const mpz_t u, const mpz_t v) +{ + mpz_t tu, tv, s0, s1, t0, t1; + mp_bitcnt_t uz, vz, gz; + mp_bitcnt_t power; + + if (u->_mp_size == 0) + { + /* g = 0 u + sgn(v) v */ + signed long sign = mpz_sgn (v); + mpz_abs (g, v); + if (s) + s->_mp_size = 0; + if (t) + mpz_set_si (t, sign); + return; + } + + if (v->_mp_size == 0) + { + /* g = sgn(u) u + 0 v */ + signed long sign = mpz_sgn (u); + mpz_abs (g, u); + if (s) + mpz_set_si (s, sign); + if (t) + t->_mp_size = 0; + return; + } + + mpz_init (tu); + mpz_init (tv); + mpz_init (s0); + mpz_init (s1); + mpz_init (t0); + mpz_init (t1); + + mpz_abs (tu, u); + uz = mpz_make_odd (tu); + mpz_abs (tv, v); + vz = mpz_make_odd (tv); + gz = GMP_MIN (uz, vz); + + uz -= gz; + vz -= gz; + + /* Cofactors corresponding to odd gcd. gz handled later. */ + if (tu->_mp_size < tv->_mp_size) + { + mpz_swap (tu, tv); + MPZ_SRCPTR_SWAP (u, v); + MPZ_PTR_SWAP (s, t); + MP_BITCNT_T_SWAP (uz, vz); + } + + /* Maintain + * + * u = t0 tu + t1 tv + * v = s0 tu + s1 tv + * + * where u and v denote the inputs with common factors of two + * eliminated, and det (s0, t0; s1, t1) = 2^p. Then + * + * 2^p tu = s1 u - t1 v + * 2^p tv = -s0 u + t0 v + */ + + /* After initial division, tu = q tv + tu', we have + * + * u = 2^uz (tu' + q tv) + * v = 2^vz tv + * + * or + * + * t0 = 2^uz, t1 = 2^uz q + * s0 = 0, s1 = 2^vz + */ + + mpz_setbit (t0, uz); + mpz_tdiv_qr (t1, tu, tu, tv); + mpz_mul_2exp (t1, t1, uz); + + mpz_setbit (s1, vz); + power = uz + vz; + + if (tu->_mp_size > 0) + { + mp_bitcnt_t shift; + shift = mpz_make_odd (tu); + mpz_mul_2exp (t0, t0, shift); + mpz_mul_2exp (s0, s0, shift); + power += shift; + + for (;;) + { + int c; + c = mpz_cmp (tu, tv); + if (c == 0) + break; + + if (c < 0) + { + /* tv = tv' + tu + * + * u = t0 tu + t1 (tv' + tu) = (t0 + t1) tu + t1 tv' + * v = s0 tu + s1 (tv' + tu) = (s0 + s1) tu + s1 tv' */ + + mpz_sub (tv, tv, tu); + mpz_add (t0, t0, t1); + mpz_add (s0, s0, s1); + + shift = mpz_make_odd (tv); + mpz_mul_2exp (t1, t1, shift); + mpz_mul_2exp (s1, s1, shift); + } + else + { + mpz_sub (tu, tu, tv); + mpz_add (t1, t0, t1); + mpz_add (s1, s0, s1); + + shift = mpz_make_odd (tu); + mpz_mul_2exp (t0, t0, shift); + mpz_mul_2exp (s0, s0, shift); + } + power += shift; + } + } + + /* Now tv = odd part of gcd, and -s0 and t0 are corresponding + cofactors. */ + + mpz_mul_2exp (tv, tv, gz); + mpz_neg (s0, s0); + + /* 2^p g = s0 u + t0 v. Eliminate one factor of two at a time. To + adjust cofactors, we need u / g and v / g */ + + mpz_divexact (s1, v, tv); + mpz_abs (s1, s1); + mpz_divexact (t1, u, tv); + mpz_abs (t1, t1); + + while (power-- > 0) + { + /* s0 u + t0 v = (s0 - v/g) u - (t0 + u/g) v */ + if (mpz_odd_p (s0) || mpz_odd_p (t0)) + { + mpz_sub (s0, s0, s1); + mpz_add (t0, t0, t1); + } + assert (mpz_even_p (t0) && mpz_even_p (s0)); + mpz_tdiv_q_2exp (s0, s0, 1); + mpz_tdiv_q_2exp (t0, t0, 1); + } + + /* Arrange so that |s| < |u| / 2g */ + mpz_add (s1, s0, s1); + if (mpz_cmpabs (s0, s1) > 0) + { + mpz_swap (s0, s1); + mpz_sub (t0, t0, t1); + } + if (u->_mp_size < 0) + mpz_neg (s0, s0); + if (v->_mp_size < 0) + mpz_neg (t0, t0); + + mpz_swap (g, tv); + if (s) + mpz_swap (s, s0); + if (t) + mpz_swap (t, t0); + + mpz_clear (tu); + mpz_clear (tv); + mpz_clear (s0); + mpz_clear (s1); + mpz_clear (t0); + mpz_clear (t1); +} + +void +mpz_lcm (mpz_t r, const mpz_t u, const mpz_t v) +{ + mpz_t g; + + if (u->_mp_size == 0 || v->_mp_size == 0) + { + r->_mp_size = 0; + return; + } + + mpz_init (g); + + mpz_gcd (g, u, v); + mpz_divexact (g, u, g); + mpz_mul (r, g, v); + + mpz_clear (g); + mpz_abs (r, r); +} + +void +mpz_lcm_ui (mpz_t r, const mpz_t u, unsigned long v) +{ + if (v == 0 || u->_mp_size == 0) + { + r->_mp_size = 0; + return; + } + + v /= mpz_gcd_ui (NULL, u, v); + mpz_mul_ui (r, u, v); + + mpz_abs (r, r); +} + +int +mpz_invert (mpz_t r, const mpz_t u, const mpz_t m) +{ + mpz_t g, tr; + int invertible; + + if (u->_mp_size == 0 || mpz_cmpabs_ui (m, 1) <= 0) + return 0; + + mpz_init (g); + mpz_init (tr); + + mpz_gcdext (g, tr, NULL, u, m); + invertible = (mpz_cmp_ui (g, 1) == 0); + + if (invertible) + { + if (tr->_mp_size < 0) + { + if (m->_mp_size >= 0) + mpz_add (tr, tr, m); + else + mpz_sub (tr, tr, m); + } + mpz_swap (r, tr); + } + + mpz_clear (g); + mpz_clear (tr); + return invertible; +} + + +/* Higher level operations (sqrt, pow and root) */ + +void +mpz_pow_ui (mpz_t r, const mpz_t b, unsigned long e) +{ + unsigned long bit; + mpz_t tr; + mpz_init_set_ui (tr, 1); + + bit = GMP_ULONG_HIGHBIT; + do + { + mpz_mul (tr, tr, tr); + if (e & bit) + mpz_mul (tr, tr, b); + bit >>= 1; + } + while (bit > 0); + + mpz_swap (r, tr); + mpz_clear (tr); +} + +void +mpz_ui_pow_ui (mpz_t r, unsigned long blimb, unsigned long e) +{ + mpz_t b; + + mpz_init_set_ui (b, blimb); + mpz_pow_ui (r, b, e); + mpz_clear (b); +} + +void +mpz_powm (mpz_t r, const mpz_t b, const mpz_t e, const mpz_t m) +{ + mpz_t tr; + mpz_t base; + mp_size_t en, mn; + mp_srcptr mp; + struct gmp_div_inverse minv; + unsigned shift; + mp_ptr tp = NULL; + + en = GMP_ABS (e->_mp_size); + mn = GMP_ABS (m->_mp_size); + if (mn == 0) + gmp_die ("mpz_powm: Zero modulo."); + + if (en == 0) + { + mpz_set_ui (r, 1); + return; + } + + mp = m->_mp_d; + mpn_div_qr_invert (&minv, mp, mn); + shift = minv.shift; + + if (shift > 0) + { + /* To avoid shifts, we do all our reductions, except the final + one, using a *normalized* m. */ + minv.shift = 0; + + tp = gmp_xalloc_limbs (mn); + gmp_assert_nocarry (mpn_lshift (tp, mp, mn, shift)); + mp = tp; + } + + mpz_init (base); + + if (e->_mp_size < 0) + { + if (!mpz_invert (base, b, m)) + gmp_die ("mpz_powm: Negative exponent and non-invertible base."); + } + else + { + mp_size_t bn; + mpz_abs (base, b); + + bn = base->_mp_size; + if (bn >= mn) + { + mpn_div_qr_preinv (NULL, base->_mp_d, base->_mp_size, mp, mn, &minv); + bn = mn; + } + + /* We have reduced the absolute value. Now take care of the + sign. Note that we get zero represented non-canonically as + m. */ + if (b->_mp_size < 0) + { + mp_ptr bp = MPZ_REALLOC (base, mn); + gmp_assert_nocarry (mpn_sub (bp, mp, mn, bp, bn)); + bn = mn; + } + base->_mp_size = mpn_normalized_size (base->_mp_d, bn); + } + mpz_init_set_ui (tr, 1); + + while (--en >= 0) + { + mp_limb_t w = e->_mp_d[en]; + mp_limb_t bit; + + bit = GMP_LIMB_HIGHBIT; + do + { + mpz_mul (tr, tr, tr); + if (w & bit) + mpz_mul (tr, tr, base); + if (tr->_mp_size > mn) + { + mpn_div_qr_preinv (NULL, tr->_mp_d, tr->_mp_size, mp, mn, &minv); + tr->_mp_size = mpn_normalized_size (tr->_mp_d, mn); + } + bit >>= 1; + } + while (bit > 0); + } + + /* Final reduction */ + if (tr->_mp_size >= mn) + { + minv.shift = shift; + mpn_div_qr_preinv (NULL, tr->_mp_d, tr->_mp_size, mp, mn, &minv); + tr->_mp_size = mpn_normalized_size (tr->_mp_d, mn); + } + if (tp) + gmp_free (tp); + + mpz_swap (r, tr); + mpz_clear (tr); + mpz_clear (base); +} + +void +mpz_powm_ui (mpz_t r, const mpz_t b, unsigned long elimb, const mpz_t m) +{ + mpz_t e; + + mpz_init_set_ui (e, elimb); + mpz_powm (r, b, e, m); + mpz_clear (e); +} + +/* x=trunc(y^(1/z)), r=y-x^z */ +void +mpz_rootrem (mpz_t x, mpz_t r, const mpz_t y, unsigned long z) +{ + int sgn; + mpz_t t, u; + + sgn = y->_mp_size < 0; + if ((~z & sgn) != 0) + gmp_die ("mpz_rootrem: Negative argument, with even root."); + if (z == 0) + gmp_die ("mpz_rootrem: Zeroth root."); + + if (mpz_cmpabs_ui (y, 1) <= 0) { + if (x) + mpz_set (x, y); + if (r) + r->_mp_size = 0; + return; + } + + mpz_init (u); + mpz_init (t); + mpz_setbit (t, mpz_sizeinbase (y, 2) / z + 1); + + if (z == 2) /* simplify sqrt loop: z-1 == 1 */ + do { + mpz_swap (u, t); /* u = x */ + mpz_tdiv_q (t, y, u); /* t = y/x */ + mpz_add (t, t, u); /* t = y/x + x */ + mpz_tdiv_q_2exp (t, t, 1); /* x'= (y/x + x)/2 */ + } while (mpz_cmpabs (t, u) < 0); /* |x'| < |x| */ + else /* z != 2 */ { + mpz_t v; + + mpz_init (v); + if (sgn) + mpz_neg (t, t); + + do { + mpz_swap (u, t); /* u = x */ + mpz_pow_ui (t, u, z - 1); /* t = x^(z-1) */ + mpz_tdiv_q (t, y, t); /* t = y/x^(z-1) */ + mpz_mul_ui (v, u, z - 1); /* v = x*(z-1) */ + mpz_add (t, t, v); /* t = y/x^(z-1) + x*(z-1) */ + mpz_tdiv_q_ui (t, t, z); /* x'=(y/x^(z-1) + x*(z-1))/z */ + } while (mpz_cmpabs (t, u) < 0); /* |x'| < |x| */ + + mpz_clear (v); + } + + if (r) { + mpz_pow_ui (t, u, z); + mpz_sub (r, y, t); + } + if (x) + mpz_swap (x, u); + mpz_clear (u); + mpz_clear (t); +} + +int +mpz_root (mpz_t x, const mpz_t y, unsigned long z) +{ + int res; + mpz_t r; + + mpz_init (r); + mpz_rootrem (x, r, y, z); + res = r->_mp_size == 0; + mpz_clear (r); + + return res; +} + +/* Compute s = floor(sqrt(u)) and r = u - s^2. Allows r == NULL */ +void +mpz_sqrtrem (mpz_t s, mpz_t r, const mpz_t u) +{ + mpz_rootrem (s, r, u, 2); +} + +void +mpz_sqrt (mpz_t s, const mpz_t u) +{ + mpz_rootrem (s, NULL, u, 2); +} + +int +mpz_perfect_square_p (const mpz_t u) +{ + if (u->_mp_size <= 0) + return (u->_mp_size == 0); + else + return mpz_root (NULL, u, 2); +} + +int +mpn_perfect_square_p (mp_srcptr p, mp_size_t n) +{ + mpz_t t; + + assert (n > 0); + assert (p [n-1] != 0); + return mpz_root (NULL, mpz_roinit_normal_n (t, p, n), 2); +} + +mp_size_t +mpn_sqrtrem (mp_ptr sp, mp_ptr rp, mp_srcptr p, mp_size_t n) +{ + mpz_t s, r, u; + mp_size_t res; + + assert (n > 0); + assert (p [n-1] != 0); + + mpz_init (r); + mpz_init (s); + mpz_rootrem (s, r, mpz_roinit_normal_n (u, p, n), 2); + + assert (s->_mp_size == (n+1)/2); + mpn_copyd (sp, s->_mp_d, s->_mp_size); + mpz_clear (s); + res = r->_mp_size; + if (rp) + mpn_copyd (rp, r->_mp_d, res); + mpz_clear (r); + return res; +} + +/* Combinatorics */ + +void +mpz_mfac_uiui (mpz_t x, unsigned long n, unsigned long m) +{ + mpz_set_ui (x, n + (n == 0)); + if (m + 1 < 2) return; + while (n > m + 1) + mpz_mul_ui (x, x, n -= m); +} + +void +mpz_2fac_ui (mpz_t x, unsigned long n) +{ + mpz_mfac_uiui (x, n, 2); +} + +void +mpz_fac_ui (mpz_t x, unsigned long n) +{ + mpz_mfac_uiui (x, n, 1); +} + +void +mpz_bin_uiui (mpz_t r, unsigned long n, unsigned long k) +{ + mpz_t t; + + mpz_set_ui (r, k <= n); + + if (k > (n >> 1)) + k = (k <= n) ? n - k : 0; + + mpz_init (t); + mpz_fac_ui (t, k); + + for (; k > 0; --k) + mpz_mul_ui (r, r, n--); + + mpz_divexact (r, r, t); + mpz_clear (t); +} + + +/* Primality testing */ + +/* Computes Kronecker (a/b) with odd b, a!=0 and GCD(a,b) = 1 */ +/* Adapted from JACOBI_BASE_METHOD==4 in mpn/generic/jacbase.c */ +static int +gmp_jacobi_coprime (mp_limb_t a, mp_limb_t b) +{ + int c, bit = 0; + + assert (b & 1); + assert (a != 0); + /* assert (mpn_gcd_11 (a, b) == 1); */ + + /* Below, we represent a and b shifted right so that the least + significant one bit is implicit. */ + b >>= 1; + + gmp_ctz(c, a); + a >>= 1; + + do + { + a >>= c; + /* (2/b) = -1 if b = 3 or 5 mod 8 */ + bit ^= c & (b ^ (b >> 1)); + if (a < b) + { + bit ^= a & b; + a = b - a; + b -= a; + } + else + { + a -= b; + assert (a != 0); + } + + gmp_ctz(c, a); + ++c; + } + while (b > 0); + + return bit & 1 ? -1 : 1; +} + +static void +gmp_lucas_step_k_2k (mpz_t V, mpz_t Qk, const mpz_t n) +{ + mpz_mod (Qk, Qk, n); + /* V_{2k} <- V_k ^ 2 - 2Q^k */ + mpz_mul (V, V, V); + mpz_submul_ui (V, Qk, 2); + mpz_tdiv_r (V, V, n); + /* Q^{2k} = (Q^k)^2 */ + mpz_mul (Qk, Qk, Qk); +} + +/* Computes V_k, Q^k (mod n) for the Lucas' sequence */ +/* with P=1, Q=Q; k = (n>>b0)|1. */ +/* Requires an odd n > 4; b0 > 0; -2*Q must not overflow a long */ +/* Returns (U_k == 0) and sets V=V_k and Qk=Q^k. */ +static int +gmp_lucas_mod (mpz_t V, mpz_t Qk, long Q, + mp_bitcnt_t b0, const mpz_t n) +{ + mp_bitcnt_t bs; + mpz_t U; + int res; + + assert (b0 > 0); + assert (Q <= - (LONG_MIN / 2)); + assert (Q >= - (LONG_MAX / 2)); + assert (mpz_cmp_ui (n, 4) > 0); + assert (mpz_odd_p (n)); + + mpz_init_set_ui (U, 1); /* U1 = 1 */ + mpz_set_ui (V, 1); /* V1 = 1 */ + mpz_set_si (Qk, Q); + + for (bs = mpz_sizeinbase (n, 2) - 1; --bs >= b0;) + { + /* U_{2k} <- U_k * V_k */ + mpz_mul (U, U, V); + /* V_{2k} <- V_k ^ 2 - 2Q^k */ + /* Q^{2k} = (Q^k)^2 */ + gmp_lucas_step_k_2k (V, Qk, n); + + /* A step k->k+1 is performed if the bit in $n$ is 1 */ + /* mpz_tstbit(n,bs) or the bit is 0 in $n$ but */ + /* should be 1 in $n+1$ (bs == b0) */ + if (b0 == bs || mpz_tstbit (n, bs)) + { + /* Q^{k+1} <- Q^k * Q */ + mpz_mul_si (Qk, Qk, Q); + /* U_{k+1} <- (U_k + V_k) / 2 */ + mpz_swap (U, V); /* Keep in V the old value of U_k */ + mpz_add (U, U, V); + /* We have to compute U/2, so we need an even value, */ + /* equivalent (mod n) */ + if (mpz_odd_p (U)) + mpz_add (U, U, n); + mpz_tdiv_q_2exp (U, U, 1); + /* V_{k+1} <-(D*U_k + V_k) / 2 = + U_{k+1} + (D-1)/2*U_k = U_{k+1} - 2Q*U_k */ + mpz_mul_si (V, V, -2*Q); + mpz_add (V, U, V); + mpz_tdiv_r (V, V, n); + } + mpz_tdiv_r (U, U, n); + } + + res = U->_mp_size == 0; + mpz_clear (U); + return res; +} + +/* Performs strong Lucas' test on x, with parameters suggested */ +/* for the BPSW test. Qk is only passed to recycle a variable. */ +/* Requires GCD (x,6) = 1.*/ +static int +gmp_stronglucas (const mpz_t x, mpz_t Qk) +{ + mp_bitcnt_t b0; + mpz_t V, n; + mp_limb_t maxD, D; /* The absolute value is stored. */ + long Q; + mp_limb_t tl; + + /* Test on the absolute value. */ + mpz_roinit_normal_n (n, x->_mp_d, GMP_ABS (x->_mp_size)); + + assert (mpz_odd_p (n)); + /* assert (mpz_gcd_ui (NULL, n, 6) == 1); */ + if (mpz_root (Qk, n, 2)) + return 0; /* A square is composite. */ + + /* Check Ds up to square root (in case, n is prime) + or avoid overflows */ + maxD = (Qk->_mp_size == 1) ? Qk->_mp_d [0] - 1 : GMP_LIMB_MAX; + + D = 3; + /* Search a D such that (D/n) = -1 in the sequence 5,-7,9,-11,.. */ + /* For those Ds we have (D/n) = (n/|D|) */ + do + { + if (D >= maxD) + return 1 + (D != GMP_LIMB_MAX); /* (1 + ! ~ D) */ + D += 2; + tl = mpz_tdiv_ui (n, D); + if (tl == 0) + return 0; + } + while (gmp_jacobi_coprime (tl, D) == 1); + + mpz_init (V); + + /* n-(D/n) = n+1 = d*2^{b0}, with d = (n>>b0) | 1 */ + b0 = mpz_scan0 (n, 0); + + /* D= P^2 - 4Q; P = 1; Q = (1-D)/4 */ + Q = (D & 2) ? (long) (D >> 2) + 1 : -(long) (D >> 2); + + if (! gmp_lucas_mod (V, Qk, Q, b0, n)) /* If Ud != 0 */ + while (V->_mp_size != 0 && --b0 != 0) /* while Vk != 0 */ + /* V <- V ^ 2 - 2Q^k */ + /* Q^{2k} = (Q^k)^2 */ + gmp_lucas_step_k_2k (V, Qk, n); + + mpz_clear (V); + return (b0 != 0); +} + +static int +gmp_millerrabin (const mpz_t n, const mpz_t nm1, mpz_t y, + const mpz_t q, mp_bitcnt_t k) +{ + assert (k > 0); + + /* Caller must initialize y to the base. */ + mpz_powm (y, y, q, n); + + if (mpz_cmp_ui (y, 1) == 0 || mpz_cmp (y, nm1) == 0) + return 1; + + while (--k > 0) + { + mpz_powm_ui (y, y, 2, n); + if (mpz_cmp (y, nm1) == 0) + return 1; + /* y == 1 means that the previous y was a non-trivial square root + of 1 (mod n). y == 0 means that n is a power of the base. + In either case, n is not prime. */ + if (mpz_cmp_ui (y, 1) <= 0) + return 0; + } + return 0; +} + +/* This product is 0xc0cfd797, and fits in 32 bits. */ +#define GMP_PRIME_PRODUCT \ + (3UL*5UL*7UL*11UL*13UL*17UL*19UL*23UL*29UL) + +/* Bit (p+1)/2 is set, for each odd prime <= 61 */ +#define GMP_PRIME_MASK 0xc96996dcUL + +int +mpz_probab_prime_p (const mpz_t n, int reps) +{ + mpz_t nm1; + mpz_t q; + mpz_t y; + mp_bitcnt_t k; + int is_prime; + int j; + + /* Note that we use the absolute value of n only, for compatibility + with the real GMP. */ + if (mpz_even_p (n)) + return (mpz_cmpabs_ui (n, 2) == 0) ? 2 : 0; + + /* Above test excludes n == 0 */ + assert (n->_mp_size != 0); + + if (mpz_cmpabs_ui (n, 64) < 0) + return (GMP_PRIME_MASK >> (n->_mp_d[0] >> 1)) & 2; + + if (mpz_gcd_ui (NULL, n, GMP_PRIME_PRODUCT) != 1) + return 0; + + /* All prime factors are >= 31. */ + if (mpz_cmpabs_ui (n, 31*31) < 0) + return 2; + + mpz_init (nm1); + mpz_init (q); + + /* Find q and k, where q is odd and n = 1 + 2**k * q. */ + mpz_abs (nm1, n); + nm1->_mp_d[0] -= 1; + k = mpz_scan1 (nm1, 0); + mpz_tdiv_q_2exp (q, nm1, k); + + /* BPSW test */ + mpz_init_set_ui (y, 2); + is_prime = gmp_millerrabin (n, nm1, y, q, k) && gmp_stronglucas (n, y); + reps -= 24; /* skip the first 24 repetitions */ + + /* Use Miller-Rabin, with a deterministic sequence of bases, a[j] = + j^2 + j + 41 using Euler's polynomial. We potentially stop early, + if a[j] >= n - 1. Since n >= 31*31, this can happen only if reps > + 30 (a[30] == 971 > 31*31 == 961). */ + + for (j = 0; is_prime & (j < reps); j++) + { + mpz_set_ui (y, (unsigned long) j*j+j+41); + if (mpz_cmp (y, nm1) >= 0) + { + /* Don't try any further bases. This "early" break does not affect + the result for any reasonable reps value (<=5000 was tested) */ + assert (j >= 30); + break; + } + is_prime = gmp_millerrabin (n, nm1, y, q, k); + } + mpz_clear (nm1); + mpz_clear (q); + mpz_clear (y); + + return is_prime; +} + + +/* Logical operations and bit manipulation. */ + +/* Numbers are treated as if represented in two's complement (and + infinitely sign extended). For a negative values we get the two's + complement from -x = ~x + 1, where ~ is bitwise complement. + Negation transforms + + xxxx10...0 + + into + + yyyy10...0 + + where yyyy is the bitwise complement of xxxx. So least significant + bits, up to and including the first one bit, are unchanged, and + the more significant bits are all complemented. + + To change a bit from zero to one in a negative number, subtract the + corresponding power of two from the absolute value. This can never + underflow. To change a bit from one to zero, add the corresponding + power of two, and this might overflow. E.g., if x = -001111, the + two's complement is 110001. Clearing the least significant bit, we + get two's complement 110000, and -010000. */ + +int +mpz_tstbit (const mpz_t d, mp_bitcnt_t bit_index) +{ + mp_size_t limb_index; + unsigned shift; + mp_size_t ds; + mp_size_t dn; + mp_limb_t w; + int bit; + + ds = d->_mp_size; + dn = GMP_ABS (ds); + limb_index = bit_index / GMP_LIMB_BITS; + if (limb_index >= dn) + return ds < 0; + + shift = bit_index % GMP_LIMB_BITS; + w = d->_mp_d[limb_index]; + bit = (w >> shift) & 1; + + if (ds < 0) + { + /* d < 0. Check if any of the bits below is set: If so, our bit + must be complemented. */ + if (shift > 0 && (mp_limb_t) (w << (GMP_LIMB_BITS - shift)) > 0) + return bit ^ 1; + while (--limb_index >= 0) + if (d->_mp_d[limb_index] > 0) + return bit ^ 1; + } + return bit; +} + +static void +mpz_abs_add_bit (mpz_t d, mp_bitcnt_t bit_index) +{ + mp_size_t dn, limb_index; + mp_limb_t bit; + mp_ptr dp; + + dn = GMP_ABS (d->_mp_size); + + limb_index = bit_index / GMP_LIMB_BITS; + bit = (mp_limb_t) 1 << (bit_index % GMP_LIMB_BITS); + + if (limb_index >= dn) + { + mp_size_t i; + /* The bit should be set outside of the end of the number. + We have to increase the size of the number. */ + dp = MPZ_REALLOC (d, limb_index + 1); + + dp[limb_index] = bit; + for (i = dn; i < limb_index; i++) + dp[i] = 0; + dn = limb_index + 1; + } + else + { + mp_limb_t cy; + + dp = d->_mp_d; + + cy = mpn_add_1 (dp + limb_index, dp + limb_index, dn - limb_index, bit); + if (cy > 0) + { + dp = MPZ_REALLOC (d, dn + 1); + dp[dn++] = cy; + } + } + + d->_mp_size = (d->_mp_size < 0) ? - dn : dn; +} + +static void +mpz_abs_sub_bit (mpz_t d, mp_bitcnt_t bit_index) +{ + mp_size_t dn, limb_index; + mp_ptr dp; + mp_limb_t bit; + + dn = GMP_ABS (d->_mp_size); + dp = d->_mp_d; + + limb_index = bit_index / GMP_LIMB_BITS; + bit = (mp_limb_t) 1 << (bit_index % GMP_LIMB_BITS); + + assert (limb_index < dn); + + gmp_assert_nocarry (mpn_sub_1 (dp + limb_index, dp + limb_index, + dn - limb_index, bit)); + dn = mpn_normalized_size (dp, dn); + d->_mp_size = (d->_mp_size < 0) ? - dn : dn; +} + +void +mpz_setbit (mpz_t d, mp_bitcnt_t bit_index) +{ + if (!mpz_tstbit (d, bit_index)) + { + if (d->_mp_size >= 0) + mpz_abs_add_bit (d, bit_index); + else + mpz_abs_sub_bit (d, bit_index); + } +} + +void +mpz_clrbit (mpz_t d, mp_bitcnt_t bit_index) +{ + if (mpz_tstbit (d, bit_index)) + { + if (d->_mp_size >= 0) + mpz_abs_sub_bit (d, bit_index); + else + mpz_abs_add_bit (d, bit_index); + } +} + +void +mpz_combit (mpz_t d, mp_bitcnt_t bit_index) +{ + if (mpz_tstbit (d, bit_index) ^ (d->_mp_size < 0)) + mpz_abs_sub_bit (d, bit_index); + else + mpz_abs_add_bit (d, bit_index); +} + +void +mpz_com (mpz_t r, const mpz_t u) +{ + mpz_add_ui (r, u, 1); + mpz_neg (r, r); +} + +void +mpz_and (mpz_t r, const mpz_t u, const mpz_t v) +{ + mp_size_t un, vn, rn, i; + mp_ptr up, vp, rp; + + mp_limb_t ux, vx, rx; + mp_limb_t uc, vc, rc; + mp_limb_t ul, vl, rl; + + un = GMP_ABS (u->_mp_size); + vn = GMP_ABS (v->_mp_size); + if (un < vn) + { + MPZ_SRCPTR_SWAP (u, v); + MP_SIZE_T_SWAP (un, vn); + } + if (vn == 0) + { + r->_mp_size = 0; + return; + } + + uc = u->_mp_size < 0; + vc = v->_mp_size < 0; + rc = uc & vc; + + ux = -uc; + vx = -vc; + rx = -rc; + + /* If the smaller input is positive, higher limbs don't matter. */ + rn = vx ? un : vn; + + rp = MPZ_REALLOC (r, rn + (mp_size_t) rc); + + up = u->_mp_d; + vp = v->_mp_d; + + i = 0; + do + { + ul = (up[i] ^ ux) + uc; + uc = ul < uc; + + vl = (vp[i] ^ vx) + vc; + vc = vl < vc; + + rl = ( (ul & vl) ^ rx) + rc; + rc = rl < rc; + rp[i] = rl; + } + while (++i < vn); + assert (vc == 0); + + for (; i < rn; i++) + { + ul = (up[i] ^ ux) + uc; + uc = ul < uc; + + rl = ( (ul & vx) ^ rx) + rc; + rc = rl < rc; + rp[i] = rl; + } + if (rc) + rp[rn++] = rc; + else + rn = mpn_normalized_size (rp, rn); + + r->_mp_size = rx ? -rn : rn; +} + +void +mpz_ior (mpz_t r, const mpz_t u, const mpz_t v) +{ + mp_size_t un, vn, rn, i; + mp_ptr up, vp, rp; + + mp_limb_t ux, vx, rx; + mp_limb_t uc, vc, rc; + mp_limb_t ul, vl, rl; + + un = GMP_ABS (u->_mp_size); + vn = GMP_ABS (v->_mp_size); + if (un < vn) + { + MPZ_SRCPTR_SWAP (u, v); + MP_SIZE_T_SWAP (un, vn); + } + if (vn == 0) + { + mpz_set (r, u); + return; + } + + uc = u->_mp_size < 0; + vc = v->_mp_size < 0; + rc = uc | vc; + + ux = -uc; + vx = -vc; + rx = -rc; + + /* If the smaller input is negative, by sign extension higher limbs + don't matter. */ + rn = vx ? vn : un; + + rp = MPZ_REALLOC (r, rn + (mp_size_t) rc); + + up = u->_mp_d; + vp = v->_mp_d; + + i = 0; + do + { + ul = (up[i] ^ ux) + uc; + uc = ul < uc; + + vl = (vp[i] ^ vx) + vc; + vc = vl < vc; + + rl = ( (ul | vl) ^ rx) + rc; + rc = rl < rc; + rp[i] = rl; + } + while (++i < vn); + assert (vc == 0); + + for (; i < rn; i++) + { + ul = (up[i] ^ ux) + uc; + uc = ul < uc; + + rl = ( (ul | vx) ^ rx) + rc; + rc = rl < rc; + rp[i] = rl; + } + if (rc) + rp[rn++] = rc; + else + rn = mpn_normalized_size (rp, rn); + + r->_mp_size = rx ? -rn : rn; +} + +void +mpz_xor (mpz_t r, const mpz_t u, const mpz_t v) +{ + mp_size_t un, vn, i; + mp_ptr up, vp, rp; + + mp_limb_t ux, vx, rx; + mp_limb_t uc, vc, rc; + mp_limb_t ul, vl, rl; + + un = GMP_ABS (u->_mp_size); + vn = GMP_ABS (v->_mp_size); + if (un < vn) + { + MPZ_SRCPTR_SWAP (u, v); + MP_SIZE_T_SWAP (un, vn); + } + if (vn == 0) + { + mpz_set (r, u); + return; + } + + uc = u->_mp_size < 0; + vc = v->_mp_size < 0; + rc = uc ^ vc; + + ux = -uc; + vx = -vc; + rx = -rc; + + rp = MPZ_REALLOC (r, un + (mp_size_t) rc); + + up = u->_mp_d; + vp = v->_mp_d; + + i = 0; + do + { + ul = (up[i] ^ ux) + uc; + uc = ul < uc; + + vl = (vp[i] ^ vx) + vc; + vc = vl < vc; + + rl = (ul ^ vl ^ rx) + rc; + rc = rl < rc; + rp[i] = rl; + } + while (++i < vn); + assert (vc == 0); + + for (; i < un; i++) + { + ul = (up[i] ^ ux) + uc; + uc = ul < uc; + + rl = (ul ^ ux) + rc; + rc = rl < rc; + rp[i] = rl; + } + if (rc) + rp[un++] = rc; + else + un = mpn_normalized_size (rp, un); + + r->_mp_size = rx ? -un : un; +} + +static unsigned +gmp_popcount_limb (mp_limb_t x) +{ + unsigned c; + + /* Do 16 bits at a time, to avoid limb-sized constants. */ + int LOCAL_SHIFT_BITS = 16; + for (c = 0; x > 0;) + { + unsigned w = x - ((x >> 1) & 0x5555); + w = ((w >> 2) & 0x3333) + (w & 0x3333); + w = (w >> 4) + w; + w = ((w >> 8) & 0x000f) + (w & 0x000f); + c += w; + if (GMP_LIMB_BITS > LOCAL_SHIFT_BITS) + x >>= LOCAL_SHIFT_BITS; + else + x = 0; + } + return c; +} + +mp_bitcnt_t +mpn_popcount (mp_srcptr p, mp_size_t n) +{ + mp_size_t i; + mp_bitcnt_t c; + + for (c = 0, i = 0; i < n; i++) + c += gmp_popcount_limb (p[i]); + + return c; +} + +mp_bitcnt_t +mpz_popcount (const mpz_t u) +{ + mp_size_t un; + + un = u->_mp_size; + + if (un < 0) + return ~(mp_bitcnt_t) 0; + + return mpn_popcount (u->_mp_d, un); +} + +mp_bitcnt_t +mpz_hamdist (const mpz_t u, const mpz_t v) +{ + mp_size_t un, vn, i; + mp_limb_t uc, vc, ul, vl, comp; + mp_srcptr up, vp; + mp_bitcnt_t c; + + un = u->_mp_size; + vn = v->_mp_size; + + if ( (un ^ vn) < 0) + return ~(mp_bitcnt_t) 0; + + comp = - (uc = vc = (un < 0)); + if (uc) + { + assert (vn < 0); + un = -un; + vn = -vn; + } + + up = u->_mp_d; + vp = v->_mp_d; + + if (un < vn) + MPN_SRCPTR_SWAP (up, un, vp, vn); + + for (i = 0, c = 0; i < vn; i++) + { + ul = (up[i] ^ comp) + uc; + uc = ul < uc; + + vl = (vp[i] ^ comp) + vc; + vc = vl < vc; + + c += gmp_popcount_limb (ul ^ vl); + } + assert (vc == 0); + + for (; i < un; i++) + { + ul = (up[i] ^ comp) + uc; + uc = ul < uc; + + c += gmp_popcount_limb (ul ^ comp); + } + + return c; +} + +mp_bitcnt_t +mpz_scan1 (const mpz_t u, mp_bitcnt_t starting_bit) +{ + mp_ptr up; + mp_size_t us, un, i; + mp_limb_t limb, ux; + + us = u->_mp_size; + un = GMP_ABS (us); + i = starting_bit / GMP_LIMB_BITS; + + /* Past the end there's no 1 bits for u>=0, or an immediate 1 bit + for u<0. Notice this test picks up any u==0 too. */ + if (i >= un) + return (us >= 0 ? ~(mp_bitcnt_t) 0 : starting_bit); + + up = u->_mp_d; + ux = 0; + limb = up[i]; + + if (starting_bit != 0) + { + if (us < 0) + { + ux = mpn_zero_p (up, i); + limb = ~ limb + ux; + ux = - (mp_limb_t) (limb >= ux); + } + + /* Mask to 0 all bits before starting_bit, thus ignoring them. */ + limb &= GMP_LIMB_MAX << (starting_bit % GMP_LIMB_BITS); + } + + return mpn_common_scan (limb, i, up, un, ux); +} + +mp_bitcnt_t +mpz_scan0 (const mpz_t u, mp_bitcnt_t starting_bit) +{ + mp_ptr up; + mp_size_t us, un, i; + mp_limb_t limb, ux; + + us = u->_mp_size; + ux = - (mp_limb_t) (us >= 0); + un = GMP_ABS (us); + i = starting_bit / GMP_LIMB_BITS; + + /* When past end, there's an immediate 0 bit for u>=0, or no 0 bits for + u<0. Notice this test picks up all cases of u==0 too. */ + if (i >= un) + return (ux ? starting_bit : ~(mp_bitcnt_t) 0); + + up = u->_mp_d; + limb = up[i] ^ ux; + + if (ux == 0) + limb -= mpn_zero_p (up, i); /* limb = ~(~limb + zero_p) */ + + /* Mask all bits before starting_bit, thus ignoring them. */ + limb &= GMP_LIMB_MAX << (starting_bit % GMP_LIMB_BITS); + + return mpn_common_scan (limb, i, up, un, ux); +} + + +/* MPZ base conversion. */ + +size_t +mpz_sizeinbase (const mpz_t u, int base) +{ + mp_size_t un; + mp_srcptr up; + mp_ptr tp; + mp_bitcnt_t bits; + struct gmp_div_inverse bi; + size_t ndigits; + + assert (base >= 2); + assert (base <= 62); + + un = GMP_ABS (u->_mp_size); + if (un == 0) + return 1; + + up = u->_mp_d; + + bits = (un - 1) * GMP_LIMB_BITS + mpn_limb_size_in_base_2 (up[un-1]); + switch (base) + { + case 2: + return bits; + case 4: + return (bits + 1) / 2; + case 8: + return (bits + 2) / 3; + case 16: + return (bits + 3) / 4; + case 32: + return (bits + 4) / 5; + /* FIXME: Do something more clever for the common case of base + 10. */ + } + + tp = gmp_xalloc_limbs (un); + mpn_copyi (tp, up, un); + mpn_div_qr_1_invert (&bi, base); + + ndigits = 0; + do + { + ndigits++; + mpn_div_qr_1_preinv (tp, tp, un, &bi); + un -= (tp[un-1] == 0); + } + while (un > 0); + + gmp_free (tp); + return ndigits; +} + +char * +mpz_get_str (char *sp, int base, const mpz_t u) +{ + unsigned bits; + const char *digits; + mp_size_t un; + size_t i, sn; + + digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + if (base > 1) + { + if (base <= 36) + digits = "0123456789abcdefghijklmnopqrstuvwxyz"; + else if (base > 62) + return NULL; + } + else if (base >= -1) + base = 10; + else + { + base = -base; + if (base > 36) + return NULL; + } + + sn = 1 + mpz_sizeinbase (u, base); + if (!sp) + sp = (char *) gmp_xalloc (1 + sn); + + un = GMP_ABS (u->_mp_size); + + if (un == 0) + { + sp[0] = '0'; + sp[1] = '\0'; + return sp; + } + + i = 0; + + if (u->_mp_size < 0) + sp[i++] = '-'; + + bits = mpn_base_power_of_two_p (base); + + if (bits) + /* Not modified in this case. */ + sn = i + mpn_get_str_bits ((unsigned char *) sp + i, bits, u->_mp_d, un); + else + { + struct mpn_base_info info; + mp_ptr tp; + + mpn_get_base_info (&info, base); + tp = gmp_xalloc_limbs (un); + mpn_copyi (tp, u->_mp_d, un); + + sn = i + mpn_get_str_other ((unsigned char *) sp + i, base, &info, tp, un); + gmp_free (tp); + } + + for (; i < sn; i++) + sp[i] = digits[(unsigned char) sp[i]]; + + sp[sn] = '\0'; + return sp; +} + +int +mpz_set_str (mpz_t r, const char *sp, int base) +{ + unsigned bits, value_of_a; + mp_size_t rn, alloc; + mp_ptr rp; + size_t dn; + int sign; + unsigned char *dp; + + assert (base == 0 || (base >= 2 && base <= 62)); + + while (isspace( (unsigned char) *sp)) + sp++; + + sign = (*sp == '-'); + sp += sign; + + if (base == 0) + { + if (sp[0] == '0') + { + if (sp[1] == 'x' || sp[1] == 'X') + { + base = 16; + sp += 2; + } + else if (sp[1] == 'b' || sp[1] == 'B') + { + base = 2; + sp += 2; + } + else + base = 8; + } + else + base = 10; + } + + if (!*sp) + { + r->_mp_size = 0; + return -1; + } + dp = (unsigned char *) gmp_xalloc (strlen (sp)); + + value_of_a = (base > 36) ? 36 : 10; + for (dn = 0; *sp; sp++) + { + unsigned digit; + + if (isspace ((unsigned char) *sp)) + continue; + else if (*sp >= '0' && *sp <= '9') + digit = *sp - '0'; + else if (*sp >= 'a' && *sp <= 'z') + digit = *sp - 'a' + value_of_a; + else if (*sp >= 'A' && *sp <= 'Z') + digit = *sp - 'A' + 10; + else + digit = base; /* fail */ + + if (digit >= (unsigned) base) + { + gmp_free (dp); + r->_mp_size = 0; + return -1; + } + + dp[dn++] = digit; + } + + if (!dn) + { + gmp_free (dp); + r->_mp_size = 0; + return -1; + } + bits = mpn_base_power_of_two_p (base); + + if (bits > 0) + { + alloc = (dn * bits + GMP_LIMB_BITS - 1) / GMP_LIMB_BITS; + rp = MPZ_REALLOC (r, alloc); + rn = mpn_set_str_bits (rp, dp, dn, bits); + } + else + { + struct mpn_base_info info; + mpn_get_base_info (&info, base); + alloc = (dn + info.exp - 1) / info.exp; + rp = MPZ_REALLOC (r, alloc); + rn = mpn_set_str_other (rp, dp, dn, base, &info); + /* Normalization, needed for all-zero input. */ + assert (rn > 0); + rn -= rp[rn-1] == 0; + } + assert (rn <= alloc); + gmp_free (dp); + + r->_mp_size = sign ? - rn : rn; + + return 0; +} + +int +mpz_init_set_str (mpz_t r, const char *sp, int base) +{ + mpz_init (r); + return mpz_set_str (r, sp, base); +} + +size_t +mpz_out_str (FILE *stream, int base, const mpz_t x) +{ + char *str; + size_t len; + + str = mpz_get_str (NULL, base, x); + if (!str) + return 0; + len = strlen (str); + len = fwrite (str, 1, len, stream); + gmp_free (str); + return len; +} + + +static int +gmp_detect_endian (void) +{ + static const int i = 2; + const unsigned char *p = (const unsigned char *) &i; + return 1 - *p; +} + +/* Import and export. Does not support nails. */ +void +mpz_import (mpz_t r, size_t count, int order, size_t size, int endian, + size_t nails, const void *src) +{ + const unsigned char *p; + ptrdiff_t word_step; + mp_ptr rp; + mp_size_t rn; + + /* The current (partial) limb. */ + mp_limb_t limb; + /* The number of bytes already copied to this limb (starting from + the low end). */ + size_t bytes; + /* The index where the limb should be stored, when completed. */ + mp_size_t i; + + if (nails != 0) + gmp_die ("mpz_import: Nails not supported."); + + assert (order == 1 || order == -1); + assert (endian >= -1 && endian <= 1); + + if (endian == 0) + endian = gmp_detect_endian (); + + p = (unsigned char *) src; + + word_step = (order != endian) ? 2 * size : 0; + + /* Process bytes from the least significant end, so point p at the + least significant word. */ + if (order == 1) + { + p += size * (count - 1); + word_step = - word_step; + } + + /* And at least significant byte of that word. */ + if (endian == 1) + p += (size - 1); + + rn = (size * count + sizeof(mp_limb_t) - 1) / sizeof(mp_limb_t); + rp = MPZ_REALLOC (r, rn); + + for (limb = 0, bytes = 0, i = 0; count > 0; count--, p += word_step) + { + size_t j; + for (j = 0; j < size; j++, p -= (ptrdiff_t) endian) + { + limb |= (mp_limb_t) *p << (bytes++ * CHAR_BIT); + if (bytes == sizeof(mp_limb_t)) + { + rp[i++] = limb; + bytes = 0; + limb = 0; + } + } + } + assert (i + (bytes > 0) == rn); + if (limb != 0) + rp[i++] = limb; + else + i = mpn_normalized_size (rp, i); + + r->_mp_size = i; +} + +void * +mpz_export (void *r, size_t *countp, int order, size_t size, int endian, + size_t nails, const mpz_t u) +{ + size_t count; + mp_size_t un; + + if (nails != 0) + gmp_die ("mpz_import: Nails not supported."); + + assert (order == 1 || order == -1); + assert (endian >= -1 && endian <= 1); + assert (size > 0 || u->_mp_size == 0); + + un = u->_mp_size; + count = 0; + if (un != 0) + { + size_t k; + unsigned char *p; + ptrdiff_t word_step; + /* The current (partial) limb. */ + mp_limb_t limb; + /* The number of bytes left to do in this limb. */ + size_t bytes; + /* The index where the limb was read. */ + mp_size_t i; + + un = GMP_ABS (un); + + /* Count bytes in top limb. */ + limb = u->_mp_d[un-1]; + assert (limb != 0); + + k = (GMP_LIMB_BITS <= CHAR_BIT); + if (!k) + { + do { + int LOCAL_CHAR_BIT = CHAR_BIT; + k++; limb >>= LOCAL_CHAR_BIT; + } while (limb != 0); + } + /* else limb = 0; */ + + count = (k + (un-1) * sizeof (mp_limb_t) + size - 1) / size; + + if (!r) + r = gmp_xalloc (count * size); + + if (endian == 0) + endian = gmp_detect_endian (); + + p = (unsigned char *) r; + + word_step = (order != endian) ? 2 * size : 0; + + /* Process bytes from the least significant end, so point p at the + least significant word. */ + if (order == 1) + { + p += size * (count - 1); + word_step = - word_step; + } + + /* And at least significant byte of that word. */ + if (endian == 1) + p += (size - 1); + + for (bytes = 0, i = 0, k = 0; k < count; k++, p += word_step) + { + size_t j; + for (j = 0; j < size; ++j, p -= (ptrdiff_t) endian) + { + if (sizeof (mp_limb_t) == 1) + { + if (i < un) + *p = u->_mp_d[i++]; + else + *p = 0; + } + else + { + int LOCAL_CHAR_BIT = CHAR_BIT; + if (bytes == 0) + { + if (i < un) + limb = u->_mp_d[i++]; + bytes = sizeof (mp_limb_t); + } + *p = limb; + limb >>= LOCAL_CHAR_BIT; + bytes--; + } + } + } + assert (i == un); + assert (k == count); + } + + if (countp) + *countp = count; + + return r; +} diff --git a/src/rigraph/vendor/mini-gmp/mini-gmp.h b/src/rigraph/vendor/mini-gmp/mini-gmp.h new file mode 100644 index 0000000..d575f7d --- /dev/null +++ b/src/rigraph/vendor/mini-gmp/mini-gmp.h @@ -0,0 +1,305 @@ +/* mini-gmp, a minimalistic implementation of a GNU GMP subset. + +Copyright 2011-2015, 2017, 2019-2020 Free Software Foundation, Inc. + +This file is part of the GNU MP Library. + +The GNU MP Library is free software; you can redistribute it and/or modify +it under the terms of either: + + * the GNU Lesser General Public License as published by the Free + Software Foundation; either version 3 of the License, or (at your + option) any later version. + +or + + * the GNU General Public License as published by the Free Software + Foundation; either version 2 of the License, or (at your option) any + later version. + +or both in parallel, as here. + +The GNU MP Library is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received copies of the GNU General Public License and the +GNU Lesser General Public License along with the GNU MP Library. If not, +see https://www.gnu.org/licenses/. */ + +/* About mini-gmp: This is a minimal implementation of a subset of the + GMP interface. It is intended for inclusion into applications which + have modest bignums needs, as a fallback when the real GMP library + is not installed. + + This file defines the public interface. */ + +#ifndef __MINI_GMP_H__ +#define __MINI_GMP_H__ + +/* For size_t */ +#include + +#if defined (__cplusplus) +extern "C" { +#endif + +void mp_set_memory_functions (void *(*) (size_t), + void *(*) (void *, size_t, size_t), + void (*) (void *, size_t)); + +void mp_get_memory_functions (void *(**) (size_t), + void *(**) (void *, size_t, size_t), + void (**) (void *, size_t)); + +#ifndef MINI_GMP_LIMB_TYPE +#define MINI_GMP_LIMB_TYPE long +#endif + +typedef unsigned MINI_GMP_LIMB_TYPE mp_limb_t; +typedef long mp_size_t; +typedef unsigned long mp_bitcnt_t; + +typedef mp_limb_t *mp_ptr; +typedef const mp_limb_t *mp_srcptr; + +typedef struct +{ + int _mp_alloc; /* Number of *limbs* allocated and pointed + to by the _mp_d field. */ + int _mp_size; /* abs(_mp_size) is the number of limbs the + last field points to. If _mp_size is + negative this is a negative number. */ + mp_limb_t *_mp_d; /* Pointer to the limbs. */ +} __mpz_struct; + +typedef __mpz_struct mpz_t[1]; + +typedef __mpz_struct *mpz_ptr; +typedef const __mpz_struct *mpz_srcptr; + +extern const int mp_bits_per_limb; + +void mpn_copyi (mp_ptr, mp_srcptr, mp_size_t); +void mpn_copyd (mp_ptr, mp_srcptr, mp_size_t); +void mpn_zero (mp_ptr, mp_size_t); + +int mpn_cmp (mp_srcptr, mp_srcptr, mp_size_t); +int mpn_zero_p (mp_srcptr, mp_size_t); + +mp_limb_t mpn_add_1 (mp_ptr, mp_srcptr, mp_size_t, mp_limb_t); +mp_limb_t mpn_add_n (mp_ptr, mp_srcptr, mp_srcptr, mp_size_t); +mp_limb_t mpn_add (mp_ptr, mp_srcptr, mp_size_t, mp_srcptr, mp_size_t); + +mp_limb_t mpn_sub_1 (mp_ptr, mp_srcptr, mp_size_t, mp_limb_t); +mp_limb_t mpn_sub_n (mp_ptr, mp_srcptr, mp_srcptr, mp_size_t); +mp_limb_t mpn_sub (mp_ptr, mp_srcptr, mp_size_t, mp_srcptr, mp_size_t); + +mp_limb_t mpn_mul_1 (mp_ptr, mp_srcptr, mp_size_t, mp_limb_t); +mp_limb_t mpn_addmul_1 (mp_ptr, mp_srcptr, mp_size_t, mp_limb_t); +mp_limb_t mpn_submul_1 (mp_ptr, mp_srcptr, mp_size_t, mp_limb_t); + +mp_limb_t mpn_mul (mp_ptr, mp_srcptr, mp_size_t, mp_srcptr, mp_size_t); +void mpn_mul_n (mp_ptr, mp_srcptr, mp_srcptr, mp_size_t); +void mpn_sqr (mp_ptr, mp_srcptr, mp_size_t); +int mpn_perfect_square_p (mp_srcptr, mp_size_t); +mp_size_t mpn_sqrtrem (mp_ptr, mp_ptr, mp_srcptr, mp_size_t); + +mp_limb_t mpn_lshift (mp_ptr, mp_srcptr, mp_size_t, unsigned int); +mp_limb_t mpn_rshift (mp_ptr, mp_srcptr, mp_size_t, unsigned int); + +mp_bitcnt_t mpn_scan0 (mp_srcptr, mp_bitcnt_t); +mp_bitcnt_t mpn_scan1 (mp_srcptr, mp_bitcnt_t); + +void mpn_com (mp_ptr, mp_srcptr, mp_size_t); +mp_limb_t mpn_neg (mp_ptr, mp_srcptr, mp_size_t); + +mp_bitcnt_t mpn_popcount (mp_srcptr, mp_size_t); + +mp_limb_t mpn_invert_3by2 (mp_limb_t, mp_limb_t); +#define mpn_invert_limb(x) mpn_invert_3by2 ((x), 0) + +size_t mpn_get_str (unsigned char *, int, mp_ptr, mp_size_t); +mp_size_t mpn_set_str (mp_ptr, const unsigned char *, size_t, int); + +void mpz_init (mpz_t); +void mpz_init2 (mpz_t, mp_bitcnt_t); +void mpz_clear (mpz_t); + +#define mpz_odd_p(z) (((z)->_mp_size != 0) & (int) (z)->_mp_d[0]) +#define mpz_even_p(z) (! mpz_odd_p (z)) + +int mpz_sgn (const mpz_t); +int mpz_cmp_si (const mpz_t, long); +int mpz_cmp_ui (const mpz_t, unsigned long); +int mpz_cmp (const mpz_t, const mpz_t); +int mpz_cmpabs_ui (const mpz_t, unsigned long); +int mpz_cmpabs (const mpz_t, const mpz_t); +int mpz_cmp_d (const mpz_t, double); +int mpz_cmpabs_d (const mpz_t, double); + +void mpz_abs (mpz_t, const mpz_t); +void mpz_neg (mpz_t, const mpz_t); +void mpz_swap (mpz_t, mpz_t); + +void mpz_add_ui (mpz_t, const mpz_t, unsigned long); +void mpz_add (mpz_t, const mpz_t, const mpz_t); +void mpz_sub_ui (mpz_t, const mpz_t, unsigned long); +void mpz_ui_sub (mpz_t, unsigned long, const mpz_t); +void mpz_sub (mpz_t, const mpz_t, const mpz_t); + +void mpz_mul_si (mpz_t, const mpz_t, long int); +void mpz_mul_ui (mpz_t, const mpz_t, unsigned long int); +void mpz_mul (mpz_t, const mpz_t, const mpz_t); +void mpz_mul_2exp (mpz_t, const mpz_t, mp_bitcnt_t); +void mpz_addmul_ui (mpz_t, const mpz_t, unsigned long int); +void mpz_addmul (mpz_t, const mpz_t, const mpz_t); +void mpz_submul_ui (mpz_t, const mpz_t, unsigned long int); +void mpz_submul (mpz_t, const mpz_t, const mpz_t); + +void mpz_cdiv_qr (mpz_t, mpz_t, const mpz_t, const mpz_t); +void mpz_fdiv_qr (mpz_t, mpz_t, const mpz_t, const mpz_t); +void mpz_tdiv_qr (mpz_t, mpz_t, const mpz_t, const mpz_t); +void mpz_cdiv_q (mpz_t, const mpz_t, const mpz_t); +void mpz_fdiv_q (mpz_t, const mpz_t, const mpz_t); +void mpz_tdiv_q (mpz_t, const mpz_t, const mpz_t); +void mpz_cdiv_r (mpz_t, const mpz_t, const mpz_t); +void mpz_fdiv_r (mpz_t, const mpz_t, const mpz_t); +void mpz_tdiv_r (mpz_t, const mpz_t, const mpz_t); + +void mpz_cdiv_q_2exp (mpz_t, const mpz_t, mp_bitcnt_t); +void mpz_fdiv_q_2exp (mpz_t, const mpz_t, mp_bitcnt_t); +void mpz_tdiv_q_2exp (mpz_t, const mpz_t, mp_bitcnt_t); +void mpz_cdiv_r_2exp (mpz_t, const mpz_t, mp_bitcnt_t); +void mpz_fdiv_r_2exp (mpz_t, const mpz_t, mp_bitcnt_t); +void mpz_tdiv_r_2exp (mpz_t, const mpz_t, mp_bitcnt_t); + +void mpz_mod (mpz_t, const mpz_t, const mpz_t); + +void mpz_divexact (mpz_t, const mpz_t, const mpz_t); + +int mpz_divisible_p (const mpz_t, const mpz_t); +int mpz_congruent_p (const mpz_t, const mpz_t, const mpz_t); + +unsigned long mpz_cdiv_qr_ui (mpz_t, mpz_t, const mpz_t, unsigned long); +unsigned long mpz_fdiv_qr_ui (mpz_t, mpz_t, const mpz_t, unsigned long); +unsigned long mpz_tdiv_qr_ui (mpz_t, mpz_t, const mpz_t, unsigned long); +unsigned long mpz_cdiv_q_ui (mpz_t, const mpz_t, unsigned long); +unsigned long mpz_fdiv_q_ui (mpz_t, const mpz_t, unsigned long); +unsigned long mpz_tdiv_q_ui (mpz_t, const mpz_t, unsigned long); +unsigned long mpz_cdiv_r_ui (mpz_t, const mpz_t, unsigned long); +unsigned long mpz_fdiv_r_ui (mpz_t, const mpz_t, unsigned long); +unsigned long mpz_tdiv_r_ui (mpz_t, const mpz_t, unsigned long); +unsigned long mpz_cdiv_ui (const mpz_t, unsigned long); +unsigned long mpz_fdiv_ui (const mpz_t, unsigned long); +unsigned long mpz_tdiv_ui (const mpz_t, unsigned long); + +unsigned long mpz_mod_ui (mpz_t, const mpz_t, unsigned long); + +void mpz_divexact_ui (mpz_t, const mpz_t, unsigned long); + +int mpz_divisible_ui_p (const mpz_t, unsigned long); + +unsigned long mpz_gcd_ui (mpz_t, const mpz_t, unsigned long); +void mpz_gcd (mpz_t, const mpz_t, const mpz_t); +void mpz_gcdext (mpz_t, mpz_t, mpz_t, const mpz_t, const mpz_t); +void mpz_lcm_ui (mpz_t, const mpz_t, unsigned long); +void mpz_lcm (mpz_t, const mpz_t, const mpz_t); +int mpz_invert (mpz_t, const mpz_t, const mpz_t); + +void mpz_sqrtrem (mpz_t, mpz_t, const mpz_t); +void mpz_sqrt (mpz_t, const mpz_t); +int mpz_perfect_square_p (const mpz_t); + +void mpz_pow_ui (mpz_t, const mpz_t, unsigned long); +void mpz_ui_pow_ui (mpz_t, unsigned long, unsigned long); +void mpz_powm (mpz_t, const mpz_t, const mpz_t, const mpz_t); +void mpz_powm_ui (mpz_t, const mpz_t, unsigned long, const mpz_t); + +void mpz_rootrem (mpz_t, mpz_t, const mpz_t, unsigned long); +int mpz_root (mpz_t, const mpz_t, unsigned long); + +void mpz_fac_ui (mpz_t, unsigned long); +void mpz_2fac_ui (mpz_t, unsigned long); +void mpz_mfac_uiui (mpz_t, unsigned long, unsigned long); +void mpz_bin_uiui (mpz_t, unsigned long, unsigned long); + +int mpz_probab_prime_p (const mpz_t, int); + +int mpz_tstbit (const mpz_t, mp_bitcnt_t); +void mpz_setbit (mpz_t, mp_bitcnt_t); +void mpz_clrbit (mpz_t, mp_bitcnt_t); +void mpz_combit (mpz_t, mp_bitcnt_t); + +void mpz_com (mpz_t, const mpz_t); +void mpz_and (mpz_t, const mpz_t, const mpz_t); +void mpz_ior (mpz_t, const mpz_t, const mpz_t); +void mpz_xor (mpz_t, const mpz_t, const mpz_t); + +mp_bitcnt_t mpz_popcount (const mpz_t); +mp_bitcnt_t mpz_hamdist (const mpz_t, const mpz_t); +mp_bitcnt_t mpz_scan0 (const mpz_t, mp_bitcnt_t); +mp_bitcnt_t mpz_scan1 (const mpz_t, mp_bitcnt_t); + +int mpz_fits_slong_p (const mpz_t); +int mpz_fits_ulong_p (const mpz_t); +long int mpz_get_si (const mpz_t); +unsigned long int mpz_get_ui (const mpz_t); +double mpz_get_d (const mpz_t); +size_t mpz_size (const mpz_t); +mp_limb_t mpz_getlimbn (const mpz_t, mp_size_t); + +void mpz_realloc2 (mpz_t, mp_bitcnt_t); +mp_srcptr mpz_limbs_read (mpz_srcptr); +mp_ptr mpz_limbs_modify (mpz_t, mp_size_t); +mp_ptr mpz_limbs_write (mpz_t, mp_size_t); +void mpz_limbs_finish (mpz_t, mp_size_t); +mpz_srcptr mpz_roinit_n (mpz_t, mp_srcptr, mp_size_t); + +#define MPZ_ROINIT_N(xp, xs) {{0, (xs),(xp) }} + +void mpz_set_si (mpz_t, signed long int); +void mpz_set_ui (mpz_t, unsigned long int); +void mpz_set (mpz_t, const mpz_t); +void mpz_set_d (mpz_t, double); + +void mpz_init_set_si (mpz_t, signed long int); +void mpz_init_set_ui (mpz_t, unsigned long int); +void mpz_init_set (mpz_t, const mpz_t); +void mpz_init_set_d (mpz_t, double); + +size_t mpz_sizeinbase (const mpz_t, int); +char *mpz_get_str (char *, int, const mpz_t); +int mpz_set_str (mpz_t, const char *, int); +int mpz_init_set_str (mpz_t, const char *, int); + +/* This long list taken from gmp.h. */ +/* For reference, "defined(EOF)" cannot be used here. In g++ 2.95.4, + defines EOF but not FILE. */ +#if defined (FILE) \ + || defined (H_STDIO) \ + || defined (_H_STDIO) /* AIX */ \ + || defined (_STDIO_H) /* glibc, Sun, SCO */ \ + || defined (_STDIO_H_) /* BSD, OSF */ \ + || defined (__STDIO_H) /* Borland */ \ + || defined (__STDIO_H__) /* IRIX */ \ + || defined (_STDIO_INCLUDED) /* HPUX */ \ + || defined (__dj_include_stdio_h_) /* DJGPP */ \ + || defined (_FILE_DEFINED) /* Microsoft */ \ + || defined (__STDIO__) /* Apple MPW MrC */ \ + || defined (_MSL_STDIO_H) /* Metrowerks */ \ + || defined (_STDIO_H_INCLUDED) /* QNX4 */ \ + || defined (_ISO_STDIO_ISO_H) /* Sun C++ */ \ + || defined (__STDIO_LOADED) /* VMS */ \ + || defined (__DEFINED_FILE) /* musl */ +size_t mpz_out_str (FILE *, int, const mpz_t); +#endif + +void mpz_import (mpz_t, size_t, int, size_t, int, size_t, const void *); +void *mpz_export (void *, size_t *, int, size_t, int, size_t, const mpz_t); + +#if defined (__cplusplus) +} +#endif +#endif /* __MINI_GMP_H__ */ diff --git a/src/rigraph/vendor/plfit/arithmetic_ansi.h b/src/rigraph/vendor/plfit/arithmetic_ansi.h new file mode 100644 index 0000000..c58c98a --- /dev/null +++ b/src/rigraph/vendor/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 +#include + +#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/src/rigraph/vendor/plfit/arithmetic_sse_double.h b/src/rigraph/vendor/plfit/arithmetic_sse_double.h new file mode 100644 index 0000000..a94d89d --- /dev/null +++ b/src/rigraph/vendor/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 + +#if !defined(__APPLE__) +#include +#endif + +#include + +#if 1400 <= _MSC_VER +#include +#endif/*1400 <= _MSC_VER*/ + +#if HAVE_EMMINTRIN_H +#include +#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/src/rigraph/vendor/plfit/arithmetic_sse_float.h b/src/rigraph/vendor/plfit/arithmetic_sse_float.h new file mode 100644 index 0000000..b88f0e2 --- /dev/null +++ b/src/rigraph/vendor/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 + +#if !defined(__APPLE__) +#include +#endif + +#include + +#if 1400 <= _MSC_VER +#include +#endif/*_MSC_VER*/ + +#if HAVE_XMMINTRIN_H +#include +#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/src/rigraph/vendor/plfit/gss.c b/src/rigraph/vendor/plfit/gss.c new file mode 100644 index 0000000..f503484 --- /dev/null +++ b/src/rigraph/vendor/plfit/gss.c @@ -0,0 +1,153 @@ +/* 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 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 +#include +#include +#include "plfit_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/src/rigraph/vendor/plfit/gss.h b/src/rigraph/vendor/plfit/gss.h new file mode 100644 index 0000000..b96b213 --- /dev/null +++ b/src/rigraph/vendor/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 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public 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/src/rigraph/vendor/plfit/hzeta.c b/src/rigraph/vendor/plfit/hzeta.c new file mode 100644 index 0000000..eefd70c --- /dev/null +++ b/src/rigraph/vendor/plfit/hzeta.c @@ -0,0 +1,651 @@ +/* vim:set ts=4 sw=2 sts=2 et: */ + +/* This file was imported from a private scientific library + * based on GSL coined Home Scientific Libray (HSL) by its author + * Jerome Benoit; this very material is itself inspired from the + * material written by G. Jungan and distributed by GSL. + * Ultimately, some modifications were done in order to render the + * imported material independent from the rest of GSL. + */ + +/* `hsl/specfunc/hzeta.c' C source file +// HSL - Home Scientific Library +// Copyright (C) 2017-2018 Jerome Benoit +// +// HSL is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public 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 material in this file is mainly inspired by the material written by +// G. Jungan and distributed under GPLv2 by the GNU Scientific Library (GSL) +// ( https://www.gnu.org/software/gsl/ [specfunc/zeta.c]), itself inspired by +// the material written by Moshier and distributed in the Cephes Mathematical +// Library ( http://www.moshier.net/ [zeta.c]). +// +// More specifically, hsl_sf_hzeta_e is a slightly modifed clone of +// gsl_sf_hzeta_e as found in GSL 2.4; the remaining is `inspired by'. +// [Sooner or later a _Working_Note_ may be deposited at ResearchGate +// ( https://www.researchgate.net/profile/Jerome_Benoit )] +*/ + +/* Author: Jerome G. Benoit < jgmbenoit _at_ rezozer _dot_ net > */ + +#ifdef _MSC_VER +#define _USE_MATH_DEFINES +#endif + +#include +#include +#include "hzeta.h" +#include "plfit_error.h" +#include "platform.h" /* because of NAN */ + +/* imported 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 + +/* imported from gsl_math.h */ + +#ifndef M_LOG2E +#define M_LOG2E 1.44269504088896340735992468100 /* log_2 (e) */ +#endif + +/* imported from gsl_sf_result.h */ + +struct gsl_sf_result_struct { + double val; + double err; +}; +typedef struct gsl_sf_result_struct gsl_sf_result; + +/* imported and adapted from hsl/specfunc/specfunc_def.h */ + +#define HSL_SF_EVAL_RESULT(FnE) \ + gsl_sf_result result; \ + FnE ; \ + return (result.val); + +#define HSL_SF_EVAL_TUPLE_RESULT(FnET) \ + gsl_sf_result result0; \ + gsl_sf_result result1; \ + FnET ; \ + *tuple1=result1.val; \ + *tuple0=result0.val; \ + return (result0.val); + +/* */ + + +#define HSL_SF_HZETA_EULERMACLAURIN_SERIES_SHIFT 10 +#define HSL_SF_HZETA_EULERMACLAURIN_SERIES_ORDER 32 + +#define HSL_SF_LNHZETA_EULERMACLAURIN_SERIES_SHIFT_MAX 256 + +// B_{2j}/(2j) +static +double hsl_sf_hzeta_eulermaclaurin_series_coeffs[HSL_SF_HZETA_EULERMACLAURIN_SERIES_ORDER+1]={ + +1.0, + +1.0/12.0, + -1.0/720.0, + +1.0/30240.0, + -1.0/1209600.0, + +1.0/47900160.0, + -691.0/1307674368000.0, + +1.0/74724249600.0, + -3.38968029632258286683019539125e-13, + +8.58606205627784456413590545043e-15, + -2.17486869855806187304151642387e-16, + +5.50900282836022951520265260890e-18, + -1.39544646858125233407076862641e-19, + +3.53470703962946747169322997780e-21, + -8.95351742703754685040261131811e-23, + +2.26795245233768306031095073887e-24, + -5.74479066887220244526388198761e-26, + +1.45517247561486490186626486727e-27, + -3.68599494066531017818178247991e-29, + +9.33673425709504467203255515279e-31, + -2.36502241570062993455963519637e-32, + +5.99067176248213430465991239682e-34, + -1.51745488446829026171081313586e-35, + +3.84375812545418823222944529099e-37, + -9.73635307264669103526762127925e-39, + +2.46624704420068095710640028029e-40, + -6.24707674182074369314875679472e-42, + +1.58240302446449142975108170683e-43, + -4.00827368594893596853001219052e-45, + +1.01530758555695563116307139454e-46, + -2.57180415824187174992481940976e-48, + +6.51445603523381493155843485864e-50, + -1.65013099068965245550609878048e-51 + }; // hsl_sf_hzeta_eulermaclaurin_series_coeffs + +// 4\zeta(2j)/(2\pi)^(2j) +static +double hsl_sf_hzeta_eulermaclaurin_series_majorantratios[HSL_SF_HZETA_EULERMACLAURIN_SERIES_ORDER+1]={ + -2.0, + +1.0/6.0, + +1.0/360.0, + +1.0/15120.0, + +1.0/604800.0, + +1.0/23950080.0, + +691.0/653837184000.0, + +1.0/37362124800.0, + +3617.0/5335311421440000.0, + +1.71721241125556891282718109009e-14, + +4.34973739711612374608303284773e-16, + +1.10180056567204590304053052178e-17, + +2.79089293716250466814153725281e-19, + +7.06941407925893494338645995561e-21, + +1.79070348540750937008052226362e-22, + +4.53590490467536612062190147774e-24, + +1.14895813377444048905277639752e-25, + +2.91034495122972980373252973454e-27, + +7.37198988133062035636356495982e-29, + +1.86734685141900893440651103056e-30, + +4.73004483140125986911927039274e-32, + +1.19813435249642686093198247936e-33, + +3.03490976893658052342162627173e-35, + +7.68751625090837646445889058198e-37, + +1.94727061452933820705352425585e-38, + +4.93249408840136191421280056051e-40, + +1.24941534836414873862975135893e-41, + +3.16480604892898285950216341362e-43, + +8.01654737189787193706002438098e-45, + +2.03061517111391126232614278906e-46, + +5.14360831648374349984963881946e-48, + +1.30289120704676298631168697172e-49, + +3.30026198137930491101219756091e-51 + }; // hsl_sf_hzeta_eulermaclaurin_series_majorantratios + + +extern +int hsl_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; // max_bits=\lceil{s}\rceil with \zeta(s,2)=\zeta(s)-1=GSL_DBL_EPSILON + const double ln_term0=-s*log(q); + if (ln_term0 < GSL_LOG_DBL_MIN+1.0) { + PLFIT_ERROR("underflow", PLFIT_UNDRFLOW); + } + else if (GSL_LOG_DBL_MAX-1.0 < ln_term0) { + PLFIT_ERROR("overflow", PLFIT_OVERFLOW); + } +#if 1 + else if (((max_bits < s) && (q < 1.0)) || ((0.5*max_bits < s) && (q < 0.25))) { + result->val=pow(q,-s); + result->err=2.0*GSL_DBL_EPSILON*fabs(result->val); + return (PLFIT_SUCCESS); + } + else if ((0.5*max_bits < s) && (q < 1.0)) { + const double a0=pow(q,-s); + const double p1=pow(q/(1.0+q),s); + const double p2=pow(q/(2.0+q),s); + const double ans=a0*(1.0+p1+p2); + result->val=ans; + result->err=GSL_DBL_EPSILON*(2.0+0.5*s)*fabs(result->val); + return (PLFIT_SUCCESS); + } +#endif + else { // Euler-Maclaurin summation formula + const double qshift=HSL_SF_HZETA_EULERMACLAURIN_SERIES_SHIFT+q; + const double inv_qshift=1.0/qshift; + const double sqr_inv_qshift=inv_qshift*inv_qshift; + const double inv_sm1=1.0/(s-1.0); + const double pmax=pow(qshift,-s); + double terms[HSL_SF_HZETA_EULERMACLAURIN_SERIES_SHIFT+HSL_SF_HZETA_EULERMACLAURIN_SERIES_ORDER+1]={NAN}; + double delta=NAN; + double tscp=s; + double scp=tscp; + double pcp=pmax*inv_qshift; + double ratio=scp*pcp; + size_t n=0; + size_t j=0; + double ans=0.0; + double mjr=NAN; + + for(j=0;jval=+ans; + result->err=2.0*((HSL_SF_HZETA_EULERMACLAURIN_SERIES_SHIFT+1.0)*GSL_DBL_EPSILON*fabs(ans)+mjr); + return (PLFIT_SUCCESS); + } + } + +} + +extern +double hsl_sf_hzeta(const double s, const double q) { + HSL_SF_EVAL_RESULT(hsl_sf_hzeta_e(s,q,&result)); } + +extern +int hsl_sf_hzeta_deriv_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 ln_hz_term0=-s*log(q); + if (ln_hz_term0 < GSL_LOG_DBL_MIN+1.0) { + PLFIT_ERROR("underflow", PLFIT_UNDRFLOW); + } + else if (GSL_LOG_DBL_MAX-1.0 < ln_hz_term0) { + PLFIT_ERROR("overflow", PLFIT_OVERFLOW); + } + else { // Euler-Maclaurin summation formula + const double qshift=HSL_SF_HZETA_EULERMACLAURIN_SERIES_SHIFT+q; + const double inv_qshift=1.0/qshift; + const double sqr_inv_qshift=inv_qshift*inv_qshift; + const double inv_sm1=1.0/(s-1.0); + const double pmax=pow(qshift,-s); + const double lmax=log(qshift); + double terms[HSL_SF_HZETA_EULERMACLAURIN_SERIES_SHIFT+HSL_SF_HZETA_EULERMACLAURIN_SERIES_ORDER+1]={NAN}; + double delta=NAN; + double tscp=s; + double scp=tscp; + double pcp=pmax*inv_qshift; + double lcp=lmax-1.0/s; + double ratio=scp*pcp*lcp; + double qs=NAN; + size_t n=0; + size_t j=0; + double ans=0.0; + double mjr=NAN; + + for(j=0,qs=q;jval=-ans; + result->err=2.0*((HSL_SF_HZETA_EULERMACLAURIN_SERIES_SHIFT+1.0)*GSL_DBL_EPSILON*fabs(ans)+mjr); + return (PLFIT_SUCCESS); + } + } + +} + +extern +double hsl_sf_hzeta_deriv(const double s, const double q) { + HSL_SF_EVAL_RESULT(hsl_sf_hzeta_deriv_e(s,q,&result)); } + +extern +int hsl_sf_hzeta_deriv2_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 ln_hz_term0=-s*log(q); + if (ln_hz_term0 < GSL_LOG_DBL_MIN+1.0) { + PLFIT_ERROR("underflow", PLFIT_UNDRFLOW); + } + else if (GSL_LOG_DBL_MAX-1.0 < ln_hz_term0) { + PLFIT_ERROR("overflow", PLFIT_OVERFLOW); + } + else { // Euler-Maclaurin summation formula + const double qshift=HSL_SF_HZETA_EULERMACLAURIN_SERIES_SHIFT+q; + const double inv_qshift=1.0/qshift; + const double sqr_inv_qshift=inv_qshift*inv_qshift; + const double inv_sm1=1.0/(s-1.0); + const double pmax=pow(qshift,-s); + const double lmax=log(qshift); + const double lmax_p_inv_sm1=lmax+inv_sm1; + const double sqr_inv_sm1=inv_sm1*inv_sm1; + const double sqr_lmax=lmax*lmax; + const double sqr_lmax_p_inv_sm1=lmax_p_inv_sm1*lmax_p_inv_sm1; + double terms[HSL_SF_HZETA_EULERMACLAURIN_SERIES_SHIFT+HSL_SF_HZETA_EULERMACLAURIN_SERIES_ORDER+1]={NAN}; + double delta=NAN; + double tscp=s; + double slcp=NAN; + double plcp=NAN; + double scp=tscp; + double pcp=pmax*inv_qshift; + double lcp=1.0/s-lmax; + double sqr_lcp=lmax*(lmax-2.0/s); + double ratio=scp*pcp*sqr_lcp; + double qs=NAN; + double lqs=NAN; + size_t n=0; + size_t j=0; + double ans=0.0; + double mjr=NAN; + + for(j=0,qs=q;jval=+ans; + result->err=2.0*((HSL_SF_HZETA_EULERMACLAURIN_SERIES_SHIFT+1.0)*GSL_DBL_EPSILON*fabs(ans)+mjr); + return (PLFIT_SUCCESS); + } + } + +} + +extern +double hsl_sf_hzeta_deriv2(const double s, const double q) { + HSL_SF_EVAL_RESULT(hsl_sf_hzeta_deriv2_e(s,q,&result)); } + +static inline +double hsl_sf_hZeta0_zed(const double s, const double q) { +#if 1 + const long double ld_q=(long double)(q); + const long double ld_s=(long double)(s); + const long double ld_log1prq=log1pl(1.0L/ld_q); + const long double ld_epsilon=expm1l(-ld_s*ld_log1prq); + const long double ld_z=ld_s+(ld_q+0.5L*ld_s+0.5L)*ld_epsilon; + const double z=(double)(ld_z); +#else + double z=s+(q+0.5*s+0.5)*expm1(-s*log1p(1.0/q)); +#endif + return (z); } + +// Z_{0}(s,a) = a^s \left(\frac{1}{2}+\frac{a}{s-1}\right)^{-1} \zeta(s,a) - 1 +// Z_{0}(s,a) = O\left(\frac{(s-1)s}{6a^{2}}\right) +static +int hsl_sf_hZeta0(const double s, const double q, double * value, double * abserror) { + const double criterion=ceil(10.0*s-q); + const size_t shift=(criterion<0.0)?0: + (criterionval=log1p(ln_hZeta0_value); + result->err=(2.0*GSL_DBL_EPSILON*ln_hz_coeff+hZeta0_abserror)/(1.0+ln_hZeta0_value); + } + + if (result_deriv) { + const double ld_hz_coeff2=1.0+inv_sm1*M_LOG2E; + const double ld_hz_coeff1=1.0+inv_qsm1*ld_hz_coeff2; + double hZeta1_value=NAN; + double hZeta1_abserror=NAN; + hsl_sf_hZeta1(s,2.0,M_LN2,&hZeta1_value,&hZeta1_abserror,NULL); + hZeta0_value*=hz_coeff1; + hZeta0_value+=hz_coeff0; + hZeta1_value+=1.0; + hZeta1_value*=-M_LN2*ld_hz_coeff1; + result_deriv->val=hZeta1_value/hZeta0_value; + result_deriv->err=2.0*GSL_DBL_EPSILON*fabs(result_deriv->val)+(hZeta0_abserror+hZeta1_abserror); + } + } + else { + const double ln_q=log(q); + double hZeta0_value=NAN; + double hZeta0_abserror=NAN; + hsl_sf_hZeta0(s,q,&hZeta0_value,&hZeta0_abserror); + if (result) { + const double ln_hz_term0=-s*ln_q; + const double ln_hz_term1=log(0.5+q/(s-1.0)); + result->val=ln_hz_term0+ln_hz_term1+log1p(hZeta0_value); + result->err=2.0*GSL_DBL_EPSILON*(fabs(ln_hz_term0)+fabs(ln_hz_term1))+hZeta0_abserror/(1.0+hZeta0_value); + } + if (result_deriv) { + double hZeta1_value=NAN; + double hZeta1_abserror=NAN; + double ld_hz_coeff1=NAN; + hsl_sf_hZeta1(s,q,ln_q,&hZeta1_value,&hZeta1_abserror,&ld_hz_coeff1); + result_deriv->val=-ln_q*ld_hz_coeff1*(1.0+hZeta1_value)/(1.0+hZeta0_value); + result_deriv->err=2.0*GSL_DBL_EPSILON*fabs(result_deriv->val)+(hZeta0_abserror+hZeta1_abserror); + } + } + + return (PLFIT_SUCCESS); } + +extern +double hsl_sf_lnhzeta_deriv_tuple(const double s, const double q, double * tuple0, double * tuple1) { + HSL_SF_EVAL_TUPLE_RESULT(hsl_sf_lnhzeta_deriv_tuple_e(s,q,&result0,&result1)); } + +extern +int hsl_sf_lnhzeta_e(const double s, const double q, gsl_sf_result * result) { + return (hsl_sf_lnhzeta_deriv_tuple_e(s,q,result,NULL)); } + +extern +double hsl_sf_lnhzeta(const double s, const double q) { + HSL_SF_EVAL_RESULT(hsl_sf_lnhzeta_e(s,q,&result)); } + +extern +int hsl_sf_lnhzeta_deriv_e(const double s, const double q, gsl_sf_result * result) { + return (hsl_sf_lnhzeta_deriv_tuple_e(s,q,NULL,result)); } + +extern +double hsl_sf_lnhzeta_deriv(const double s, const double q) { + HSL_SF_EVAL_RESULT(hsl_sf_lnhzeta_deriv_e(s,q,&result)); } + +// +// End of file `hsl/specfunc/hzeta.c'. diff --git a/src/rigraph/vendor/plfit/hzeta.h b/src/rigraph/vendor/plfit/hzeta.h new file mode 100644 index 0000000..31d646e --- /dev/null +++ b/src/rigraph/vendor/plfit/hzeta.h @@ -0,0 +1,96 @@ +/* This file was imported from a private scientific library + * based on GSL coined Home Scientific Libray (HSL) by its author + * Jerome Benoit; this very material is itself inspired from the + * material written by G. Jungan and distributed by GSL. + * Ultimately, some modifications were done in order to render the + * imported material independent from the rest of GSL. + */ + +/* `hsl/hsl_sf_zeta.h' C header file +// HSL - Home Scientific Library +// Copyright (C) 2005-2018 Jerome Benoit +// +// HSL is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +/* For futher details, see its source conterpart src/hzeta.c */ + +/* Author: Jerome G. Benoit < jgmbenoit _at_ rezozer _dot_ net > */ + +#ifndef __HZETA_H__ +#define __HZETA_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 hsl_sf_hzeta(const double s, const double q); + +/* First Derivative of Hurwitz Zeta Function + * zeta'(s,q) = - Sum[ Ln(k+q)/(k+q)^(s), {k,0,Infinity} ] + * + * s > 1.0, q > 0.0 + */ +double hsl_sf_hzeta_deriv(const double s, const double q); + +/* Second Derivative of Hurwitz Zeta Function + * zeta''(s,q) = + Sum[ Ln(k+q)^2/(k+q)^(s), {k,0,Infinity} ] + * + * s > 1.0, q > 0.0 + */ +double hsl_sf_hzeta_deriv2(const double s, const double q); + +/* Logarithm of Hurwitz Zeta Function + * lnzeta(s,q) = ln(zeta(s,q)) + * + * s > 1.0, q > 0.0 (and q >> 1) + */ +double hsl_sf_lnhzeta(const double s, const double q); + +/* Logarithmic Derivative of Hurwitz Zeta Function + * lnzeta'(s,q) = zeta'(s,q)/zeta(s,q) + * + * s > 1.0, q > 0.0 (and q >> 1) + */ +double hsl_sf_lnhzeta_deriv(const double s, const double q); + +/* Logarithm and Logarithmic Derivative of Hurwitz Zeta Function: + * nonredundant computation version: + * - lnzeta(s,q) and lnzeta'(s,q) are stored in *deriv0 and *deriv1, respectively; + * - the return value and the value stored in *deriv0 are the same; + * - deriv0 and deriv1 must be effective pointers, that is, not the NULL pointer. + * + * s > 1.0, q > 0.0 (and q >> 1) + */ +double hsl_sf_lnhzeta_deriv_tuple(const double s, const double q, double * deriv0, double * deriv1); + + +__END_DECLS + +#endif // __HZETA_H__ diff --git a/src/rigraph/vendor/plfit/kolmogorov.c b/src/rigraph/vendor/plfit/kolmogorov.c new file mode 100644 index 0000000..432878f --- /dev/null +++ b/src/rigraph/vendor/plfit/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 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 +#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((double) 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/src/rigraph/vendor/plfit/kolmogorov.h b/src/rigraph/vendor/plfit/kolmogorov.h new file mode 100644 index 0000000..358e697 --- /dev/null +++ b/src/rigraph/vendor/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 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 + +__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/src/rigraph/vendor/plfit/lbfgs.c b/src/rigraph/vendor/plfit/lbfgs.c new file mode 100644 index 0000000..3fcd2c3 --- /dev/null +++ b/src/rigraph/vendor/plfit/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. + Mathematics of Computation, Vol. 35, No. 151, pp. 773--782, 1980. + - Dong C. Liu and Jorge Nocedal. + On the limited memory BFGS method for large scale optimization. + Mathematical Programming 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. + Numerical Methods for Unconstrained Optimization and Nonlinear + Equations, Englewood Cliffs, 1983. + - 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. + +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 Proceedings of the 24th International Conference on Machine + Learning (ICML 2007), 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 +#endif + +#include +#include +#include + +#include "lbfgs.h" +#include "platform.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, ¶m); + } else { + ls = linesearch(n, x, &fx, g, d, &step, xp, pg, w, &cd, ¶m); + 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) { + ret = cd.proc_progress(cd.instance, x, g, fx, xnorm, gnorm, step, cd.n, k, ls); + if (ret) { + 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); + } + } +} + + + +/** + * 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/src/rigraph/vendor/plfit/lbfgs.h b/src/rigraph/vendor/plfit/lbfgs.h new file mode 100644 index 0000000..f26ae87 --- /dev/null +++ b/src/rigraph/vendor/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, + +
+    minimize F(x), x = (x1, x2, ..., xN),
+
+ +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: +- Optimization with L1-norm (Orthant-Wise Limited-memory Quasi-Newton + (OWL-QN) method): + 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). +- Clean C code: + 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. +- Callback interface: + 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. +- Thread safe: + The library is thread-safe, which is the secondary gain from the callback + interface. +- Cross platform. The source code can be compiled on Microsoft Visual + Studio 2005, GNU C Compiler (gcc), etc. +- Configurable precision: A user can choose single-precision (float) + or double-precision (double) accuracy by changing ::LBFGS_FLOAT macro. +- SSE/SSE2 optimization: + 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: +- CRFsuite: A fast implementation of Conditional Random Fields (CRFs) +- Classias: A collection of machine-learning algorithms for classification +- mlegp: an R package for maximum likelihood estimates for Gaussian processes +- imaging2: the imaging2 class library +- Algorithm::LBFGS - Perl extension for L-BFGS +- YAP-LBFGS (an interface to call libLBFGS from YAP Prolog) + +@section download Download + +- Source code + +libLBFGS is distributed under the term of the +MIT license. + +@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, the OW-LQN method must be used with the + backtracking algorithm (::LBFGS_LINESEARCH_BACKTRACKING), 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. + Mathematics of Computation, Vol. 35, No. 151, pp. 773--782, 1980. + - Dong C. Liu and Jorge Nocedal. + On the limited memory BFGS method for large scale optimization. + Mathematical Programming 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. + Numerical Methods for Unconstrained Optimization and Nonlinear + Equations, Englewood Cliffs, 1983. + - 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. + +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 Proceedings of the 24th International Conference on Machine + Learning (ICML 2007), 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 + +- L-BFGS by Jorge Nocedal. +- Orthant-Wise Limited-memory Quasi-Newton Optimizer for L1-regularized Objectives by Galen Andrew. +- C port (via f2c) by Taku Kudo. +- C#/C++/Delphi/VisualBasic6 port in ALGLIB. +- Computational Crystallography Toolbox includes + scitbx::lbfgs. +*/ + +#endif/*__LBFGS_H__*/ diff --git a/src/rigraph/vendor/plfit/mt.c b/src/rigraph/vendor/plfit/mt.c new file mode 100644 index 0000000..615c6bc --- /dev/null +++ b/src/rigraph/vendor/plfit/mt.c @@ -0,0 +1,93 @@ +/* mt.c + * + * Mersenne Twister random number generator, based on the implementation of + * Michael Brundage (which has been placed in the public domain). + * + * Author: Tamas Nepusz (original by Michael Brundage) + * + * See the following URL for the original implementation: + * http://www.qbrundage.com/michaelb/pubs/essays/random_number_generation.html + * + * This file has been placed in the public domain. + */ + +#include + +#include "igraph_random.h" + +#include "plfit_mt.h" + +static uint16_t get_random_uint16() { + return RNG_INT31() & 0xFFFF; +} + +void plfit_mt_init(plfit_mt_rng_t* rng) { + plfit_mt_init_from_rng(rng, 0); +} + +void plfit_mt_init_from_rng(plfit_mt_rng_t* rng, plfit_mt_rng_t* seeder) { + int i; + + if (seeder == 0) { + for (i = 0; i < PLFIT_MT_LEN; i++) { + /* RAND_MAX is guaranteed to be at least 32767, so we can use two + * calls to rand() to produce a random 32-bit number */ + rng->mt_buffer[i] = (get_random_uint16() << 16) + get_random_uint16(); + } + } else { + for (i = 0; i < PLFIT_MT_LEN; i++) { + rng->mt_buffer[i] = plfit_mt_random(seeder); + } + } + + rng->mt_index = 0; +} + +#define MT_IA 397 +#define MT_IB (PLFIT_MT_LEN - MT_IA) +#define UPPER_MASK 0x80000000 +#define LOWER_MASK 0x7FFFFFFF +#define MATRIX_A 0x9908B0DF +#define TWIST(b,i,j) ((b)[i] & UPPER_MASK) | ((b)[j] & LOWER_MASK) +#define MAGIC(s) (((s)&1)*MATRIX_A) + +uint32_t plfit_mt_random(plfit_mt_rng_t* rng) { + uint32_t * b = rng->mt_buffer; + int idx = rng->mt_index; + uint32_t s; + int i; + + if (idx == PLFIT_MT_LEN * sizeof(uint32_t)) { + idx = 0; + i = 0; + for (; i < MT_IB; i++) { + s = TWIST(b, i, i+1); + b[i] = b[i + MT_IA] ^ (s >> 1) ^ MAGIC(s); + } + for (; i < PLFIT_MT_LEN-1; i++) { + s = TWIST(b, i, i+1); + b[i] = b[i - MT_IB] ^ (s >> 1) ^ MAGIC(s); + } + + s = TWIST(b, PLFIT_MT_LEN-1, 0); + b[PLFIT_MT_LEN-1] = b[MT_IA-1] ^ (s >> 1) ^ MAGIC(s); + } + + rng->mt_index = idx + sizeof(uint32_t); + return *(uint32_t *)((unsigned char *)b + idx); + /* + Matsumoto and Nishimura additionally confound the bits returned to the caller + but this doesn't increase the randomness, and slows down the generator by + as much as 25%. So I omit these operations here. + + r ^= (r >> 11); + r ^= (r << 7) & 0x9D2C5680; + r ^= (r << 15) & 0xEFC60000; + r ^= (r >> 18); + */ +} + + +double plfit_mt_uniform_01(plfit_mt_rng_t* rng) { + return ((double)plfit_mt_random(rng)) / PLFIT_MT_RAND_MAX; +} diff --git a/src/rigraph/vendor/plfit/options.c b/src/rigraph/vendor/plfit/options.c new file mode 100644 index 0000000..1665560 --- /dev/null +++ b/src/rigraph/vendor/plfit/options.c @@ -0,0 +1,52 @@ +/* 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 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 "plfit_error.h" +#include "plfit.h" + +const plfit_continuous_options_t plfit_continuous_default_options = { + /* .finite_size_correction = */ 0, + /* .xmin_method = */ PLFIT_DEFAULT_CONTINUOUS_METHOD, + /* .p_value_method = */ PLFIT_DEFAULT_P_VALUE_METHOD, + /* .p_value_precision = */ 0.01, + /* .rng = */ 0 +}; + +const plfit_discrete_options_t plfit_discrete_default_options = { + /* .finite_size_correction = */ 0, + /* .alpha_method = */ PLFIT_DEFAULT_DISCRETE_METHOD, + /* .alpha = */ { + /* .min = */ 1.01, + /* .max = */ 5, + /* .step = */ 0.01 + }, + /* .p_value_method = */ PLFIT_DEFAULT_P_VALUE_METHOD, + /* .p_value_precision = */ 0.01, + /* .rng = */ 0 +}; + +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/src/rigraph/vendor/plfit/platform.c b/src/rigraph/vendor/plfit/platform.c new file mode 100644 index 0000000..e9560e4 --- /dev/null +++ b/src/rigraph/vendor/plfit/platform.c @@ -0,0 +1,36 @@ +/* platform.c + * + * Copyright (C) 2014 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 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 "platform.h" + +#ifdef _MSC_VER + +inline double _plfit_fmin(double a, double b) { + return (a < b) ? a : b; +} + +inline double _plfit_round(double x) { + return floor(x+0.5); +} + +#endif + +/* Dummy function to prevent a warning when compiling with Clang - the file + * would contain no symbols */ +void _plfit_i_unused() {} diff --git a/src/rigraph/vendor/plfit/platform.h b/src/rigraph/vendor/plfit/platform.h new file mode 100644 index 0000000..6512955 --- /dev/null +++ b/src/rigraph/vendor/plfit/platform.h @@ -0,0 +1,69 @@ +/* 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 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 + +__BEGIN_DECLS + +#ifdef _MSC_VER +#include + +#define snprintf _snprintf +#define inline __inline + +#ifndef isfinite +# define isfinite(x) _finite(x) +#endif + +extern double _plfit_fmin(double a, double b); +extern double _plfit_round(double x); + +#define fmin _plfit_fmin +#define round _plfit_round + +#endif /* _MSC_VER */ + +#ifndef isnan +# define isnan(x) ((x) != (x)) +#endif + +#ifndef INFINITY +# define INFINITY (1.0/0.0) +#endif + +#ifndef NAN +# define NAN ((double)0.0 / (double)DBL_MIN) +#endif + +__END_DECLS + +#endif /* __PLATFORM_H__ */ diff --git a/src/rigraph/vendor/plfit/plfit.c b/src/rigraph/vendor/plfit/plfit.c new file mode 100644 index 0000000..266a9b0 --- /dev/null +++ b/src/rigraph/vendor/plfit/plfit.c @@ -0,0 +1,1342 @@ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* 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 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 +#include +#include +#include +#include +#include +#include "plfit_error.h" +#include "gss.h" +#include "lbfgs.h" +#include "platform.h" +#include "plfit.h" +#include "kolmogorov.h" +#include "plfit_sampling.h" +#include "hzeta.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 plfit_i_resample_continuous(double* xs_head, size_t num_smaller, + size_t n, double alpha, double xmin, size_t num_samples, plfit_mt_rng_t* rng, + double* result); +static int plfit_i_resample_discrete(double* xs_head, size_t num_smaller, + size_t n, double alpha, double xmin, size_t num_samples, plfit_mt_rng_t* rng, + double* result); + +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); +} + +static int plfit_i_copy_and_sort(double* xs, size_t n, double** result) { + *result = (double*)malloc(sizeof(double) * n); + if (*result == 0) { + PLFIT_ERROR("cannot create sorted copy of input data", PLFIT_ENOMEM); + } + + memcpy(*result, xs, sizeof(double) * n); + qsort(*result, n, sizeof(double), double_comparator); + + return PLFIT_SUCCESS; +} + +/** + * Given an unsorted array of doubles, counts how many elements there are that + * are smaller than a given value. + * + * \param begin pointer to the beginning of the array + * \param end pointer to the first element after the end of the array + * \param xmin the threshold value + * + * \return the nubmer of elements in the array that are smaller than the given + * value. + */ +static size_t count_smaller(double* begin, double* end, double xmin) { + double* p; + size_t counter = 0; + + for (p = begin; p < end; p++) { + if (*p < xmin) { + counter++; + } + } + + return counter; +} + +/** + * Given an unsorted array of doubles, return another array that contains the + * elements that are smaller than a given value + * + * \param begin pointer to the beginning of the array + * \param end pointer to the first element after the end of the array + * \param xmin the threshold value + * \param result_length if not \c NULL, the number of unique elements in the + * given array is returned here + * + * \return pointer to the head of the new array or 0 if there is not enough + * memory + */ +static double* extract_smaller(double* begin, double* end, double xmin, + size_t* result_length) { + size_t counter = count_smaller(begin, end, xmin); + double *p, *result; + + result = calloc(counter, sizeof(double)); + if (result == 0) + return 0; + + for (p = result; begin < end; begin++) { + if (*begin < xmin) { + *p = *begin; + p++; + } + } + + if (result_length) { + *result_length = counter; + } + + return result; +} + +/** + * 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. It is left unchanged if + * the function returns with an error. + * + * \return pointer to the head of the new array or 0 if there is not enough + * memory + */ +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; + if (result_length != 0) { + *result_length = 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 **********/ + +static 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; +} + +static 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; +} + +static 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; +} + +static 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; +} + +static int plfit_i_calculate_p_value_continuous(double* xs, size_t n, + const plfit_continuous_options_t *options, plfit_bool_t xmin_fixed, + plfit_result_t *result) { + long int num_trials; + long int successes = 0; + double *xs_head; + size_t num_smaller; + plfit_continuous_options_t options_no_p_value = *options; + int retval = PLFIT_SUCCESS; + + if (options->p_value_method == PLFIT_P_VALUE_SKIP) { + result->p = NAN; + return PLFIT_SUCCESS; + } + + if (options->p_value_method == PLFIT_P_VALUE_APPROXIMATE) { + num_smaller = count_smaller(xs, xs + n, result->xmin); + result->p = plfit_ks_test_one_sample_p(result->D, n - num_smaller); + return PLFIT_SUCCESS; + } + + options_no_p_value.p_value_method = PLFIT_P_VALUE_SKIP; + num_trials = (long int)(0.25 / options->p_value_precision / options->p_value_precision); + if (num_trials <= 0) { + PLFIT_ERROR("invalid p-value precision", PLFIT_EINVAL); + } + + /* Extract the head of xs that contains elements smaller than xmin */ + xs_head = extract_smaller(xs, xs+n, result->xmin, &num_smaller); + if (xs_head == 0) + PLFIT_ERROR("cannot calculate exact p-value", PLFIT_ENOMEM); + +#ifdef _OPENMP +#pragma omp parallel +#endif + { + /* Parallel section starts here. If we are compiling using OpenMP, each + * thread will use its own RNG that is seeded from the master RNG. If + * we are compiling without OpenMP, there is only one thread and it uses + * the master RNG. This section must be critical to ensure that only one + * thread is using the master RNG at the same time. */ +#ifdef _OPENMP + plfit_mt_rng_t private_rng; +#endif + plfit_mt_rng_t *p_rng; + double *ys; + long int i; + plfit_result_t result_synthetic; + +#ifdef _OPENMP +#pragma omp critical + { + p_rng = &private_rng; + plfit_mt_init_from_rng(p_rng, options->rng); + } +#else + p_rng = options->rng; +#endif + + /* Allocate memory to sample into */ + ys = calloc(n, sizeof(double)); + if (ys == 0) { + retval = PLFIT_ENOMEM; + } else { + /* The main for loop starts here. */ +#ifdef _OPENMP +#pragma omp for reduction(+:successes) +#endif + for (i = 0; i < num_trials; i++) { + plfit_i_resample_continuous(xs_head, num_smaller, n, result->alpha, + result->xmin, n, p_rng, ys); + if (xmin_fixed) { + plfit_estimate_alpha_continuous(ys, n, result->xmin, + &options_no_p_value, &result_synthetic); + } else { + plfit_continuous(ys, n, &options_no_p_value, &result_synthetic); + } + if (result_synthetic.D > result->D) + successes++; + } + free(ys); + } + + /* End of parallelized part */ + } + + free(xs_head); + + if (retval == PLFIT_SUCCESS) { + result->p = successes / ((double)num_trials); + } else { + PLFIT_ERROR("cannot calculate exact p-value", retval); + } + + return retval; +} + +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_sorted(double* xs, size_t n, double xmin, + const plfit_continuous_options_t* options, plfit_result_t *result) { + double *begin, *end; + + if (!options) + options = &plfit_continuous_default_options; + + begin = xs; + end = xs + n; + while (begin < end && *begin < xmin) + begin++; + + PLFIT_CHECK(plfit_i_estimate_alpha_continuous_sorted(begin, end-begin, + xmin, &result->alpha)); + PLFIT_CHECK(plfit_i_ks_test_continuous(begin, end, result->alpha, + xmin, &result->D)); + + if (options->finite_size_correction) + plfit_i_perform_finite_size_correction(result, end-begin); + result->xmin = xmin; + + PLFIT_CHECK(plfit_log_likelihood_continuous(begin, end-begin, result->alpha, + result->xmin, &result->L)); + PLFIT_CHECK(plfit_i_calculate_p_value_continuous(xs, n, options, 1, result)); + + 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; + + PLFIT_CHECK(plfit_i_copy_and_sort(xs, n, &xs_copy)); + PLFIT_CHECK(plfit_estimate_alpha_continuous_sorted(xs_copy, n, xmin, + options, result)); + free(xs_copy); + + 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 **probes; /**< Pointers to the elements of the array that will be probed */ + size_t num_probes; /**< Number of probes */ + plfit_result_t last; /**< Result of the last evaluation */ +} plfit_continuous_xmin_opt_data_t; + +static 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->probes[(long int)x]; + + data->last.xmin = *begin; + +#ifdef PLFIT_DEBUG + printf("Trying with probes[%ld] = %.4f\n", (long int)x, *begin); +#endif + + plfit_i_estimate_alpha_continuous_sorted(begin, 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; +} + +static 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; +} + +static int plfit_i_continuous_xmin_opt_linear_scan( + plfit_continuous_xmin_opt_data_t* opt_data, plfit_result_t* best_result, + size_t* best_n) { + /* this must be signed because OpenMP with Windows MSVC needs signed for + * loop index variables. ssize_t will not work because that is a POSIX + * extension */ + ptrdiff_t i = 0; /* initialize to work around incorrect warning issued by Clang 9.0 */ + plfit_result_t global_best_result; + size_t global_best_n; + + /* Prepare some variables */ + global_best_n = 0; + global_best_result.D = DBL_MAX; + global_best_result.xmin = 0; + global_best_result.alpha = 0; + + /* Due to the OpenMP parallelization, we do things as follows. Each + * OpenMP thread will search for the best D-score on its own and store + * the result in a private local_best_result variable. The end of the + * parallel block contains a critical section that threads will enter + * one by one and compare their private local_best_result with a + * global_best that is shared among the threads. + */ +#ifdef _OPENMP +#pragma omp parallel shared(global_best_result, global_best_n) private(i) firstprivate(opt_data) +#endif + { + /* These variables are private since they are declared within the + * parallel block */ + plfit_result_t local_best_result; + plfit_continuous_xmin_opt_data_t local_opt_data = *opt_data; + size_t local_best_n; + + /* Initialize the local_best_result and local_best_n variables */ + local_best_n = 0; + local_best_result.D = DBL_MAX; + local_best_result.xmin = 0; + local_best_result.alpha = 0; + local_best_result.p = NAN; + local_best_result.L = NAN; + + /* The range of the for loop below is divided among the threads. + * nowait means that there will be no implicit barrier at the end + * of the loop so threads that get there earlier can enter the + * critical section without waiting for the others */ +#ifdef _OPENMP +#pragma omp for nowait schedule(dynamic,10) +#endif + for (i = 0; i < local_opt_data.num_probes-1; i++) { + plfit_i_continuous_xmin_opt_evaluate(&local_opt_data, i); + if (local_opt_data.last.D < local_best_result.D) { +#ifdef PLFIT_DEBUG + printf("Found new local best at %g with D=%g\n", + local_opt_data.last.xmin, local_opt_data.last.D); +#endif + local_best_result = local_opt_data.last; + local_best_n = local_opt_data.end - local_opt_data.probes[i] + 1; + } + } + + /* Critical section that finds the global best result from the + * local ones collected by each thread */ +#ifdef _OPENMP +#pragma omp critical +#endif + if (local_best_result.D < global_best_result.D) { + global_best_result = local_best_result; + global_best_n = local_best_n; +#ifdef PLFIT_DEBUG + printf("Found new global best at %g with D=%g\n", global_best_result.xmin, + global_best_result.D); +#endif + } + } + + *best_result = global_best_result; + *best_n = global_best_n; + +#ifdef PLFIT_DEBUG + printf("Returning global best: %g\n", best_result->xmin); +#endif + + return PLFIT_SUCCESS; +} + +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 = { + /* alpha = */ NAN, + /* xmin = */ NAN, + /* L = */ NAN, + /* D = */ NAN, + /* p = */ NAN + }; + + int success; + size_t i, best_n, num_uniques = 0; + double x, *px, **uniques; + + DATA_POINTS_CHECK; + + /* Sane defaults */ + best_n = n; + if (!options) + options = &plfit_continuous_default_options; + + /* Make a copy of xs and sort it */ + PLFIT_CHECK(plfit_i_copy_and_sort(xs, n, &opt_data.begin)); + 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. */ + uniques = unique_element_pointers(opt_data.begin, opt_data.end, &num_uniques); + if (uniques == 0) + PLFIT_ERROR("cannot fit continuous power-law", PLFIT_ENOMEM); + + /* We will now determine the best xmin that yields the lowest D-score. The + * 'success' variable will denote whether the search procedure we tried was + * successful. If it is false after having exhausted all options, we fall + * back to a linear search. */ + success = 0; + switch (options->xmin_method) { + case PLFIT_GSS_OR_LINEAR: + /* Try golden section search first. */ + if (num_uniques > 5) { + opt_data.probes = uniques; + opt_data.num_probes = num_uniques; + gss_parameter_init(&gss_param); + success = (gss(0, opt_data.num_probes-5, &x, 0, + plfit_i_continuous_xmin_opt_evaluate, + plfit_i_continuous_xmin_opt_progress, &opt_data, &gss_param) == 0); + if (success) { + px = opt_data.probes[(int)x]; + best_n = opt_data.end-px+1; + best_result = opt_data.last; + } + } + break; + + case PLFIT_STRATIFIED_SAMPLING: + if (num_uniques >= 50) { + /* Try stratified sampling to narrow down the interval where the minimum + * is likely to reside. We check 10% of the unique items, distributed + * evenly, find the one with the lowest D-score, and then check the + * area around it more thoroughly. */ + const size_t subdivision_length = 10; + size_t num_strata = num_uniques / subdivision_length; + double **strata = calloc(num_strata, sizeof(double*)); + int error_code; + + for (i = 0; i < num_strata; i++) { + strata[i] = uniques[i * subdivision_length]; + } + + opt_data.probes = strata; + opt_data.num_probes = num_strata; + error_code = plfit_i_continuous_xmin_opt_linear_scan(&opt_data, &best_result, &best_n); + if (error_code != PLFIT_SUCCESS) { + free(strata); + return error_code; + } + + opt_data.num_probes = 0; + for (i = 0; i < num_strata; i++) { + if (*strata[i] == best_result.xmin) { + /* Okay, scan more thoroughly from strata[i-1] to strata[i+1], + * which is from uniques[(i-1)*subdivision_length] to + * uniques[(i+1)*subdivision_length */ + opt_data.probes = uniques + (i > 0 ? (i-1)*subdivision_length : 0); + opt_data.num_probes = 0; + if (i != 0) + opt_data.num_probes += subdivision_length; + if (i != num_strata-1) + opt_data.num_probes += subdivision_length; + break; + } + } + + free(strata); + if (opt_data.num_probes > 0) { + /* Do a strict linear scan in the subrange determined above */ + PLFIT_CHECK( + plfit_i_continuous_xmin_opt_linear_scan( + &opt_data, &best_result, &best_n + ) + ); + success = 1; + } else { + /* This should not happen, but we handle it anyway */ + success = 0; + } + } + break; + + default: + /* Just use the linear search */ + break; + } + + if (!success) { + /* More advanced search methods failed or were skipped; try linear search */ + opt_data.probes = uniques; + opt_data.num_probes = num_uniques; + PLFIT_CHECK(plfit_i_continuous_xmin_opt_linear_scan(&opt_data, &best_result, &best_n)); + success = 1; + } + + /* Get rid of the uniques array, we don't need it any more */ + free(uniques); + + /* Sort out the result */ + *result = best_result; + if (options->finite_size_correction) + plfit_i_perform_finite_size_correction(result, best_n); + + PLFIT_CHECK(plfit_log_likelihood_continuous(opt_data.begin + n - best_n, best_n, + result->alpha, result->xmin, &result->L)); + PLFIT_CHECK(plfit_i_calculate_p_value_continuous(opt_data.begin, n, options, 0, result)); + + /* 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; + +static double plfit_i_logsum_discrete(double* begin, double* end, double xmin) { + double logsum = 0.0; + for (; begin != end; begin++) + logsum += log(*begin); + return logsum; +} + +static 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; +} + +static 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 */ + double lnhzeta_x=NAN; + double lnhzeta_deriv_x=NAN; + + 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; + result = x[0] * data->logsum + data->m * hsl_sf_lnhzeta(x[0], data->xmin); + } else { + hsl_sf_lnhzeta_deriv_tuple(x[0], data->xmin, &lnhzeta_x, &lnhzeta_deriv_x); + g[0] = data->logsum + data->m * lnhzeta_deriv_x; + result = x[0] * data->logsum + data->m * lnhzeta_x; + } + +#ifdef PLFIT_DEBUG + printf(" - Gradient: %.4f\n", g[0]); + printf(" - Result: %.4f\n", result); +#endif + + return result; +} + +static 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; +} + +static 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 * hsl_sf_lnhzeta(curr_alpha, xmin); + if (L > L_max) { + L_max = L; + best_alpha = curr_alpha; + } + } + + *alpha = best_alpha; + + return PLFIT_SUCCESS; +} + +static 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(¶m); + 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, ¶m); + + if (ret < 0 && + ret != LBFGSERR_ROUNDING_ERROR && + ret != LBFGSERR_MAXIMUMLINESEARCH && + ret != LBFGSERR_MINIMUMSTEP && + 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; +} + +static 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); + } +} + +static 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, lnhzeta, x; + int m = 0; + + n = xs_end - xs; + lnhzeta = hsl_sf_lnhzeta(alpha, xmin); + + while (xs < xs_end) { + double d; + + x = *xs; + + /* Re the next line: this used to be the following: + * + * fabs( 1 - hzeta(alpha, x) / hzeta(alpha, xmin) - m / n) + * + * However, using the Hurwitz zeta directly sometimes yields + * underflows (see Github pull request #17 and related issues). + * hzeta(alpha, x) / hzeta(alpha, xmin) can be replaced with + * exp(lnhzeta(alpha, x) - lnhzeta(alpha, xmin)), but then + * we have 1 - exp(something), which is better to calculate + * with a dedicated expm1() function. + */ + d = fabs( expm1( hsl_sf_lnhzeta(alpha, x) - lnhzeta ) + m / n); + + if (d > result) + result = d; + + do { + xs++; m++; + } while (xs < xs_end && *xs == x); + } + + *D = result; + + return PLFIT_SUCCESS; +} + +static int plfit_i_calculate_p_value_discrete(double* xs, size_t n, + const plfit_discrete_options_t* options, plfit_bool_t xmin_fixed, + plfit_result_t *result) { + long int num_trials; + long int successes = 0; + double *xs_head; + size_t num_smaller; + plfit_discrete_options_t options_no_p_value = *options; + int retval = PLFIT_SUCCESS; + + if (options->p_value_method == PLFIT_P_VALUE_SKIP) { + /* skipping p-value calculation */ + result->p = NAN; + return PLFIT_SUCCESS; + } + + if (options->p_value_method == PLFIT_P_VALUE_APPROXIMATE) { + /* p-value approximation; most likely an upper bound */ + num_smaller = count_smaller(xs, xs + n, result->xmin); + result->p = plfit_ks_test_one_sample_p(result->D, n - num_smaller); + return PLFIT_SUCCESS; + } + + options_no_p_value.p_value_method = PLFIT_P_VALUE_SKIP; + num_trials = (long int)(0.25 / options->p_value_precision / options->p_value_precision); + if (num_trials <= 0) { + PLFIT_ERROR("invalid p-value precision", PLFIT_EINVAL); + } + + /* Extract the head of xs that contains elements smaller than xmin */ + xs_head = extract_smaller(xs, xs+n, result->xmin, &num_smaller); + if (xs_head == 0) + PLFIT_ERROR("cannot calculate exact p-value", PLFIT_ENOMEM); + +#ifdef _OPENMP +#pragma omp parallel +#endif + { + /* Parallel section starts here. If we are compiling using OpenMP, each + * thread will use its own RNG that is seeded from the master RNG. If + * we are compiling without OpenMP, there is only one thread and it uses + * the master RNG. This section must be critical to ensure that only one + * thread is using the master RNG at the same time. */ +#ifdef _OPENMP + plfit_mt_rng_t private_rng; +#endif + plfit_mt_rng_t *p_rng; + double *ys; + long int i; + plfit_result_t result_synthetic; + +#ifdef _OPENMP +#pragma omp critical + { + p_rng = &private_rng; + plfit_mt_init_from_rng(p_rng, options->rng); + } +#else + p_rng = options->rng; +#endif + + /* Allocate memory to sample into */ + ys = calloc(n, sizeof(double)); + if (ys == 0) { + retval = PLFIT_ENOMEM; + } else { + /* The main for loop starts here. */ +#ifdef _OPENMP +#pragma omp for reduction(+:successes) +#endif + for (i = 0; i < num_trials; i++) { + plfit_i_resample_discrete(xs_head, num_smaller, n, result->alpha, + result->xmin, n, p_rng, ys); + if (xmin_fixed) { + plfit_estimate_alpha_discrete(ys, n, result->xmin, + &options_no_p_value, &result_synthetic); + } else { + plfit_discrete(ys, n, &options_no_p_value, &result_synthetic); + } + if (result_synthetic.D > result->D) + successes++; + } + + free(ys); + } + + /* End of parallelized part */ + } + + free(xs_head); + + if (retval == PLFIT_SUCCESS) { + result->p = successes / ((double)num_trials); + } else { + PLFIT_ERROR("cannot calculate exact p-value", retval); + } + + return retval; +} + +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 * hsl_sf_lnhzeta(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, *begin, *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); + } + } + + PLFIT_CHECK(plfit_i_copy_and_sort(xs, n, &xs_copy)); + + begin = xs_copy; end = xs_copy + n; + while (begin < end && *begin < xmin) + begin++; + + PLFIT_CHECK(plfit_i_estimate_alpha_discrete(begin, end-begin, xmin, &result->alpha, + options, /* sorted = */ 1)); + PLFIT_CHECK(plfit_i_ks_test_discrete(begin, end, result->alpha, xmin, &result->D)); + + result->xmin = xmin; + if (options->finite_size_correction) + plfit_i_perform_finite_size_correction(result, end-begin); + + PLFIT_CHECK(plfit_log_likelihood_discrete(begin, end-begin, result->alpha, + result->xmin, &result->L)); + PLFIT_CHECK(plfit_i_calculate_p_value_discrete(xs, n, options, 1, result)); + + 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; + int 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); + } + } + + PLFIT_CHECK(plfit_i_copy_and_sort(xs, n, &xs_copy)); + + best_result.D = DBL_MAX; + best_result.xmin = 1; + best_result.alpha = 1; + best_n = 0; + + /* Skip initial values from xs_copy until we get to a positive element or + * until we reach the end of the array */ + px = xs_copy; end = px + n; end_xmin = end - 1; + while (px < end && *px < 1) { + px++; + } + + /* Make sure there are at least three distinct values if possible */ + m = px - xs_copy; + prev_x = *end_xmin; + while (end_xmin > px && *end_xmin == prev_x) { + end_xmin--; + } + prev_x = *end_xmin; + while (end_xmin > px && *end_xmin == prev_x) { + end_xmin--; + } + + prev_x = 0; + end_xmin++; + while (px < end_xmin) { + while (px < end_xmin && *px == prev_x) { + px++; m++; + } + + PLFIT_CHECK( + plfit_i_estimate_alpha_discrete( + px, n-m, *px, &curr_alpha, options, /* sorted = */ 1 + ) + ); + PLFIT_CHECK( + 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); + + PLFIT_CHECK(plfit_log_likelihood_discrete(xs_copy+(n-best_n), best_n, + result->alpha, result->xmin, &result->L)); + PLFIT_CHECK(plfit_i_calculate_p_value_discrete(xs_copy, n, options, 0, result)); + + free(xs_copy); + + return PLFIT_SUCCESS; +} + +/***** resampling routines to generate synthetic replicates ****/ + +static int plfit_i_resample_continuous(double* xs_head, size_t num_smaller, + size_t n, double alpha, double xmin, size_t num_samples, plfit_mt_rng_t* rng, + double* result) +{ + size_t num_orig_samples, i; + + /* Calculate how many samples have to be drawn from xs_head */ + num_orig_samples = (size_t) plfit_rbinom(num_samples, num_smaller / (double)n, rng); + + /* Draw the samples from xs_head */ + for (i = 0; i < num_orig_samples; i++, result++) { + *result = xs_head[(size_t)plfit_runif(0, num_smaller, rng)]; + } + + /* Draw the remaining samples from the fitted distribution */ + PLFIT_CHECK(plfit_rpareto_array(xmin, alpha-1, num_samples-num_orig_samples, rng, + result)); + + return PLFIT_SUCCESS; +} + +int plfit_resample_continuous(double* xs, size_t n, double alpha, double xmin, + size_t num_samples, plfit_mt_rng_t* rng, double* result) { + double *xs_head; + size_t num_smaller = 0; + int retval; + + /* Extract the head of xs that contains elements smaller than xmin */ + xs_head = extract_smaller(xs, xs+n, xmin, &num_smaller); + if (xs_head == 0) + PLFIT_ERROR("cannot resample continuous dataset", PLFIT_ENOMEM); + + retval = plfit_i_resample_continuous(xs_head, num_smaller, n, alpha, xmin, + num_samples, rng, result); + + /* Free xs_head; we don't need it any more */ + free(xs_head); + + return retval; +} + +static int plfit_i_resample_discrete(double* xs_head, size_t num_smaller, size_t n, + double alpha, double xmin, size_t num_samples, plfit_mt_rng_t* rng, + double* result) +{ + size_t num_orig_samples, i; + + /* Calculate how many samples have to be drawn from xs_head */ + num_orig_samples = (size_t) plfit_rbinom(num_samples, num_smaller / (double)n, rng); + + /* Draw the samples from xs_head */ + for (i = 0; i < num_orig_samples; i++, result++) { + *result = xs_head[(size_t)plfit_runif(0, num_smaller, rng)]; + } + + /* Draw the remaining samples from the fitted distribution */ + PLFIT_CHECK(plfit_rzeta_array((long int)xmin, alpha, + num_samples-num_orig_samples, rng, result)); + + return PLFIT_SUCCESS; +} + +int plfit_resample_discrete(double* xs, size_t n, double alpha, double xmin, + size_t num_samples, plfit_mt_rng_t* rng, double* result) { + double *xs_head; + size_t num_smaller = 0; + int retval; + + /* Extract the head of xs that contains elements smaller than xmin */ + xs_head = extract_smaller(xs, xs+n, xmin, &num_smaller); + if (xs_head == 0) + PLFIT_ERROR("cannot resample discrete dataset", PLFIT_ENOMEM); + + retval = plfit_i_resample_discrete(xs_head, num_smaller, n, alpha, xmin, + num_samples, rng, result); + + /* Free xs_head; we don't need it any more */ + free(xs_head); + + return retval; +} + +/******** calculating the p-value of a fitted model only *******/ + +int plfit_calculate_p_value_continuous(double* xs, size_t n, + const plfit_continuous_options_t* options, plfit_bool_t xmin_fixed, + plfit_result_t *result) { + double* xs_copy; + + PLFIT_CHECK(plfit_i_copy_and_sort(xs, n, &xs_copy)); + PLFIT_CHECK(plfit_i_calculate_p_value_continuous(xs_copy, n, options, + xmin_fixed, result)); + free(xs_copy); + + return PLFIT_SUCCESS; +} + +int plfit_calculate_p_value_discrete(double* xs, size_t n, + const plfit_discrete_options_t* options, plfit_bool_t xmin_fixed, + plfit_result_t *result) { + double* xs_copy; + + PLFIT_CHECK(plfit_i_copy_and_sort(xs, n, &xs_copy)); + PLFIT_CHECK(plfit_i_calculate_p_value_discrete(xs_copy, n, options, + xmin_fixed, result)); + free(xs_copy); + + return PLFIT_SUCCESS; +} diff --git a/src/rigraph/vendor/plfit/plfit.h b/src/rigraph/vendor/plfit/plfit.h new file mode 100644 index 0000000..afc0348 --- /dev/null +++ b/src/rigraph/vendor/plfit/plfit.h @@ -0,0 +1,140 @@ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* 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 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 +#include "plfit_mt.h" +#include "plfit_version.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 + +typedef unsigned short int plfit_bool_t; + +typedef enum { + PLFIT_LINEAR_ONLY, + PLFIT_STRATIFIED_SAMPLING, + PLFIT_GSS_OR_LINEAR, + PLFIT_DEFAULT_CONTINUOUS_METHOD = PLFIT_STRATIFIED_SAMPLING +} 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 enum { + PLFIT_P_VALUE_SKIP, + PLFIT_P_VALUE_APPROXIMATE, + PLFIT_P_VALUE_EXACT, + PLFIT_DEFAULT_P_VALUE_METHOD = PLFIT_P_VALUE_EXACT +} plfit_p_value_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_p_value_method_t p_value_method; + double p_value_precision; + plfit_mt_rng_t* rng; +} 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_p_value_method_t p_value_method; + double p_value_precision; + plfit_mt_rng_t* rng; +} 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_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); + +/***** resampling routines to generate synthetic replicates ****/ + +int plfit_resample_continuous(double* xs, size_t n, double alpha, double xmin, + size_t num_samples, plfit_mt_rng_t* rng, double* result); +int plfit_resample_discrete(double* xs, size_t n, double alpha, double xmin, + size_t num_samples, plfit_mt_rng_t* rng, double* result); + +/******** calculating the p-value of a fitted model only *******/ + +int plfit_calculate_p_value_continuous(double* xs, size_t n, + const plfit_continuous_options_t* options, plfit_bool_t xmin_fixed, + plfit_result_t *result); +int plfit_calculate_p_value_discrete(double* xs, size_t n, + const plfit_discrete_options_t* options, plfit_bool_t xmin_fixed, + plfit_result_t *result); + +/************* calculating descriptive statistics **************/ + +int plfit_moments(double* data, size_t n, double* mean, double* variance, + double* skewness, double* kurtosis); + +__END_DECLS + +#endif /* __PLFIT_H__ */ diff --git a/src/rigraph/vendor/plfit/plfit_error.c b/src/rigraph/vendor/plfit/plfit_error.c new file mode 100644 index 0000000..d93d1c0 --- /dev/null +++ b/src/rigraph/vendor/plfit/plfit_error.c @@ -0,0 +1,66 @@ +/* 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 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 +#include +#include "plfit_error.h" +#include "platform.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_printignore; +#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_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/src/rigraph/vendor/plfit/plfit_error.h b/src/rigraph/vendor/plfit/plfit_error.h new file mode 100644 index 0000000..e542997 --- /dev/null +++ b/src/rigraph/vendor/plfit/plfit_error.h @@ -0,0 +1,86 @@ +/* plfit_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 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public 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/src/rigraph/vendor/plfit/plfit_mt.h b/src/rigraph/vendor/plfit/plfit_mt.h new file mode 100644 index 0000000..c7ed0dc --- /dev/null +++ b/src/rigraph/vendor/plfit/plfit_mt.h @@ -0,0 +1,101 @@ +/* plfit_mt.h + * + * Mersenne Twister random number generator, based on the implementation of + * Michael Brundage (which has been placed in the public domain). + * + * Author: Tamas Nepusz (original by Michael Brundage) + * + * See the following URL for the original implementation: + * http://www.qbrundage.com/michaelb/pubs/essays/random_number_generation.html + * + * This file has been placed in the public domain. + */ + +#ifndef __PLFIT_MT_H__ +#define __PLFIT_MT_H__ + +/* VS 2010, i.e. _MSC_VER == 1600, already has stdint.h */ +#if defined(_MSC_VER) && _MSC_VER < 1600 +# define uint32_t unsigned __int32 +#else +# include +#endif + +#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_MT_LEN 624 + +/** + * \def PLFIT_MT_RAND_MAX + * + * The maximum random number that \c plfit_mt_random() can generate. + */ +#define PLFIT_MT_RAND_MAX 0xFFFFFFFF + +/** + * Struct that stores the internal state of a Mersenne Twister random number + * generator. + */ +typedef struct { + int mt_index; + uint32_t mt_buffer[PLFIT_MT_LEN]; +} plfit_mt_rng_t; + +/** + * \brief Initializes a Mersenne Twister random number generator. + * + * The random number generator is seeded with random 32-bit numbers obtained + * from the \em built-in random number generator using consecutive calls to + * \c rand(). + * + * \param rng the random number generator to initialize + */ +void plfit_mt_init(plfit_mt_rng_t* rng); + +/** + * \brief Initializes a Mersenne Twister random number generator, seeding it + * from another one. + * + * The random number generator is seeded with random 32-bit numbers obtained + * from another, initialized Mersenne Twister random number generator. + * + * \param rng the random number generator to initialize + * \param seeder the random number generator that will seed the one being + * initialized. When null, the random number generator will + * be initialized from the built-in RNG as if \ref plfit_mt_init() + * was called. + */ +void plfit_mt_init_from_rng(plfit_mt_rng_t* rng, plfit_mt_rng_t* seeder); + +/** + * \brief Returns the next 32-bit random number from the given Mersenne Twister + * random number generator. + * + * \param rng the random number generator to use + * \return the next 32-bit random number from the generator + */ +uint32_t plfit_mt_random(plfit_mt_rng_t* rng); + +/** + * \brief Returns a uniformly distributed double from the interval [0;1) + * based on the next value of the given Mersenne Twister random number + * generator. + * + * \param rng the random number generator to use + * \return a uniformly distributed random number from the interval [0;1) + */ +double plfit_mt_uniform_01(plfit_mt_rng_t* rng); + +__END_DECLS + +#endif diff --git a/src/rigraph/vendor/plfit/plfit_sampling.h b/src/rigraph/vendor/plfit/plfit_sampling.h new file mode 100644 index 0000000..f63407c --- /dev/null +++ b/src/rigraph/vendor/plfit/plfit_sampling.h @@ -0,0 +1,177 @@ +/* plfit_sampling.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 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 __SAMPLING_H__ +#define __SAMPLING_H__ + +#include +#include "plfit_mt.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 + +/** + * Draws a sample from a binomial distribution with the given count and + * probability values. + * + * This function is borrowed from R; see the corresponding license in + * \c rbinom.c. The return value is always an integer. + * + * The function is \em not thread-safe. + * + * \param n the number of trials + * \param p the success probability of each trial + * \param rng the Mersenne Twister random number generator to use + * \return the value drawn from the given binomial distribution. + */ +double plfit_rbinom(double n, double p, plfit_mt_rng_t* rng); + +/** + * Draws a sample from a Pareto distribution with the given minimum value and + * power-law exponent. + * + * \param xmin the minimum value of the distribution. Must be positive. + * \param alpha the exponent. Must be positive + * \param rng the Mersenne Twister random number generator to use + * + * \return the sample or NaN if one of the parameters is invalid + */ +extern double plfit_rpareto(double xmin, double alpha, plfit_mt_rng_t* rng); + +/** + * Draws a given number of samples from a Pareto distribution with the given + * minimum value and power-law exponent. + * + * \param xmin the minimum value of the distribution. Must be positive. + * \param alpha the exponent. Must be positive + * \param n the number of samples to draw + * \param rng the Mersenne Twister random number generator to use + * \param result the array where the result should be written. It must + * have enough space to store n items + * + * \return \c PLFIT_EINVAL if one of the parameters is invalid, zero otherwise + */ +int plfit_rpareto_array(double xmin, double alpha, size_t n, plfit_mt_rng_t* rng, + double* result); + +/** + * Draws a sample from a zeta distribution with the given minimum value and + * power-law exponent. + * + * \param xmin the minimum value of the distribution. Must be positive. + * \param alpha the exponent. Must be positive + * \param rng the Mersenne Twister random number generator to use + * + * \return the sample or NaN if one of the parameters is invalid + */ +extern double plfit_rzeta(long int xmin, double alpha, plfit_mt_rng_t* rng); + +/** + * Draws a given number of samples from a zeta distribution with the given + * minimum value and power-law exponent. + * + * \param xmin the minimum value of the distribution. Must be positive. + * \param alpha the exponent. Must be positive + * \param n the number of samples to draw + * \param rng the Mersenne Twister random number generator to use + * \param result the array where the result should be written. It must + * have enough space to store n items + * + * \return \c PLFIT_EINVAL if one of the parameters is invalid, zero otherwise + */ +int plfit_rzeta_array(long int xmin, double alpha, size_t n, plfit_mt_rng_t* rng, + double* result); + +/** + * Draws a sample from a uniform distribution with the given lower and + * upper bounds. + * + * The lower bound is inclusive, the uppoer bound is not. + * + * \param lo the lower bound + * \param hi the upper bound + * \param rng the Mersenne Twister random number generator to use + * \return the value drawn from the given uniform distribution. + */ +extern double plfit_runif(double lo, double hi, plfit_mt_rng_t* rng); + +/** + * Draws a sample from a uniform distribution over the [0; 1) interval. + * + * The interval is closed from the left and open from the right. + * + * \param rng the Mersenne Twister random number generator to use + * \return the value drawn from the given uniform distribution. + */ +extern double plfit_runif_01(plfit_mt_rng_t* rng); + +/** + * Random sampler using Walker's alias method. + */ +typedef struct { + long int num_bins; /**< Number of bins */ + long int* indexes; /**< Index of the "other" element in each bin */ + double* probs; /**< Probability of drawing the "own" element from a bin */ +} plfit_walker_alias_sampler_t; + +/** + * \brief Initializes the sampler with item probabilities. + * + * \param sampler the sampler to initialize + * \param ps pointer to an array containing a value proportional to the + * sampling probability of each item in the set being sampled. + * \param n the number of items in the array + * \return error code + */ +int plfit_walker_alias_sampler_init(plfit_walker_alias_sampler_t* sampler, + double* ps, size_t n); + +/** + * \brief Destroys an initialized sampler and frees the allocated memory. + * + * \param sampler the sampler to destroy + */ +void plfit_walker_alias_sampler_destroy(plfit_walker_alias_sampler_t* sampler); + +/** + * \brief Draws a given number of samples from the sampler and writes them + * to a given array. + * + * \param sampler the sampler to use + * \param xs pointer to an array where the sampled items should be + * written + * \param n the number of samples to draw + * \param rng the Mersenne Twister random number generator to use + * \return error code + */ +int plfit_walker_alias_sampler_sample(const plfit_walker_alias_sampler_t* sampler, + long int* xs, size_t n, plfit_mt_rng_t* rng); + +__END_DECLS + +#endif diff --git a/src/rigraph/vendor/plfit/plfit_version.h b/src/rigraph/vendor/plfit/plfit_version.h new file mode 100644 index 0000000..1212dd1 --- /dev/null +++ b/src/rigraph/vendor/plfit/plfit_version.h @@ -0,0 +1,28 @@ +/* plfit_version.h + * + * Copyright (C) 2021 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 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public 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_VERSION_H +#define PLFIT_VERSION_H + +#define PLFIT_VERSION_MAJOR 0 +#define PLFIT_VERSION_MINOR 9 +#define PLFIT_VERSION_PATCH 3 +#define PLFIT_VERSION_STRING "0.9.3" + +#endif diff --git a/src/rigraph/vendor/plfit/rbinom.c b/src/rigraph/vendor/plfit/rbinom.c new file mode 100644 index 0000000..771f972 --- /dev/null +++ b/src/rigraph/vendor/plfit/rbinom.c @@ -0,0 +1,208 @@ +/* + * Mathlib : A C Library of Special Functions + * Copyright (C) 1998 Ross Ihaka + * Copyright (C) 2000-2002 The R Core Team + * Copyright (C) 2007 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 + * + * #include + * double rbinom(double nin, double pp) + * + * DESCRIPTION + * + * Random variates from the binomial distribution. + * + * REFERENCE + * + * Kachitvichyanukul, V. and Schmeiser, B. W. (1988). + * Binomial random variate generation. + * Communications of the ACM 31, 216-222. + * (Algorithm BTPEC). + */ + +/* + * Modifications for this file were performed by Tamas Nepusz to make it fit + * better with plfit. The license of the original file applies to the + * modifications as well. + */ + +#include +#include +#include +#include "plfit_sampling.h" +#include "platform.h" + +#define repeat for(;;) + +double plfit_rbinom(double nin, double pp, plfit_mt_rng_t* rng) +{ + /* FIXME: These should become THREAD_specific globals : */ + + static double c, fm, npq, p1, p2, p3, p4, qn; + static double xl, xll, xlr, xm, xr; + + static double psave = -1.0; + static int nsave = -1; + static 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 (!isfinite(nin)) return NAN; + r = floor(nin + 0.5); + if (r != nin) return NAN; + if (!isfinite(pp) || + /* n=0, p=0, p=1 are not errors */ + r < 0 || pp < 0. || pp > 1.) return NAN; + + if (r == 0 || pp == 0.) return 0; + if (pp == 1.) return r; + + n = (int) r; + + 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 = (int) 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 = plfit_runif_01(rng) * p4; + v = plfit_runif_01(rng); + /* triangular region */ + if (u <= p1) { + ix = (int)(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 = (int) x; + } else { + if (u > p3) { /* right tail */ + ix = (int)(xr - log(v) / xlr); + if (ix > n) + continue; + v = v * (u - p3) * xlr; + } else {/* left tail */ + ix = (int)(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 = plfit_runif_01(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; +} diff --git a/src/rigraph/vendor/plfit/sampling.c b/src/rigraph/vendor/plfit/sampling.c new file mode 100644 index 0000000..4becf13 --- /dev/null +++ b/src/rigraph/vendor/plfit/sampling.c @@ -0,0 +1,312 @@ +/* sampling.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 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 +#include + +#include "igraph_random.h" + +#include "plfit_error.h" +#include "plfit_sampling.h" +#include "platform.h" + +inline double plfit_runif(double lo, double hi, plfit_mt_rng_t* rng) { + if (rng == 0) { + return RNG_UNIF(lo, hi); + } + return lo + plfit_mt_uniform_01(rng) * (hi-lo); +} + +inline double plfit_runif_01(plfit_mt_rng_t* rng) { + if (rng == 0) { + return RNG_UNIF01(); + } + return plfit_mt_uniform_01(rng); +} + +inline double plfit_rpareto(double xmin, double alpha, plfit_mt_rng_t* rng) { + if (alpha <= 0 || xmin <= 0) + return NAN; + + /* 1-u is used in the base here because we want to avoid the case of + * sampling zero */ + return pow(1-plfit_runif_01(rng), -1.0 / alpha) * xmin; +} + +int plfit_rpareto_array(double xmin, double alpha, size_t n, plfit_mt_rng_t* rng, + double* result) { + double gamma; + + if (alpha <= 0 || xmin <= 0) + return PLFIT_EINVAL; + + if (result == 0 || n == 0) + return PLFIT_SUCCESS; + + gamma = -1.0 / alpha; + while (n > 0) { + /* 1-u is used in the base here because we want to avoid the case of + * sampling zero */ + *result = pow(1-plfit_runif_01(rng), gamma) * xmin; + result++; n--; + } + + return PLFIT_SUCCESS; +} + +inline double plfit_rzeta(long int xmin, double alpha, plfit_mt_rng_t* rng) { + double u, v, t; + long int x; + double alpha_minus_1 = alpha-1; + double minus_1_over_alpha_minus_1 = -1.0 / (alpha-1); + double b; + double one_over_b_minus_1; + + if (alpha <= 0 || xmin < 1) + return NAN; + + xmin = (long int) round(xmin); + + /* Rejection sampling for the win. We use Y=floor(U^{-1/alpha} * xmin) as the + * envelope distribution, similarly to Chapter X.6 of Luc Devroye's book + * (where xmin is assumed to be 1): http://luc.devroye.org/chapter_ten.pdf + * + * Some notes that should help me recover what I was doing: + * + * p_i = 1/zeta(alpha, xmin) * i^-alpha + * q_i = (xmin/i)^{alpha-1} - (xmin/(i+1))^{alpha-1} + * = (i/xmin)^{1-alpha} - ((i+1)/xmin)^{1-alpha} + * = [i^{1-alpha} - (i+1)^{1-alpha}] / xmin^{1-alpha} + * + * p_i / q_i attains its maximum at xmin=i, so the rejection constant is: + * + * c = p_xmin / q_xmin + * + * We have to accept the sample if V <= (p_i / q_i) * (q_xmin / p_xmin) = + * (i/xmin)^-alpha * [xmin^{1-alpha} - (xmin+1)^{1-alpha}] / [i^{1-alpha} - (i+1)^{1-alpha}] = + * [xmin - xmin^alpha / (xmin+1)^{alpha-1}] / [i - i^alpha / (i+1)^{alpha-1}] = + * xmin/i * [1-(xmin/(xmin+1))^{alpha-1}]/[1-(i/(i+1))^{alpha-1}] + * + * In other words (and substituting i with X, which is the same), + * + * V * (X/xmin) <= [1 - (1+1/xmin)^{1-alpha}] / [1 - (1+1/i)^{1-alpha}] + * + * Let b := (1+1/xmin)^{alpha-1} and let T := (1+1/i)^{alpha-1}. Then: + * + * V * (X/xmin) <= [(b-1)/b] / [(T-1)/T] + * V * (X/xmin) * (T-1) / (b-1) <= T / b + * + * which is the same as in Devroye's book, except for the X/xmin term, and + * the definition of b. + */ + b = pow(1 + 1.0/xmin, alpha_minus_1); + one_over_b_minus_1 = 1.0/(b-1); + do { + do { + u = plfit_runif_01(rng); + v = plfit_runif_01(rng); + /* 1-u is used in the base here because we want to avoid the case of + * having zero in x */ + x = (long int) floor(pow(1-u, minus_1_over_alpha_minus_1) * xmin); + } while (x < xmin); + t = pow((x+1.0)/x, alpha_minus_1); + } while (v*x*(t-1)*one_over_b_minus_1*b > t*xmin); + + return x; +} + +int plfit_rzeta_array(long int xmin, double alpha, size_t n, plfit_mt_rng_t* rng, + double* result) { + double u, v, t; + long int x; + double alpha_minus_1 = alpha-1; + double minus_1_over_alpha_minus_1 = -1.0 / (alpha-1); + double b, one_over_b_minus_1; + + if (alpha <= 0 || xmin < 1) + return PLFIT_EINVAL; + + if (result == 0 || n == 0) + return PLFIT_SUCCESS; + + /* See the comments in plfit_rzeta for an explanation of the algorithm + * below. */ + xmin = (long int) round(xmin); + b = pow(1 + 1.0/xmin, alpha_minus_1); + one_over_b_minus_1 = 1.0/(b-1); + + while (n > 0) { + do { + do { + u = plfit_runif_01(rng); + v = plfit_runif_01(rng); + /* 1-u is used in the base here because we want to avoid the case of + * having zero in x */ + x = (long int) floor(pow(1-u, minus_1_over_alpha_minus_1) * xmin); + } while (x < xmin); /* handles overflow as well */ + t = pow((x+1.0)/x, alpha_minus_1); + } while (v*x*(t-1)*one_over_b_minus_1*b > t*xmin); + *result = x; + if (x < 0) return PLFIT_EINVAL; + result++; n--; + } + + return PLFIT_SUCCESS; +} + +int plfit_walker_alias_sampler_init(plfit_walker_alias_sampler_t* sampler, + double* ps, size_t n) { + double *p, *p2, *ps_end; + double sum; + long int *short_sticks, *long_sticks; + long int num_short_sticks, num_long_sticks; + long int i; + + if (n > LONG_MAX) { + return PLFIT_EINVAL; + } + + sampler->num_bins = (long int) n; + + ps_end = ps + n; + + /* Initialize indexes and probs */ + sampler->indexes = (long int*)calloc(n, sizeof(long int)); + if (sampler->indexes == 0) { + return PLFIT_ENOMEM; + } + sampler->probs = (double*)calloc(n, sizeof(double)); + if (sampler->probs == 0) { + free(sampler->indexes); + return PLFIT_ENOMEM; + } + + /* Normalize the probability vector; count how many short and long sticks + * are there initially */ + for (sum = 0.0, p = ps; p != ps_end; p++) { + sum += *p; + } + sum = n / sum; + + num_short_sticks = num_long_sticks = 0; + for (p = ps, p2 = sampler->probs; p != ps_end; p++, p2++) { + *p2 = *p * sum; + if (*p2 < 1) { + num_short_sticks++; + } else if (*p2 > 1) { + num_long_sticks++; + } + } + + /* Allocate space for short & long stick indexes */ + long_sticks = (long int*)calloc(num_long_sticks, sizeof(long int)); + if (long_sticks == 0) { + free(sampler->probs); + free(sampler->indexes); + return PLFIT_ENOMEM; + } + short_sticks = (long int*)calloc(num_long_sticks, sizeof(long int)); + if (short_sticks == 0) { + free(sampler->probs); + free(sampler->indexes); + free(long_sticks); + return PLFIT_ENOMEM; + } + + /* Initialize short_sticks and long_sticks */ + num_short_sticks = num_long_sticks = 0; + for (i = 0, p = sampler->probs; i < n; i++, p++) { + if (*p < 1) { + short_sticks[num_short_sticks++] = i; + } else if (*p > 1) { + long_sticks[num_long_sticks++] = i; + } + } + + /* Prepare the index table */ + while (num_short_sticks && num_long_sticks) { + long int short_index, long_index; + short_index = short_sticks[--num_short_sticks]; + long_index = long_sticks[num_long_sticks-1]; + sampler->indexes[short_index] = long_index; + sampler->probs[long_index] = /* numerical stability */ + (sampler->probs[long_index] + sampler->probs[short_index]) - 1; + if (sampler->probs[long_index] < 1) { + short_sticks[num_short_sticks++] = long_index; + num_long_sticks--; + } + } + + /* Fix numerical stability issues */ + while (num_long_sticks) { + i = long_sticks[--num_long_sticks]; + sampler->probs[i] = 1; + } + while (num_short_sticks) { + i = short_sticks[--num_short_sticks]; + sampler->probs[i] = 1; + } + + free(short_sticks); + free(long_sticks); + + return PLFIT_SUCCESS; +} + + +void plfit_walker_alias_sampler_destroy(plfit_walker_alias_sampler_t* sampler) { + if (sampler->indexes) { + free(sampler->indexes); + sampler->indexes = 0; + } + if (sampler->probs) { + free(sampler->probs); + sampler->probs = 0; + } +} + + +int plfit_walker_alias_sampler_sample(const plfit_walker_alias_sampler_t* sampler, + long int *xs, size_t n, plfit_mt_rng_t* rng) { + double u; + long int j; + long int *x; + + x = xs; + + if (rng == 0) { + /* Using built-in RNG */ + while (n > 0) { + u = RNG_UNIF01(); + j = RNG_INTEGER(0, sampler->num_bins - 1); + *x = (u < sampler->probs[j]) ? j : sampler->indexes[j]; + n--; x++; + } + } else { + /* Using Mersenne Twister */ + while (n > 0) { + u = plfit_mt_uniform_01(rng); + j = plfit_mt_random(rng) % sampler->num_bins; + *x = (u < sampler->probs[j]) ? j : sampler->indexes[j]; + n--; x++; + } + } + + return PLFIT_SUCCESS; +} diff --git a/src/rigraph/vendor/simpleraytracer/Color.cpp b/src/rigraph/vendor/simpleraytracer/Color.cpp new file mode 100644 index 0000000..3b31cf0 --- /dev/null +++ b/src/rigraph/vendor/simpleraytracer/Color.cpp @@ -0,0 +1,96 @@ +#include "Color.h" +#include "unit_limiter.h" + +namespace igraph { + +Color::Color() +{ +} + +Color::Color(double vRed, double vGreen, double vBlue, + double vTransparent) +{ + Red(vRed); + Green(vGreen); + Blue(vBlue); + Transparent(vTransparent); +} + +Color::~Color() +{ +} + +// returns multiplication of a scalar with this vector +Color Color::operator* (double vRhs) const +{ + return Color(mRed*vRhs, mGreen*vRhs, mBlue*vRhs, mTransparent); +} + +// returns the addition of this color with another color +Color Color::operator+ (const Color& vRhs) const +{ + double trans=Transparent() > vRhs.Transparent() ? Transparent() : + vRhs.Transparent(); + return Color(Red()+vRhs.Red(),Green()+vRhs.Green(),Blue()+vRhs.Blue(), + trans); +} + +void Color::Red(double vRed) +{ + mRed = unit_limiter(vRed); +} +double Color::Red() const +{ + return mRed; +} +void Color::Green(double vGreen) +{ + mGreen = unit_limiter(vGreen); + +} +double Color::Green() const +{ + return mGreen; +} +void Color::Blue(double vBlue) +{ + mBlue = unit_limiter(vBlue); +} +double Color::Blue() const +{ + return mBlue; +} + +void Color::Transparent(double vTransparent) +{ + mTransparent = unit_limiter(vTransparent); +} + +double Color::Transparent() const +{ + return mTransparent; +} + +unsigned char Color::RedByte() const +{ + return ByteValue(mRed); +} +unsigned char Color::GreenByte() const +{ + return ByteValue(mGreen); +} +unsigned char Color::BlueByte() const +{ + return ByteValue(mBlue); +} +unsigned char Color::TransparentByte() const +{ + return ByteValue(mTransparent); +} + +unsigned char Color::ByteValue(double vZeroToOne) const +{ + return (unsigned char)(vZeroToOne*255.0); +} + +} // namespace igraph diff --git a/src/rigraph/vendor/simpleraytracer/Color.h b/src/rigraph/vendor/simpleraytracer/Color.h new file mode 100644 index 0000000..8a10430 --- /dev/null +++ b/src/rigraph/vendor/simpleraytracer/Color.h @@ -0,0 +1,40 @@ +/** Color.h + */ + +#ifndef COLOR_H +#define COLOR_H + +namespace igraph { + +class Color +{ +public: + Color(); + Color(double vRed, double vGreen, double vBlue, + double vTransparent=1.0); + ~Color(); + + Color operator* (double vRhs) const; // returns multiplication of a scalar with a vector + Color operator+ (const Color& vRhs) const; // returns the addition of this color with another color + + void Red(double vRed); + double Red() const; + void Green(double vGreen); + double Green() const; + void Blue(double vBlue); + double Blue() const; + void Transparent(double vTransparent); + double Transparent() const; + + unsigned char RedByte() const; + unsigned char GreenByte() const; + unsigned char BlueByte() const; + unsigned char TransparentByte() const; +private: + unsigned char ByteValue(double vZeroToOne) const; + double mRed, mGreen, mBlue, mTransparent; +}; + +} // namespace igraph + +#endif diff --git a/src/rigraph/vendor/simpleraytracer/Light.cpp b/src/rigraph/vendor/simpleraytracer/Light.cpp new file mode 100644 index 0000000..d74cf0c --- /dev/null +++ b/src/rigraph/vendor/simpleraytracer/Light.cpp @@ -0,0 +1,46 @@ +#include "Light.h" +#include "unit_limiter.h" + +namespace igraph { + +Light::Light() : mLightPoint(0,0,0) +{ + mIntensity = 0.1; +} + +Light::Light(const Point& rLightPoint) : mLightPoint(rLightPoint) +{ + mIntensity = 0.1; +} + +Light::~Light() +{} + +const Point& Light::LightPoint() const +{ + return mLightPoint; +} +void Light::LightPoint(const Point& rLightPoint) +{ + mLightPoint = rLightPoint; +} +double Light::Intensity() const +{ + return mIntensity; +} +void Light::Intensity(double vIntensity) +{ + mIntensity = unit_limiter(vIntensity); +} + +const Color& Light::LightColor() const +{ + return mLightColor; +} + +void Light::LightColor(const Color& rLightColor) +{ + mLightColor = rLightColor; +} + +} // namespace igraph diff --git a/src/rigraph/vendor/simpleraytracer/Light.h b/src/rigraph/vendor/simpleraytracer/Light.h new file mode 100644 index 0000000..369b926 --- /dev/null +++ b/src/rigraph/vendor/simpleraytracer/Light.h @@ -0,0 +1,39 @@ +#ifndef LIGHT_H +#define LIGHT_H + +#include "Point.h" +#include "Color.h" + +#include +using namespace std; + +namespace igraph { + +class Light +{ +public: + Light(); // creates a light at the origin + Light(const Point& rLightPoint); + ~Light(); + + const Point& LightPoint() const; + void LightPoint(const Point& rLightPoint); + + double Intensity() const; + void Intensity(double vIntensity); + + const Color& LightColor() const; + void LightColor(const Color& rLightColor); + +private: + Point mLightPoint; + double mIntensity; // 0 to 1 + Color mLightColor; +}; + +typedef list LightList; +typedef list::iterator LightListIterator; + +} // namespace igraph + +#endif diff --git a/src/rigraph/vendor/simpleraytracer/Point.cpp b/src/rigraph/vendor/simpleraytracer/Point.cpp new file mode 100644 index 0000000..8ccbc27 --- /dev/null +++ b/src/rigraph/vendor/simpleraytracer/Point.cpp @@ -0,0 +1,106 @@ +#include "Point.h" +#include + +namespace igraph { + +Point::Point() +{ + X(0.0); + Y(0.0); + Z(0.0); + Name(0); +} + +Point::Point(double vX, double vY, double vZ, int vName) +{ + X(vX); + Y(vY); + Z(vZ); + Name(vName); +} + +Point::Point(double vX, double vY, double vZ) +{ + X(vX); + Y(vY); + Z(vZ); + Name(0); +} + +Point::~Point() +{} + +double Point::X() const +{ + return mX; +} + +void Point::X(double vX) +{ + mX = vX; +} + +double Point::Y() const +{ + return mY; +} + +void Point::Y(double vY) +{ + mY = vY; +} + +double Point::Z() const +{ + return mZ; +} + +void Point::Z(double vZ) +{ + mZ = vZ; +} + +int Point::Name() const +{ + return mName; +} + +void Point::Name(int vName) +{ + mName = vName; +} + +double Point::Distance(const Point& rPoint) const +{ + return sqrt( (rPoint.X() - mX)*(rPoint.X() - mX) + (rPoint.Y() - mY)*(rPoint.Y() - mY) + (rPoint.Z() - mZ)*(rPoint.Z() - mZ) ); +} + +bool Point::operator==(const Point& vRhs) const +{ + bool result = true; +/* + if ( mX + .001 <= vRhs.X() ) + result = false; + if ( mX - .001 >= vRhs.X() ) + result = false; + if ( mY + .001 <= vRhs.Y() ) + result = false; + if ( mY - .001 >= vRhs.Y() ) + result = false; + if ( mZ + .001 <= vRhs.Z() ) + result = false; + if ( mZ - .001 >= vRhs.Z() ) + result = false; +*/ + if ( mX != vRhs.X() ) + result = false; + if ( mY != vRhs.Y() ) + result = false; + if ( mZ != vRhs.Z() ) + result = false; + + + return result; +} + +} // namespace igraph diff --git a/src/rigraph/vendor/simpleraytracer/Point.h b/src/rigraph/vendor/simpleraytracer/Point.h new file mode 100644 index 0000000..647bc47 --- /dev/null +++ b/src/rigraph/vendor/simpleraytracer/Point.h @@ -0,0 +1,45 @@ +/** + this is a simple generic class representing a 3d point with a name. + it also defines the PointList type, which is a linked list of Points +*/ + +#ifndef POINT_H +#define POINT_H + +#include +using namespace std; + +namespace igraph { + +class Point +{ +public: + Point(); // creates a point at the origin with name 0 + Point(double vX, double vY, double vZ, int vName); + Point(double vX, double vY, double vZ); + ~Point(); + + double X() const; + void X(double vX); + double Y() const; + void Y(double vY); + double Z() const; + void Z(double vZ); + + int Name() const; + void Name(int vName); + double Distance(const Point& rPoint) const; + + bool operator==(const Point& vRhs) const; + +private: + double mX, mY, mZ; + int mName; +}; + +typedef list PointList; +typedef list::iterator PointListIterator; + +} // namespace igraph + +#endif diff --git a/src/rigraph/vendor/simpleraytracer/RIgraphRay.cpp b/src/rigraph/vendor/simpleraytracer/RIgraphRay.cpp new file mode 100644 index 0000000..8bc9ddb --- /dev/null +++ b/src/rigraph/vendor/simpleraytracer/RIgraphRay.cpp @@ -0,0 +1,93 @@ +/* -*- mode: C -*- */ +/* + IGraph library R interface. + Copyright (C) 2005-2012 Gabor Csardi + 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.h" +#include "igraph_error.h" + +#include "RayTracer.h" +#include "Sphere.h" + +#include "config.h" + +#include +#include +#include + +using namespace igraph; + +extern "C" { + +SEXP R_igraph_getsphere(SEXP pos, SEXP radius, SEXP color, SEXP bgcolor, + SEXP lightpos, SEXP lightcolor, SEXP width, + SEXP height) { + + /* All error checking is done at the R level */ + + int i; + double *spos=REAL(pos); + double *scolor=REAL(color); + int no_lights=GET_LENGTH(lightpos); + RayTracer* p_ray_tracer; + Sphere * sphere; + int swidth=INTEGER(width)[0]; + int sheight=INTEGER(height)[0]; + int nopixels=swidth * sheight; + SEXP result, dim; + Image image; + + p_ray_tracer = new RayTracer(); + p_ray_tracer->EyePoint(Point(0,0,0)); + + for (i=0; iIntensity(1); + light->LightColor(Color(lcol[0], lcol[1], lcol[2])); + p_ray_tracer->AddLight(light); + } + + sphere = new Sphere(Point(spos[0], spos[1], spos[2]), REAL(radius)[0]); + sphere->ShapeColor(Color(scolor[0], scolor[1], scolor[2])); + p_ray_tracer->AddShape(sphere); + + PROTECT(result=NEW_NUMERIC(nopixels * 4)); + PROTECT(dim=NEW_INTEGER(3)); + INTEGER(dim)[0]=swidth; INTEGER(dim)[1]=sheight; INTEGER(dim)[2]=4; + SET_DIM(result, dim); + + image.width=swidth; + image.height=sheight; + image.red=REAL(result); + image.green=image.red + nopixels; + image.blue=image.green + nopixels; + image.trans=image.blue + nopixels; + + p_ray_tracer->RayTrace(image); + delete p_ray_tracer; + + UNPROTECT(2); + return result; +} + +} // extern C diff --git a/src/rigraph/vendor/simpleraytracer/Ray.cpp b/src/rigraph/vendor/simpleraytracer/Ray.cpp new file mode 100644 index 0000000..7f6744a --- /dev/null +++ b/src/rigraph/vendor/simpleraytracer/Ray.cpp @@ -0,0 +1,44 @@ +#include "Ray.h" + +namespace igraph { + +Ray::Ray() +{} + +Ray::~Ray() +{} + +Ray::Ray(const Point& rOrigin, const Vector& rDirection) +{ + Direction(rDirection); + Origin(rOrigin); + +} + +Ray::Ray(const Point& rOrigin, const Point& rEndPoint) +{ + Direction(Vector(rOrigin,rEndPoint)); + Origin(rOrigin); +} + +const Point& Ray::Origin() const +{ + return mOrigin; +} + +void Ray::Origin(Point vOrigin) +{ + mOrigin = vOrigin; +} + +const Vector& Ray::Direction() const +{ + return mDirection; +} + +void Ray::Direction(Vector vDirection) +{ + mDirection = vDirection; +} + +} // namespace igraph diff --git a/src/rigraph/vendor/simpleraytracer/Ray.h b/src/rigraph/vendor/simpleraytracer/Ray.h new file mode 100644 index 0000000..cd14ca1 --- /dev/null +++ b/src/rigraph/vendor/simpleraytracer/Ray.h @@ -0,0 +1,33 @@ +/** Ray.h + */ + +#ifndef RAY_H +#define RAY_H + +#include "RayVector.h" +#include "Point.h" + +namespace igraph { + +class Ray +{ +public: + Ray(); + Ray(const Point& rOrigin, const Vector& rDirection); + Ray(const Point& rOrigin, const Point& rEndPoint); + ~Ray(); + + void Origin(Point vPoint); + const Point& Origin() const; + + const Vector& Direction() const; + void Direction(Vector vDirection); + +private: + Vector mDirection; + Point mOrigin; +}; + +} // namespace igraph + +#endif diff --git a/src/rigraph/vendor/simpleraytracer/RayTracer.cpp b/src/rigraph/vendor/simpleraytracer/RayTracer.cpp new file mode 100644 index 0000000..27e3cc5 --- /dev/null +++ b/src/rigraph/vendor/simpleraytracer/RayTracer.cpp @@ -0,0 +1,266 @@ +#include "RayTracer.h" +#include "unit_limiter.h" +#include +#include + +namespace igraph { + +RayTracer::RayTracer() : mBackgroundColor(0,0,0,0), mAmbientColor(0,0,0), mEyePoint(0,0,0), mSpecularColor(1,1,1) +{ + // begin settings + mAmbientIntensity = .7; + mRecursionLimit = 700; + mAntiAliasDetail = 1; + // end settings + + mRecursions = 0; + + mpShapes = new ShapeList; + mpLights = new LightList; +} + +RayTracer::~RayTracer() +{ + ShapeListIterator iter1 = mpShapes->begin(); + while ( iter1 != mpShapes->end() ) + { + delete *iter1; + iter1++; + } + delete mpShapes; + + LightListIterator iter2 = mpLights->begin(); + while ( iter2 != mpLights->end() ) + { + delete *iter2; + iter2++; + } + + delete mpLights; + +} + +void RayTracer::RayTrace(Image &result) +{ + int mWidth=result.width; + int mHeight=result.height; + Ray eye_ray(mEyePoint,Vector(0,0,1)); + Color draw_color; + double i_inc, j_inc, anti_alias_i_inc, anti_alias_j_inc; // amount to increment the ray in each direction + double i, j, anti_alias_i, anti_alias_j; // the i and j values of the ray + int pixel_x, pixel_y, anti_alias_pixel_x, anti_alias_pixel_y; // the pixels being drawn + + double average_red_byte, average_green_byte, average_blue_byte, average_trans_byte; + int anti_alias_count; // the number of anti aliases (used in averaging) + int idx=0; + + i_inc = 2.0/(double)mWidth; + j_inc = 2.0/(double)mHeight; + anti_alias_i_inc = 1.0/(double)mAntiAliasDetail; + anti_alias_j_inc = 1.0/(double)mAntiAliasDetail; + + pixel_y = 0; + j = 1.0; + for (; pixel_y < mHeight; j -= j_inc, pixel_y++) + { + pixel_x = 0; + i = -1.0; + for (; pixel_x < mWidth; i += i_inc, pixel_x++) + { + anti_alias_pixel_y = 0; + anti_alias_j = 0.0; + average_red_byte = 0; + average_green_byte = 0; + average_blue_byte = 0; + average_trans_byte = 0; + anti_alias_count = 0; + for (; anti_alias_pixel_y < mAntiAliasDetail; anti_alias_j += anti_alias_j_inc, anti_alias_pixel_y++) + { + anti_alias_pixel_x = 0; + anti_alias_i = 0.0; + + for (; anti_alias_pixel_x < mAntiAliasDetail; anti_alias_i += anti_alias_i_inc, anti_alias_pixel_x++) + { + anti_alias_count++; + eye_ray.Direction( Vector(i+(anti_alias_i*i_inc),j+(anti_alias_j*j_inc),1.0) ); + draw_color = Render(eye_ray); + + average_red_byte = average_red_byte + ((double)draw_color.RedByte() - average_red_byte)/(double)anti_alias_count; + average_green_byte = average_green_byte + ((double)draw_color.GreenByte() - average_green_byte)/(double)anti_alias_count; + average_blue_byte = average_blue_byte + ((double)draw_color.BlueByte() - average_blue_byte)/(double)anti_alias_count; + average_trans_byte = average_trans_byte + ((double)draw_color.TransparentByte() - average_trans_byte)/(double)anti_alias_count; + } + } + + result.red [idx] = average_red_byte/255; + result.green[idx] = average_green_byte/255; + result.blue [idx] = average_blue_byte/255; + result.trans[idx] = average_trans_byte/255; + idx++; + } + } +} + +Color RayTracer::Render(const Ray& rRay, bool vIsReflecting, const Shape* pReflectingFrom ) +{ + mRecursions++; + Shape* closest_shape; + Point intersect_point; + Color result; + if (vIsReflecting) + closest_shape = QueryScene(rRay, intersect_point, vIsReflecting, pReflectingFrom); + else + closest_shape = QueryScene(rRay, intersect_point); + + if (closest_shape == NULL && !vIsReflecting) + { + mRecursions = 0; + return mBackgroundColor; + } + if (closest_shape == NULL && vIsReflecting) + { + mRecursions = 0; + return mAmbientColor*mAmbientIntensity; + } + if ( mRecursions > mRecursionLimit ) + { + mRecursions = 0; + return Color(0,0,0); // mAmbientColor*mAmbientIntensity; + } + result = closest_shape->ShapeColor()*Shade(closest_shape, intersect_point); + + Ray backwards_ray(intersect_point,rRay.Direction()*-1); + if ( closest_shape->DiffuseReflectivity() > 0.0 ) + result = result + (Render( closest_shape->Reflect(intersect_point,backwards_ray), true, closest_shape )*closest_shape->DiffuseReflectivity()); + + return (result + mSpecularColor); +} + +double RayTracer::Shade(const Shape* pShapeToShade, const Point& rPointOnShapeToShade) +{ + double intensity = mAmbientIntensity * pShapeToShade->AmbientReflectivity(); // the ambient intensity of the scene + Ray light_ray; // the ray that goes from the intersection point to the light sources + double dot_product; + Shape* closest_shape; // the shape closest from the intersection point to the light source + Point light_intersect; // the intersection point of the ray that goes from the intersection point to the light source + light_ray.Origin(rPointOnShapeToShade); // lightRay. org= object. intersect; + Ray light_ray_from_light; + + LightListIterator iter = mpLights->begin(); + mSpecularColor.Red(0); + mSpecularColor.Green(0); + mSpecularColor.Blue(0); + + while ( iter != mpLights->end() ) // foreach light in LightList do + { + + light_ray.Direction(Vector(rPointOnShapeToShade,(*iter)->LightPoint())); // lightRay. dir= light. dir + light_ray_from_light.Origin((*iter)->LightPoint()); + light_ray_from_light.Direction(Vector((*iter)->LightPoint(),rPointOnShapeToShade)); + + closest_shape = QueryScene(light_ray_from_light, light_intersect); + if ( closest_shape == NULL || (closest_shape == pShapeToShade && light_ray.Direction().Dot(pShapeToShade->Normal(rPointOnShapeToShade, light_ray_from_light.Origin() )) >= 0.0 ) ) //if (QueryScene( lightRay)= NIL) + { + Vector normal_vector = pShapeToShade->Normal(rPointOnShapeToShade, Point() ); + dot_product = normal_vector.Dot(light_ray.Direction().Normalize()); + dot_product *= (*iter)->Intensity(); + + if (dot_product < 0.0) + { + if (pShapeToShade->Type() == "Triangle") + dot_product = dot_product*-1.0; + else + dot_product = 0.0; + } + intensity = unit_limiter( intensity + dot_product ); + + if ( light_ray.Direction().Dot(pShapeToShade->Normal(rPointOnShapeToShade, light_ray_from_light.Origin() )) >= 0.0 ) + { + double specular = Specular(pShapeToShade, rPointOnShapeToShade, *iter); + mSpecularColor = mSpecularColor + Color(specular,specular,specular); + } + } + + iter++; + } + + return intensity; +} + +double RayTracer::Specular(const Shape* pShapeToShade, const Point& rPointOnShapeToShade, const Light* pLight) +{ + Ray reflected = pShapeToShade->Reflect(rPointOnShapeToShade,Ray(rPointOnShapeToShade, pLight->LightPoint())); + + Vector eye_vector(rPointOnShapeToShade, mEyePoint); + Vector reflected_vector = reflected.Direction().Normalize(); + eye_vector.NormalizeThis(); + double dot_product = eye_vector.Dot(reflected_vector); + + int n = pShapeToShade->SpecularSize(); + double specular_intensity = dot_product/(n - n*dot_product+ dot_product); + return unit_limiter(specular_intensity*pLight->Intensity()); +} + +Shape* RayTracer::QueryScene(const Ray& rRay, Point& rIntersectionPoint, bool vIsReflecting, const Shape* pReflectingFrom) +{ + Shape* closest_shape = NULL; + Point intersect_point; + double closest_distance; + double intersect_distance; + bool found_intersection = false; + + ShapeListIterator iter = mpShapes->begin(); + while ( iter != mpShapes->end() ) + { + if ( (*iter)->Intersect( rRay, intersect_point ) ) + { + intersect_distance = intersect_point.Distance(rRay.Origin()); + if ( !found_intersection && (*iter) != pReflectingFrom) + { + found_intersection = true; + rIntersectionPoint = intersect_point; + closest_shape = *iter; + closest_distance = intersect_distance; + } + else if ( intersect_distance < closest_distance && (*iter) != pReflectingFrom ) + { + rIntersectionPoint = intersect_point; + closest_shape = *iter; + closest_distance = intersect_distance; + } + } + iter++; + } + + return closest_shape; +} + +void RayTracer::AddShape(Shape* pShape) +{ + // should check if a shape with the same name already exists + mpShapes->push_back(pShape); +} +void RayTracer::AddLight(Light* pLight) +{ + // should check if a shape with the same name already exists + mpLights->push_back(pLight); +} + +void RayTracer::BackgroundColor(const Color& rBackgroundColor) +{ + mBackgroundColor = rBackgroundColor; +} +void RayTracer::EyePoint(const Point& rEyePoint) +{ + mEyePoint = rEyePoint; +} +void RayTracer::AmbientColor(const Color& rAmbientColor) +{ + mAmbientColor = rAmbientColor; +} +void RayTracer::AmbientIntensity(double vAmbientIntensity) +{ + mAmbientIntensity = unit_limiter(vAmbientIntensity); +} + +} // namespace igraph diff --git a/src/rigraph/vendor/simpleraytracer/RayTracer.h b/src/rigraph/vendor/simpleraytracer/RayTracer.h new file mode 100644 index 0000000..cc4830d --- /dev/null +++ b/src/rigraph/vendor/simpleraytracer/RayTracer.h @@ -0,0 +1,63 @@ +/** RayTraceCanvas.h + */ + +#ifndef RAY_TRACER_H +#define RAY_TRACER_H + + +#include +#include "Point.h" +#include "Shape.h" +#include "Color.h" +#include "Light.h" + +namespace igraph { + +class Image +{ + public: + int width, height; + double *red, *green, *blue, *trans; +}; + +class RayTracer +{ + +public: + RayTracer(); + ~RayTracer(); + + void RayTrace(Image &result); + + void AddShape(Shape* pShape); + void AddLight(Light* pLight); + + void BackgroundColor(const Color& rBackgroundColor); + void EyePoint(const Point& rEyePoint); + void AmbientColor(const Color& rAmbient); + void AmbientIntensity(double vAmbientIntensity); + +private: + + Color Render(const Ray& rRay, bool vIsReflecting = false, const Shape* pReflectingFrom = 0 ); // vEyeRay should be true if the ray we are tracing is a ray from the eye, otherwise it should be false + Shape* QueryScene(const Ray& rRay, Point& rIntersectionPoint, bool vIsReflecting = false, const Shape* pReflectingFrom = 0); + double Shade(const Shape* pShapeToShade, const Point& rPointOnShapeToShade); + double Specular(const Shape* pShapeToShade, const Point& rPointOnShapeToShade, const Light* pLight); + + Color mBackgroundColor; + Color mAmbientColor; + Point mEyePoint; + Color mSpecularColor; + double mAmbientIntensity; + + ShapeList* mpShapes; + LightList* mpLights; + + int mRecursions; + int mRecursionLimit; + int mAntiAliasDetail; +}; + +} // namespace igraph + +#endif diff --git a/src/rigraph/vendor/simpleraytracer/RayVector.cpp b/src/rigraph/vendor/simpleraytracer/RayVector.cpp new file mode 100644 index 0000000..76f21aa --- /dev/null +++ b/src/rigraph/vendor/simpleraytracer/RayVector.cpp @@ -0,0 +1,128 @@ +#include "RayVector.h" +#include + +namespace igraph { + +Vector::Vector() +{ + mI = mJ = mK = 0.0; +} + +Vector::Vector(const Point& vStartPoint, const Point& vEndPoint) +{ + mI = vEndPoint.X() - vStartPoint.X(); + mJ = vEndPoint.Y() - vStartPoint.Y(); + mK = vEndPoint.Z() - vStartPoint.Z(); +} + +Vector::Vector(double vI, double vJ, double vK) +{ + mI = vI; + mJ = vJ; + mK = vK; +} + +Vector::~Vector() +{} + +// returns a unit vector of this vector +Vector Vector::Normalize() const +{ + double magnitude = Magnitude(); + return Vector(mI/magnitude, mJ/magnitude, mK/magnitude); +} + +void Vector::NormalizeThis() +{ + *this = Normalize(); +} + +void Vector::ReverseDirection() +{ + *this = *this * -1.0; +} + +bool Vector::IsSameDirection(const Vector& rVector) const +{ + return ( this->Normalize().Dot(rVector.Normalize()) > 0.0 ); +} + + +void Vector::I(double vI) +{ + mI = vI; +} + +double Vector::I() const +{ + return mI; +} + +void Vector::J(double vJ) +{ + mJ = vJ; +} + +double Vector::J() const +{ + return mJ; +} +void Vector::K(double vK) +{ + mK = vK; +} + +double Vector::K() const +{ + return mK; +} + +// returns the dot product of this and rVector +double Vector::Dot(const Vector& rVector) const +{ + return mI*rVector.I() + mJ*rVector.J() + mK*rVector.K(); +} + +// returns the cross product of this and vVector +Vector Vector::Cross(const Vector& rVector) const +{ + return Vector(mJ*rVector.K() - rVector.J()*mK, -1.0*(mI*rVector.K() - rVector.I()*mK), mI*rVector.J() - rVector.I()*mJ); +} + +// returns the sum of this vector with another vector +Vector Vector::operator+ (Vector vRhs) const +{ + return Vector(mI + vRhs.I(), mJ + vRhs.J(), mK + vRhs.K()); +} + +// returns the sume of a vector and a Point +Point Vector::operator+ (Point vRhs) const +{ + return Point(mI + vRhs.X(), mJ + vRhs.Y(), mK + vRhs.Z()); +} + +// returns the difference of two vectors +Vector Vector::operator- (Vector vRhs) const +{ + return Vector(mI-vRhs.I(), mJ-vRhs.J(), mK-vRhs.K()); +} + +// returns multiplication of a scalar with this vector +Vector Vector::operator* (double vRhs) const +{ + return Vector(mI*vRhs, mJ*vRhs, mK*vRhs); +} + +// converts this vector to a point +Point Vector::ToPoint() const +{ + return Point(mI,mJ,mK); +} + +// returns the magnitude +double Vector::Magnitude() const +{ + return sqrt(mI*mI + mJ*mJ + mK*mK); +} + +} // namespace igraph diff --git a/src/rigraph/vendor/simpleraytracer/RayVector.h b/src/rigraph/vendor/simpleraytracer/RayVector.h new file mode 100644 index 0000000..7157ba9 --- /dev/null +++ b/src/rigraph/vendor/simpleraytracer/RayVector.h @@ -0,0 +1,49 @@ +/** Vector.h + */ + +#ifndef VECTOR_H +#define VECTOR_H + +#include "Point.h" + +namespace igraph { + +class Vector +{ +public: + Vector(); + Vector(const Point& vStartPoint, const Point& vEndPoint); + Vector(double vI, double vJ, double vK); + ~Vector(); + + Vector Normalize() const; // returns a unit vector of this vector + void NormalizeThis(); + void ReverseDirection(); + bool IsSameDirection(const Vector& rVector) const; + + void I(double vI); + double I() const; + void J(double vJ); + double J() const; + void K(double vK); + double K() const; + + double Dot(const Vector& rVector) const; // returns the dot product of this and rVector + Vector Cross(const Vector& rVector) const; // returns the cross product of this and rVector + Vector operator+ (Vector vRhs) const; // returns the sum of two vectors + Vector operator- (Vector vRhs) const; // returns the difference of two vectors + Point operator+ (Point vRhs) const; // returns the sum of a vector and a Point + Vector operator* (double vRhs) const; // returns multiplication of a scalar with a vector + Point ToPoint() const; // converts a vector to a point + + double Magnitude() const; + +private: + + + double mI, mJ, mK; +}; + +} // namespace igraph + +#endif diff --git a/src/rigraph/vendor/simpleraytracer/Shape.cpp b/src/rigraph/vendor/simpleraytracer/Shape.cpp new file mode 100644 index 0000000..3ca2b14 --- /dev/null +++ b/src/rigraph/vendor/simpleraytracer/Shape.cpp @@ -0,0 +1,106 @@ +#include "Shape.h" +#include "unit_limiter.h" + +namespace igraph { + +Shape::Shape() +{ + mName = 0; + mAmbientReflectivity = .6; + mSpecularReflectivity = 0; + mDiffuseReflectivity = 0; + mSpecularSize = 64; +} + +Shape::~Shape() +{} + +int Shape::Name() const +{ + return mName; +} + +void Shape::Name(int vName) +{ + mName = vName; +} + +const Color& Shape::ShapeColor() const +{ + return mShapeColor; +} + +void Shape::ShapeColor(const Color& rColor) +{ + mShapeColor = rColor; +} + +double Shape::AmbientReflectivity() const +{ + return mAmbientReflectivity; +} +double Shape::SpecularReflectivity() const +{ + return mSpecularReflectivity; +} +double Shape::DiffuseReflectivity() const +{ + return mDiffuseReflectivity; +} + +void Shape::AmbientReflectivity(double rReflectivity) +{ + mAmbientReflectivity = unit_limiter(rReflectivity); +} +void Shape::SpecularReflectivity(double rReflectivity) +{ + mSpecularReflectivity = unit_limiter(rReflectivity); +} +void Shape::DiffuseReflectivity(double rReflectivity) +{ + mDiffuseReflectivity = unit_limiter(rReflectivity); +} + +Ray Shape::Reflect(const Point& rReflectFrom, const Ray& rIncidentRay) const +{ + Ray result; // the reflected ray + Vector result_direction; // the reflected direction vector + Vector incident_unit = rIncidentRay.Direction().Normalize(); + Vector normal = this->Normal(rReflectFrom, rIncidentRay.Origin() ); + if ( !normal.IsSameDirection(incident_unit) ) + normal.ReverseDirection(); // we want the normal in the same direction of the incident ray. + + result.Origin(rReflectFrom); + result.Direction( normal*2.0*normal.Dot(incident_unit) - incident_unit ); +/* + if ( normal.Dot(rIncidentRay.Direction().Normalize()) < 0.0 ) + normal.ReverseDirection(); + + result.Origin(rReflectFrom); + result.Direction((normal*2.0) - rIncidentRay.Direction().Normalize()); +*/ + + return result; +} + +const string& Shape::Type() const +{ + return mType; +} + +void Shape::Type(const string& rType) +{ + mType = rType; +} + +int Shape::SpecularSize() const +{ + return mSpecularSize; +} + +void Shape::SpecularSize(int vSpecularSize) +{ + mSpecularSize = vSpecularSize; +} + +} // namespace igraph diff --git a/src/rigraph/vendor/simpleraytracer/Shape.h b/src/rigraph/vendor/simpleraytracer/Shape.h new file mode 100644 index 0000000..fc9c7c1 --- /dev/null +++ b/src/rigraph/vendor/simpleraytracer/Shape.h @@ -0,0 +1,65 @@ +/** Shape.h + */ + +#ifndef SHAPE_H +#define SHAPE_H + +#include +#include "Color.h" +#include "Ray.h" +#include "Point.h" + +#include +using namespace std; + +namespace igraph { + +class Shape +{ +public: + Shape(); + virtual ~Shape(); + + virtual bool Intersect(const Ray& rRay, Point& rIntersectPoint) const = 0; + virtual Vector Normal(const Point& rSurfacePoint, const Point& rOffSurface) const = 0; + // returns a normalized vector that is the normal of this shape from the surface point + // it also takes the rOffSurface point into account, for example: + // if rSurfacePoint is on top of a triangle, then the normal returned will be going up. + + Ray Reflect(const Point& rReflectFrom, const Ray& rRay) const; + + void Name(int vName); + int Name() const; + + const Color& ShapeColor() const; + void ShapeColor(const Color& rColor); + + double SpecularReflectivity() const; + void SpecularReflectivity(double rReflectivity); + double DiffuseReflectivity() const; + void DiffuseReflectivity(double rReflectivity); + double AmbientReflectivity() const; + void AmbientReflectivity(double rReflectivity); + + int SpecularSize() const; + void SpecularSize(int vSpecularSize); + + const string& Type() const; + void Type(const string& rType); + +private: + int mName; + string mType; + Color mShapeColor; + double mSpecularReflectivity; // from 0 to 1 + int mSpecularSize; // 1 to 64 + double mDiffuseReflectivity; // from 0 to 1 + double mAmbientReflectivity; // from 0 to 1 +}; + +typedef list ShapeList; +typedef list::iterator ShapeListIterator; + +} // namespace igraph + +#endif diff --git a/src/rigraph/vendor/simpleraytracer/Sphere.cpp b/src/rigraph/vendor/simpleraytracer/Sphere.cpp new file mode 100644 index 0000000..c449b80 --- /dev/null +++ b/src/rigraph/vendor/simpleraytracer/Sphere.cpp @@ -0,0 +1,70 @@ +#include "Sphere.h" +#include + +namespace igraph { + +Sphere::Sphere() +{} + +Sphere::Sphere(Point vCenter, double vRadius) +{ + Type("Sphere"); + mCenter = vCenter; + mRadius = vRadius; +} + +Sphere::~Sphere() +{ +} + +bool Sphere::Intersect(const Ray& vRay, Point& vIntersectPoint) const +{ + Vector V; + Vector EO(vRay.Origin(), mCenter); + double v; + double disc; + double d; + Vector E(Point(0,0,0), vRay.Origin()); // E = vector from origin to ray origin + Vector P; + mCenter.Distance(vRay.Origin()); //c = distance from eye to center of sphere + V = vRay.Direction(); + V.NormalizeThis(); + v = EO.Dot(V); + double v2 = V.Dot(EO.Normalize()); + if (v2 >= 0.0) + { + disc = mRadius*mRadius - (EO.Dot(EO) - v*v); + if (disc <= 0) + return false; + else + { + d = sqrt(disc); + P = E + V*(v-d); + vIntersectPoint = P.ToPoint(); + return true; + } + } + else + return false; +} + +Vector Sphere::Normal(const Point& rSurfacePoint, const Point& rOffSurface) const +{ + // currently does not take rOffSurface point into account, + // it should check if this point is inside the sphere, if it is + // return a normal facing the center. + + Vector radius_vector (mCenter, rSurfacePoint); + return (radius_vector.Normalize()); +} + +double Sphere::Radius() const +{ + return mRadius; +} +const Point& Sphere::Center() const +{ + return mCenter; +} + +} // namespace igraph diff --git a/src/rigraph/vendor/simpleraytracer/Sphere.h b/src/rigraph/vendor/simpleraytracer/Sphere.h new file mode 100644 index 0000000..faff6da --- /dev/null +++ b/src/rigraph/vendor/simpleraytracer/Sphere.h @@ -0,0 +1,31 @@ +/** Sphere.h +*/ + +#ifndef SPHERE_H +#define SPHERE_H + +#include "Shape.h" + +namespace igraph { + +class Sphere : public Shape +{ +public: + Sphere(); + Sphere(Point vCenter, double vRadius); + ~Sphere(); + + virtual bool Intersect(const Ray& vRay, Point& vIntersectPoint) const; + virtual Vector Normal(const Point& rSurfacePoint, const Point& rOffSurface) const; + + double Radius() const; + const Point& Center() const; + +private: + Point mCenter; + double mRadius; +}; + +} // namespace igraph + +#endif diff --git a/src/rigraph/vendor/simpleraytracer/Triangle.cpp b/src/rigraph/vendor/simpleraytracer/Triangle.cpp new file mode 100644 index 0000000..7946c26 --- /dev/null +++ b/src/rigraph/vendor/simpleraytracer/Triangle.cpp @@ -0,0 +1,90 @@ +#include "Triangle.h" +#include + +namespace igraph { + +Triangle::Triangle() +{} + +Triangle::Triangle(const Point& rPoint1, const Point& rPoint2, const Point& rPoint3) +{ + Type("Triangle"); + mPoint1 = rPoint1; + mPoint2 = rPoint2; + mPoint3 = rPoint3; +} + +Triangle::~Triangle() +{ +} + +bool Triangle::Intersect(const Ray& vRay, Point& rIntersectPoint) const +{ + + Vector pointb_minus_pointa (mPoint1, mPoint2); + Vector pointb_minus_pointc (mPoint1, mPoint3); +/* + Vector plane_normal = pointb_minus_pointa.Cross(pointb_minus_pointc); + + // get the plane normal facing the right way: + Vector plane_normal_normalized = plane_normal.Normalize(); + Vector triangle_to_ray_origin = Vector(mPoint1, vRay.Origin() ); + triangle_to_ray_origin.NormalizeThis(); + if ( plane_normal_normalized.Dot(triangle_to_ray_origin) < 0.0 ) + { + plane_normal = plane_normal * -1.0; + plane_normal_normalized = plane_normal_normalized * -1.0; + } + + // check that the ray is actually facing the triangle + Vector ray_direction_normalized = vRay.Direction().Normalize(); + if ( plane_normal_normalized.Dot(ray_direction_normalized) > 0.0 ) + return false; +*/ + Vector plane_normal = this->Normal(mPoint1, vRay.Origin()); + Vector ray_direction_normalized = vRay.Direction().Normalize(); + if ( plane_normal.IsSameDirection(ray_direction_normalized) ) + return false; + + Vector b_minus_u (vRay.Origin(), mPoint2); + + double t = plane_normal.Dot(b_minus_u) / plane_normal.Dot(vRay.Direction()); + Point p = (vRay.Direction() * t) + vRay.Origin(); + + Vector p_minus_a (mPoint1, p); + Vector p_minus_b (mPoint2, p); + Vector p_minus_c (mPoint3, p); + Vector pointc_minus_pointb (mPoint2, mPoint3); + Vector pointa_minus_pointc (mPoint3, mPoint1); + + double test1 = (pointb_minus_pointa.Cross(p_minus_a)).Dot(plane_normal); + double test2 = (pointc_minus_pointb.Cross(p_minus_b)).Dot(plane_normal); + double test3 = (pointa_minus_pointc.Cross(p_minus_c)).Dot(plane_normal); + + if ((test1 > 0 && test2 > 0 && test3 > 0) || (test1 < 0 && test2 < 0 && test3 < 0)) + { + rIntersectPoint = p; + return true; + } + else + return false; +} + +Vector Triangle::Normal(const Point& rSurfacePoint, const Point& rOffSurface) const +{ + Vector pointb_minus_pointa (mPoint1, mPoint2); + Vector pointb_minus_pointc (mPoint1, mPoint3); + Vector plane_normal = pointb_minus_pointa.Cross(pointb_minus_pointc).Normalize(); + + // get the plane normal facing the right way: + Vector triangle_to_off_surface_point = Vector(mPoint1, rOffSurface ); + triangle_to_off_surface_point.NormalizeThis(); + if ( !plane_normal.IsSameDirection(triangle_to_off_surface_point) ) + { + plane_normal.ReverseDirection(); + } + + return plane_normal; +} + +} // namespace igraph diff --git a/src/rigraph/vendor/simpleraytracer/Triangle.h b/src/rigraph/vendor/simpleraytracer/Triangle.h new file mode 100644 index 0000000..198d241 --- /dev/null +++ b/src/rigraph/vendor/simpleraytracer/Triangle.h @@ -0,0 +1,27 @@ +/** Triangle.h +*/ + +#ifndef TRIANGLE_H +#define TRIANGLE_H + +#include "Shape.h" + +namespace igraph { + +class Triangle : public Shape +{ +public: + Triangle(); + Triangle(const Point& rPoint1, const Point& rPoint2, const Point& rPoint3); + ~Triangle(); + + virtual bool Intersect(const Ray& vRay, Point& vIntersectPoint) const; + virtual Vector Normal(const Point& rSurfacePoint, const Point& rOffSurface) const; + +private: + Point mPoint1, mPoint2, mPoint3; +}; + +} // namespace igraph + +#endif diff --git a/src/rigraph/vendor/simpleraytracer/unit_limiter.cpp b/src/rigraph/vendor/simpleraytracer/unit_limiter.cpp new file mode 100644 index 0000000..75a59da --- /dev/null +++ b/src/rigraph/vendor/simpleraytracer/unit_limiter.cpp @@ -0,0 +1,15 @@ +#include "unit_limiter.h" + +namespace igraph { + +double unit_limiter(double vUnitDouble) +{ + double result = vUnitDouble; + if (result < 0.0) + result = 0.0; + else if (result > 1.0) + result = 1.0; + return result; +} + +} // namespace igraph diff --git a/src/rigraph/vendor/simpleraytracer/unit_limiter.h b/src/rigraph/vendor/simpleraytracer/unit_limiter.h new file mode 100644 index 0000000..9c6f61b --- /dev/null +++ b/src/rigraph/vendor/simpleraytracer/unit_limiter.h @@ -0,0 +1,10 @@ +#ifndef ZERO_TO_ONE_H +#define ZERO_TO_ONE_H + +namespace igraph { + +double unit_limiter(double vUnitDouble); + +} // namespace igraph + +#endif diff --git a/src/rigraph/vendor/uuid/COPYING b/src/rigraph/vendor/uuid/COPYING new file mode 100644 index 0000000..e7a8054 --- /dev/null +++ b/src/rigraph/vendor/uuid/COPYING @@ -0,0 +1,28 @@ +This library is free software; you can redistribute it and/or +modify it under the terms of the Modified BSD License: + +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, and the entire permission notice in its entirety, + including the disclaimer of warranties. +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. The name of the author may not be used to endorse or promote + products derived from this software without specific prior + written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF +WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR 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 NOT ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. diff --git a/src/rigraph/vendor/uuid/Makevars.in b/src/rigraph/vendor/uuid/Makevars.in new file mode 100644 index 0000000..0ab302b --- /dev/null +++ b/src/rigraph/vendor/uuid/Makevars.in @@ -0,0 +1,2 @@ +PKG_CPPFLAGS=@CPPFLAGS@ +PKG_LIBS=@LIBS@ diff --git a/src/rigraph/vendor/uuid/Makevars.win b/src/rigraph/vendor/uuid/Makevars.win new file mode 100644 index 0000000..248e1ce --- /dev/null +++ b/src/rigraph/vendor/uuid/Makevars.win @@ -0,0 +1 @@ +PKG_CPPFLAGS=-Iwin32 diff --git a/src/rigraph/vendor/uuid/R.c b/src/rigraph/vendor/uuid/R.c new file mode 100644 index 0000000..b956b8d --- /dev/null +++ b/src/rigraph/vendor/uuid/R.c @@ -0,0 +1,25 @@ +#include "uuid.h" + +#include +#include "igraph_random.h" + +SEXP UUID_gen(SEXP sTime) { + + RNG_BEGIN(); + + uuid_t u; + char c[40]; + int use_time = asInteger(sTime); + if (use_time == TRUE) + uuid_generate_time(u); + else if (use_time == FALSE) + uuid_generate_random(u); + else + uuid_generate(u); + uuid_unparse_lower(u, c); + + RNG_END(); + + return mkString(c); +} + diff --git a/src/rigraph/vendor/uuid/clear.c b/src/rigraph/vendor/uuid/clear.c new file mode 100644 index 0000000..ad1f066 --- /dev/null +++ b/src/rigraph/vendor/uuid/clear.c @@ -0,0 +1,43 @@ +/* + * clear.c -- Clear a UUID + * + * Copyright (C) 1996, 1997 Theodore Ts'o. + * + * %Begin-Header% + * 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, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 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. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF + * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR 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 NOT ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * %End-Header% + */ + +#include + +#include "uuidP.h" + +void uuid_clear(uuid_t uu) +{ + memset(uu, 0, 16); +} + diff --git a/src/rigraph/vendor/uuid/compare.c b/src/rigraph/vendor/uuid/compare.c new file mode 100644 index 0000000..8f3437a --- /dev/null +++ b/src/rigraph/vendor/uuid/compare.c @@ -0,0 +1,55 @@ +/* + * compare.c --- compare whether or not two UUIDs are the same + * + * Returns 0 if the two UUIDs are different, and 1 if they are the same. + * + * Copyright (C) 1996, 1997 Theodore Ts'o. + * + * %Begin-Header% + * 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, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 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. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF + * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR 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 NOT ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * %End-Header% + */ + +#include "uuidP.h" +#include + +#define UUCMP(u1,u2) if (u1 != u2) return((u1 < u2) ? -1 : 1); + +int uuid_compare(const uuid_t uu1, const uuid_t uu2) +{ + struct uuid uuid1, uuid2; + + uuid_unpack(uu1, &uuid1); + uuid_unpack(uu2, &uuid2); + + UUCMP(uuid1.time_low, uuid2.time_low); + UUCMP(uuid1.time_mid, uuid2.time_mid); + UUCMP(uuid1.time_hi_and_version, uuid2.time_hi_and_version); + UUCMP(uuid1.clock_seq, uuid2.clock_seq); + return memcmp(uuid1.node, uuid2.node, 6); +} + diff --git a/src/rigraph/vendor/uuid/config.h.in b/src/rigraph/vendor/uuid/config.h.in new file mode 100644 index 0000000..b6f72b3 --- /dev/null +++ b/src/rigraph/vendor/uuid/config.h.in @@ -0,0 +1,82 @@ +/* src/config.h.in. Generated from configure.ac by autoheader. */ + +/* Define to 1 if you have the header file. */ +#undef HAVE_INTTYPES_H + +/* Define to 1 if you have the `jrand48' function. */ +#undef HAVE_JRAND48 + +/* Define to 1 if you have the header file. */ +#undef HAVE_MEMORY_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_NETINET_IN_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_NET_IF_DL_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_NET_IF_H + +/* Define if struct sockaddr contains sa_len */ +#undef HAVE_SA_LEN + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDINT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDLIB_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRINGS_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRING_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_FILE_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_IOCTL_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_SOCKET_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_SOCKIO_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_STAT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_SYSCALL_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_TIME_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_TYPES_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_UN_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_UNISTD_H + +/* Define to the address where bug reports for this package should be sent. */ +#undef PACKAGE_BUGREPORT + +/* Define to the full name of this package. */ +#undef PACKAGE_NAME + +/* Define to the full name and version of this package. */ +#undef PACKAGE_STRING + +/* Define to the one symbol short name of this package. */ +#undef PACKAGE_TARNAME + +/* Define to the version of this package. */ +#undef PACKAGE_VERSION + +/* Define to 1 if you have the ANSI C header files. */ +#undef STDC_HEADERS diff --git a/src/rigraph/vendor/uuid/copy.c b/src/rigraph/vendor/uuid/copy.c new file mode 100644 index 0000000..ead33aa --- /dev/null +++ b/src/rigraph/vendor/uuid/copy.c @@ -0,0 +1,45 @@ +/* + * copy.c --- copy UUIDs + * + * Copyright (C) 1996, 1997 Theodore Ts'o. + * + * %Begin-Header% + * 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, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 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. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF + * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR 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 NOT ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * %End-Header% + */ + +#include "uuidP.h" + +void uuid_copy(uuid_t dst, const uuid_t src) +{ + unsigned char *cp1; + const unsigned char *cp2; + int i; + + for (i=0, cp1 = dst, cp2 = src; i < 16; i++) + *cp1++ = *cp2++; +} diff --git a/src/rigraph/vendor/uuid/gen_uuid.c b/src/rigraph/vendor/uuid/gen_uuid.c new file mode 100644 index 0000000..4bc6143 --- /dev/null +++ b/src/rigraph/vendor/uuid/gen_uuid.c @@ -0,0 +1,536 @@ +/* + * gen_uuid.c --- generate a DCE-compatible uuid + * + * Copyright (C) 1996, 1997, 1998, 1999 Theodore Ts'o. + * + * %Begin-Header% + * 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, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 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. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF + * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR 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 NOT ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * %End-Header% + */ + +/* + * Force inclusion of SVID stuff since we need it if we're compiling in + * gcc-wall wall mode + */ +#define _DEFAULT_SOURCE + +#include "config.h" + +#ifdef _WIN32 +#define _WIN32_WINNT 0x0500 +#include +#define UUID MYUUID +#endif +#include +#ifdef HAVE_UNISTD_H +#include +#endif +#ifdef HAVE_STDLIB_H +#include +#endif +#include +#include +#include +#include +#include +#ifdef HAVE_SYS_TIME_H +#include +#endif +#include +#ifdef HAVE_SYS_FILE_H +#include +#endif +#ifdef HAVE_SYS_IOCTL_H +#include +#endif +#ifdef HAVE_SYS_SOCKET_H +#include +#endif +#ifdef HAVE_SYS_UN_H +#include +#endif +#ifdef HAVE_SYS_SOCKIO_H +#include +#endif +#ifdef HAVE_NET_IF_H +#include +#endif +#ifdef HAVE_NETINET_IN_H +#include +#endif +#ifdef HAVE_NET_IF_DL_H +#include +#endif +#if defined(__linux__) && defined(HAVE_SYS_SYSCALL_H) +#include +#endif + +#include "uuidP.h" +#include "uuidd.h" + +#ifdef USING_R +#include "igraph_random.h" +#define srand(x) ; +#define rand() RNG_INTEGER(0, RAND_MAX) +#endif + +#ifdef HAVE_TLS +#define THREAD_LOCAL static __thread +#else +#define THREAD_LOCAL static +#endif + +#ifdef _WIN32 +#if 0 /* MinGW has gettimeofday so we don't need this */ +static int gettimeofday (struct timeval *tv, void *dummy) +{ + FILETIME ftime; + uint64_t n; + + GetSystemTimeAsFileTime (&ftime); + n = (((uint64_t) ftime.dwHighDateTime << 32) + + (uint64_t) ftime.dwLowDateTime); + if (n) { + n /= 10; + n -= ((369 * 365 + 89) * (uint64_t) 86400) * 1000000; + } + + tv->tv_sec = n / 1000000; + tv->tv_usec = n % 1000000; +} +#endif + +#ifdef __MINGW32__ +int gettimeofday (struct timeval *tv, void *dummy); +#endif +#ifdef __MINGW64__ +int gettimeofday (struct timeval *tv, void *dummy); +#endif + +static int getuid (void) +{ + return 1; +} +#endif + +/* + * Get the ethernet hardware address, if we can find it... + * + * XXX for a windows version, probably should use GetAdaptersInfo: + * http://www.codeguru.com/cpp/i-n/network/networkinformation/article.php/c5451 + * commenting out get_node_id just to get gen_uuid to compile under windows + * is not the right way to go! + */ +static int get_node_id(unsigned char *node_id) +{ +#ifdef HAVE_NET_IF_H + int sd; + struct ifreq ifr, *ifrp; + struct ifconf ifc; + char buf[1024]; + int n, i; + unsigned char *a; +#ifdef HAVE_NET_IF_DL_H + struct sockaddr_dl *sdlp; +#endif + +/* + * BSD 4.4 defines the size of an ifreq to be + * max(sizeof(ifreq), sizeof(ifreq.ifr_name)+ifreq.ifr_addr.sa_len + * However, under earlier systems, sa_len isn't present, so the size is + * just sizeof(struct ifreq) + */ +#ifdef HAVE_SA_LEN +#define max(x, y) (((x) > (y)) ? (x) : (y)) +#define ifreq_size(i) max(sizeof(struct ifreq),\ + sizeof((i).ifr_name)+(i).ifr_addr.sa_len) +#else +#define ifreq_size(i) sizeof(struct ifreq) +#endif /* HAVE_SA_LEN */ + + sd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); + if (sd < 0) { + return -1; + } + memset(buf, 0, sizeof(buf)); + ifc.ifc_len = sizeof(buf); + ifc.ifc_buf = buf; + if (ioctl (sd, SIOCGIFCONF, (char *)&ifc) < 0) { + close(sd); + return -1; + } + n = ifc.ifc_len; + for (i = 0; i < n; i+= ifreq_size(*ifrp) ) { + ifrp = (struct ifreq *)((char *) ifc.ifc_buf+i); + strncpy(ifr.ifr_name, ifrp->ifr_name, IFNAMSIZ); +#if defined(SIOCGIFHWADDR) && (!defined(__sun__)) + if (ioctl(sd, SIOCGIFHWADDR, &ifr) < 0) + continue; + a = (unsigned char *) &ifr.ifr_hwaddr.sa_data; +#else +#ifdef SIOCGENADDR + if (ioctl(sd, SIOCGENADDR, &ifr) < 0) + continue; + a = (unsigned char *) ifr.ifr_enaddr; +#else +#ifdef HAVE_NET_IF_DL_H + sdlp = (struct sockaddr_dl *) &ifrp->ifr_addr; + if ((sdlp->sdl_family != AF_LINK) || (sdlp->sdl_alen != 6)) + continue; + a = (unsigned char *) &sdlp->sdl_data[sdlp->sdl_nlen]; +#else + /* + * XXX we don't have a way of getting the hardware + * address + */ + close(sd); + return 0; +#endif /* HAVE_NET_IF_DL_H */ +#endif /* SIOCGENADDR */ +#endif /* SIOCGIFHWADDR */ + if (!a[0] && !a[1] && !a[2] && !a[3] && !a[4] && !a[5]) + continue; + if (node_id) { + memcpy(node_id, a, 6); + close(sd); + return 1; + } + } + close(sd); +#endif + return 0; +} + +#if defined(__linux__) && defined(__NR_gettid) && defined(HAVE_JRAND48) +#define DO_JRAND_MIX +static unsigned short ul_jrand_seed[3]; +#endif + +static int random_get_fd(void) +{ + int i, fd = -1; + struct timeval tv; + + gettimeofday(&tv, NULL); +#ifndef _WIN32 + fd = open("/dev/urandom", O_RDONLY); + if (fd == -1) + fd = open("/dev/random", O_RDONLY | O_NONBLOCK); + if (fd >= 0) { + i = fcntl(fd, F_GETFD); + if (i >= 0) + fcntl(fd, F_SETFD, i | FD_CLOEXEC); + } +#endif + srand((getpid() << 16) ^ getuid() ^ tv.tv_sec ^ tv.tv_usec); + +#ifdef DO_JRAND_MIX + ul_jrand_seed[0] = getpid() ^ (tv.tv_sec & 0xFFFF); + ul_jrand_seed[1] = getppid() ^ (tv.tv_usec & 0xFFFF); + ul_jrand_seed[2] = (tv.tv_sec ^ tv.tv_usec) >> 16; +#endif + /* Crank the random number generator a few times */ + gettimeofday(&tv, NULL); + for (i = (tv.tv_sec ^ tv.tv_usec) & 0x1F; i > 0; i--) + rand(); + return fd; +} + +/* + * Generate a stream of random nbytes into buf. + * Use /dev/urandom if possible, and if not, + * use glibc pseudo-random functions. + */ +static void random_get_bytes(void *buf, size_t nbytes) +{ + size_t i, n = nbytes; + int fd = random_get_fd(); + int lose_counter = 0; + unsigned char *cp = (unsigned char *) buf; + + if (fd >= 0) { + while (n > 0) { + ssize_t x = read(fd, cp, n); + if (x <= 0) { + if (lose_counter++ > 16) + break; + continue; + } + n -= x; + cp += x; + lose_counter = 0; + } + + close(fd); + } + + /* + * We do this all the time, but this is the only source of + * randomness if /dev/random/urandom is out to lunch. + */ + for (cp = buf, i = 0; i < nbytes; i++) + *cp++ ^= (rand() >> 7) & 0xFF; + +#ifdef DO_JRAND_MIX + { + unsigned short tmp_seed[3]; + + memcpy(tmp_seed, ul_jrand_seed, sizeof(tmp_seed)); + ul_jrand_seed[2] = ul_jrand_seed[2] ^ syscall(__NR_gettid); + for (cp = buf, i = 0; i < nbytes; i++) + *cp++ ^= (jrand48(tmp_seed) >> 7) & 0xFF; + memcpy(ul_jrand_seed, tmp_seed, + sizeof(ul_jrand_seed)-sizeof(unsigned short)); + } +#endif + + return; +} + +/* Assume that the gettimeofday() has microsecond granularity */ +#define MAX_ADJUSTMENT 10 + +/* + * Get clock from global sequence clock counter. + * + * Return -1 if the clock counter could not be opened/locked (in this case + * pseudorandom value is returned in @ret_clock_seq), otherwise return 0. + */ +static int get_clock(uint32_t *clock_high, uint32_t *clock_low, + uint16_t *ret_clock_seq, int *num) +{ + THREAD_LOCAL int adjustment = 0; + THREAD_LOCAL struct timeval last = {0, 0}; + THREAD_LOCAL int state_fd = -2; + THREAD_LOCAL FILE *state_f; + THREAD_LOCAL uint16_t clock_seq; + struct timeval tv; + uint64_t clock_reg; + mode_t save_umask; + int len; + int ret = 0; + + if (state_fd == -2) { + save_umask = umask(0); + state_fd = open(LIBUUID_CLOCK_FILE, O_RDWR|O_CREAT, 0660); + (void) umask(save_umask); + if (state_fd != -1) { + state_f = fdopen(state_fd, "r+"); + if (!state_f) { + close(state_fd); + state_fd = -1; + ret = -1; + } + } + else + ret = -1; + } + if (state_fd >= 0) { + rewind(state_f); + } + if (state_fd >= 0) { + unsigned int cl; + unsigned long tv1, tv2; + int a; + + if (fscanf(state_f, "clock: %04x tv: %lu %lu adj: %d\n", + &cl, &tv1, &tv2, &a) == 4) { + clock_seq = cl & 0x3FFF; + last.tv_sec = tv1; + last.tv_usec = tv2; + adjustment = a; + } + } + + if ((last.tv_sec == 0) && (last.tv_usec == 0)) { + random_get_bytes(&clock_seq, sizeof(clock_seq)); + clock_seq &= 0x3FFF; + gettimeofday(&last, NULL); + last.tv_sec--; + } + +try_again: + gettimeofday(&tv, NULL); + if ((tv.tv_sec < last.tv_sec) || + ((tv.tv_sec == last.tv_sec) && + (tv.tv_usec < last.tv_usec))) { + clock_seq = (clock_seq+1) & 0x3FFF; + adjustment = 0; + last = tv; + } else if ((tv.tv_sec == last.tv_sec) && + (tv.tv_usec == last.tv_usec)) { + if (adjustment >= MAX_ADJUSTMENT) + goto try_again; + adjustment++; + } else { + adjustment = 0; + last = tv; + } + + clock_reg = tv.tv_usec*10 + adjustment; + clock_reg += ((uint64_t) tv.tv_sec)*10000000; + clock_reg += (((uint64_t) 0x01B21DD2) << 32) + 0x13814000; + + if (num && (*num > 1)) { + adjustment += *num - 1; + last.tv_usec += adjustment / 10; + adjustment = adjustment % 10; + last.tv_sec += last.tv_usec / 1000000; + last.tv_usec = last.tv_usec % 1000000; + } + + if (state_fd >= 0) { + rewind(state_f); + len = fprintf(state_f, + "clock: %04x tv: %016lu %08lu adj: %08d\n", + clock_seq, (unsigned long) last.tv_sec, (unsigned long) last.tv_usec, adjustment); + fflush(state_f); + if (ftruncate(state_fd, len) < 0) { + fprintf(state_f, " \n"); + fflush(state_f); + } + rewind(state_f); + } + + *clock_high = clock_reg >> 32; + *clock_low = clock_reg; + *ret_clock_seq = clock_seq; + return ret; +} + + +int __uuid_generate_time(uuid_t out, int *num) +{ + static unsigned char node_id[6]; + static int has_init = 0; + struct uuid uu; + uint32_t clock_mid; + int ret; + + if (!has_init) { + if (get_node_id(node_id) <= 0) { + random_get_bytes(node_id, 6); + /* + * Set multicast bit, to prevent conflicts + * with IEEE 802 addresses obtained from + * network cards + */ + node_id[0] |= 0x01; + } + has_init = 1; + } + ret = get_clock(&clock_mid, &uu.time_low, &uu.clock_seq, num); + uu.clock_seq |= 0x8000; + uu.time_mid = (uint16_t) clock_mid; + uu.time_hi_and_version = ((clock_mid >> 16) & 0x0FFF) | 0x1000; + memcpy(uu.node, node_id, 6); + uuid_pack(&uu, out); + return ret; +} + +/* + * Generate time-based UUID and store it to @out + * + * Since there is no daemon here, use fall-back right away + */ +static int uuid_generate_time_generic(uuid_t out) { + return __uuid_generate_time(out, 0); +} + +/* + * Generate time-based UUID and store it to @out. + * + * Discards return value from uuid_generate_time_generic() + */ +void uuid_generate_time(uuid_t out) +{ + (void)uuid_generate_time_generic(out); +} + + +int uuid_generate_time_safe(uuid_t out) +{ + return uuid_generate_time_generic(out); +} + + +void __uuid_generate_random(uuid_t out, int *num) +{ + uuid_t buf; + struct uuid uu; + int i, n; + + if (!num || !*num) + n = 1; + else + n = *num; + + for (i = 0; i < n; i++) { + random_get_bytes(buf, sizeof(buf)); + uuid_unpack(buf, &uu); + + uu.clock_seq = (uu.clock_seq & 0x3FFF) | 0x8000; + uu.time_hi_and_version = (uu.time_hi_and_version & 0x0FFF) + | 0x4000; + uuid_pack(&uu, out); + out += sizeof(uuid_t); + } +} + +void uuid_generate_random(uuid_t out) +{ + int num = 1; + /* No real reason to use the daemon for random uuid's -- yet */ + + __uuid_generate_random(out, &num); +} + +/* + * Check whether good random source (/dev/random or /dev/urandom) + * is available. + */ +static int have_random_source(void) +{ + struct stat s; + + return (!stat("/dev/random", &s) || !stat("/dev/urandom", &s)); +} + + +/* + * This is the generic front-end to uuid_generate_random and + * uuid_generate_time. It uses uuid_generate_random only if + * /dev/urandom is available, since otherwise we won't have + * high-quality randomness. + */ +void uuid_generate(uuid_t out) +{ + if (have_random_source()) + uuid_generate_random(out); + else + uuid_generate_time(out); +} diff --git a/src/rigraph/vendor/uuid/isnull.c b/src/rigraph/vendor/uuid/isnull.c new file mode 100644 index 0000000..931e7e7 --- /dev/null +++ b/src/rigraph/vendor/uuid/isnull.c @@ -0,0 +1,48 @@ +/* + * isnull.c --- Check whether or not the UUID is null + * + * Copyright (C) 1996, 1997 Theodore Ts'o. + * + * %Begin-Header% + * 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, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 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. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF + * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR 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 NOT ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * %End-Header% + */ + +#include "uuidP.h" + +/* Returns 1 if the uuid is the NULL uuid */ +int uuid_is_null(const uuid_t uu) +{ + const unsigned char *cp; + int i; + + for (i=0, cp = uu; i < 16; i++) + if (*cp++) + return 0; + return 1; +} + diff --git a/src/rigraph/vendor/uuid/pack.c b/src/rigraph/vendor/uuid/pack.c new file mode 100644 index 0000000..6e12476 --- /dev/null +++ b/src/rigraph/vendor/uuid/pack.c @@ -0,0 +1,69 @@ +/* + * Internal routine for packing UUIDs + * + * Copyright (C) 1996, 1997 Theodore Ts'o. + * + * %Begin-Header% + * 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, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 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. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF + * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR 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 NOT ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * %End-Header% + */ + +#include +#include "uuidP.h" + +void uuid_pack(const struct uuid *uu, uuid_t ptr) +{ + uint32_t tmp; + unsigned char *out = ptr; + + tmp = uu->time_low; + out[3] = (unsigned char) tmp; + tmp >>= 8; + out[2] = (unsigned char) tmp; + tmp >>= 8; + out[1] = (unsigned char) tmp; + tmp >>= 8; + out[0] = (unsigned char) tmp; + + tmp = uu->time_mid; + out[5] = (unsigned char) tmp; + tmp >>= 8; + out[4] = (unsigned char) tmp; + + tmp = uu->time_hi_and_version; + out[7] = (unsigned char) tmp; + tmp >>= 8; + out[6] = (unsigned char) tmp; + + tmp = uu->clock_seq; + out[9] = (unsigned char) tmp; + tmp >>= 8; + out[8] = (unsigned char) tmp; + + memcpy(out+10, uu->node, 6); +} + diff --git a/src/rigraph/vendor/uuid/parse.c b/src/rigraph/vendor/uuid/parse.c new file mode 100644 index 0000000..074383e --- /dev/null +++ b/src/rigraph/vendor/uuid/parse.c @@ -0,0 +1,79 @@ +/* + * parse.c --- UUID parsing + * + * Copyright (C) 1996, 1997 Theodore Ts'o. + * + * %Begin-Header% + * 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, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 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. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF + * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR 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 NOT ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * %End-Header% + */ + +#include +#include +#include +#include + +#include "uuidP.h" + +int uuid_parse(const char *in, uuid_t uu) +{ + struct uuid uuid; + int i; + const char *cp; + char buf[3]; + + if (strlen(in) != 36) + return -1; + for (i=0, cp = in; i <= 36; i++,cp++) { + if ((i == 8) || (i == 13) || (i == 18) || + (i == 23)) { + if (*cp == '-') + continue; + else + return -1; + } + if (i== 36) + if (*cp == 0) + continue; + if (!isxdigit(*cp)) + return -1; + } + uuid.time_low = strtoul(in, NULL, 16); + uuid.time_mid = strtoul(in+9, NULL, 16); + uuid.time_hi_and_version = strtoul(in+14, NULL, 16); + uuid.clock_seq = strtoul(in+19, NULL, 16); + cp = in+24; + buf[2] = 0; + for (i=0; i < 6; i++) { + buf[0] = *cp++; + buf[1] = *cp++; + uuid.node[i] = strtoul(buf, NULL, 16); + } + + uuid_pack(&uuid, uu); + return 0; +} diff --git a/src/rigraph/vendor/uuid/unpack.c b/src/rigraph/vendor/uuid/unpack.c new file mode 100644 index 0000000..beaaff3 --- /dev/null +++ b/src/rigraph/vendor/uuid/unpack.c @@ -0,0 +1,63 @@ +/* + * Internal routine for unpacking UUID + * + * Copyright (C) 1996, 1997 Theodore Ts'o. + * + * %Begin-Header% + * 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, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 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. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF + * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR 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 NOT ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * %End-Header% + */ + +#include +#include "uuidP.h" + +void uuid_unpack(const uuid_t in, struct uuid *uu) +{ + const uint8_t *ptr = in; + uint32_t tmp; + + tmp = *ptr++; + tmp = (tmp << 8) | *ptr++; + tmp = (tmp << 8) | *ptr++; + tmp = (tmp << 8) | *ptr++; + uu->time_low = tmp; + + tmp = *ptr++; + tmp = (tmp << 8) | *ptr++; + uu->time_mid = tmp; + + tmp = *ptr++; + tmp = (tmp << 8) | *ptr++; + uu->time_hi_and_version = tmp; + + tmp = *ptr++; + tmp = (tmp << 8) | *ptr++; + uu->clock_seq = tmp; + + memcpy(uu->node, ptr, 6); +} + diff --git a/src/rigraph/vendor/uuid/unparse.c b/src/rigraph/vendor/uuid/unparse.c new file mode 100644 index 0000000..a95bbb0 --- /dev/null +++ b/src/rigraph/vendor/uuid/unparse.c @@ -0,0 +1,76 @@ +/* + * unparse.c -- convert a UUID to string + * + * Copyright (C) 1996, 1997 Theodore Ts'o. + * + * %Begin-Header% + * 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, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 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. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF + * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR 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 NOT ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * %End-Header% + */ + +#include + +#include "uuidP.h" + +static const char *fmt_lower = + "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x"; + +static const char *fmt_upper = + "%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X"; + +#ifdef UUID_UNPARSE_DEFAULT_UPPER +#define FMT_DEFAULT fmt_upper +#else +#define FMT_DEFAULT fmt_lower +#endif + +static void uuid_unparse_x(const uuid_t uu, char *out, const char *fmt) +{ + struct uuid uuid; + + uuid_unpack(uu, &uuid); + sprintf(out, fmt, + uuid.time_low, uuid.time_mid, uuid.time_hi_and_version, + uuid.clock_seq >> 8, uuid.clock_seq & 0xFF, + uuid.node[0], uuid.node[1], uuid.node[2], + uuid.node[3], uuid.node[4], uuid.node[5]); +} + +void uuid_unparse_lower(const uuid_t uu, char *out) +{ + uuid_unparse_x(uu, out, fmt_lower); +} + +void uuid_unparse_upper(const uuid_t uu, char *out) +{ + uuid_unparse_x(uu, out, fmt_upper); +} + +void uuid_unparse(const uuid_t uu, char *out) +{ + uuid_unparse_x(uu, out, FMT_DEFAULT); +} diff --git a/src/rigraph/vendor/uuid/uuid.h b/src/rigraph/vendor/uuid/uuid.h new file mode 100644 index 0000000..874d65a --- /dev/null +++ b/src/rigraph/vendor/uuid/uuid.h @@ -0,0 +1,104 @@ +/* + * Public include file for the UUID library + * + * Copyright (C) 1996, 1997, 1998 Theodore Ts'o. + * + * %Begin-Header% + * 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, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 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. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF + * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR 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 NOT ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * %End-Header% + */ + +#ifndef _UUID_UUID_H +#define _UUID_UUID_H + +#include +#ifndef _WIN32 +#include +#endif +#include + +typedef unsigned char uuid_t[16]; + +/* UUID Variant definitions */ +#define UUID_VARIANT_NCS 0 +#define UUID_VARIANT_DCE 1 +#define UUID_VARIANT_MICROSOFT 2 +#define UUID_VARIANT_OTHER 3 + +/* UUID Type definitions */ +#define UUID_TYPE_DCE_TIME 1 +#define UUID_TYPE_DCE_RANDOM 4 + +/* Allow UUID constants to be defined */ +#ifdef __GNUC__ +#define UUID_DEFINE(name,u0,u1,u2,u3,u4,u5,u6,u7,u8,u9,u10,u11,u12,u13,u14,u15) \ + static const uuid_t name __attribute__ ((unused)) = {u0,u1,u2,u3,u4,u5,u6,u7,u8,u9,u10,u11,u12,u13,u14,u15} +#else +#define UUID_DEFINE(name,u0,u1,u2,u3,u4,u5,u6,u7,u8,u9,u10,u11,u12,u13,u14,u15) \ + static const uuid_t name = {u0,u1,u2,u3,u4,u5,u6,u7,u8,u9,u10,u11,u12,u13,u14,u15} +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* clear.c */ +void uuid_clear(uuid_t uu); + +/* compare.c */ +int uuid_compare(const uuid_t uu1, const uuid_t uu2); + +/* copy.c */ +void uuid_copy(uuid_t dst, const uuid_t src); + +/* gen_uuid.c */ +void uuid_generate(uuid_t out); +void uuid_generate_random(uuid_t out); +void uuid_generate_time(uuid_t out); +int uuid_generate_time_safe(uuid_t out); + +/* isnull.c */ +int uuid_is_null(const uuid_t uu); + +/* parse.c */ +int uuid_parse(const char *in, uuid_t uu); + +/* unparse.c */ +void uuid_unparse(const uuid_t uu, char *out); +void uuid_unparse_lower(const uuid_t uu, char *out); +void uuid_unparse_upper(const uuid_t uu, char *out); + +/* uuid_time.c */ +time_t uuid_time(const uuid_t uu, struct timeval *ret_tv); +int uuid_type(const uuid_t uu); +int uuid_variant(const uuid_t uu); + +#ifdef __cplusplus +} +#endif + +#endif /* _UUID_UUID_H */ diff --git a/src/rigraph/vendor/uuid/uuidP.h b/src/rigraph/vendor/uuid/uuidP.h new file mode 100644 index 0000000..604d8bf --- /dev/null +++ b/src/rigraph/vendor/uuid/uuidP.h @@ -0,0 +1,63 @@ +/* + * uuid.h -- private header file for uuids + * + * Copyright (C) 1996, 1997 Theodore Ts'o. + * + * %Begin-Header% + * 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, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 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. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF + * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR 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 NOT ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * %End-Header% + */ + +#include +#include + +#include "config.h" + +#include "uuid.h" + +#define LIBUUID_CLOCK_FILE "/var/lib/libuuid/clock.txt" + +/* + * Offset between 15-Oct-1582 and 1-Jan-70 + */ +#define TIME_OFFSET_HIGH 0x01B21DD2 +#define TIME_OFFSET_LOW 0x13814000 + +struct uuid { + uint32_t time_low; + uint16_t time_mid; + uint16_t time_hi_and_version; + uint16_t clock_seq; + uint8_t node[6]; +}; + + +/* + * prototypes + */ +void uuid_pack(const struct uuid *uu, uuid_t ptr); +void uuid_unpack(const uuid_t in, struct uuid *uu); diff --git a/src/rigraph/vendor/uuid/uuidd.h b/src/rigraph/vendor/uuid/uuidd.h new file mode 100644 index 0000000..2f70968 --- /dev/null +++ b/src/rigraph/vendor/uuid/uuidd.h @@ -0,0 +1,54 @@ +/* + * Definitions used by the uuidd daemon + * + * Copyright (C) 2007 Theodore Ts'o. + * + * %Begin-Header% + * 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, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 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. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF + * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR 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 NOT ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * %End-Header% + */ + +#ifndef _UUID_UUIDD_H +#define _UUID_UUIDD_H + +#define UUIDD_DIR _PATH_LOCALSTATEDIR "/uuidd" +#define UUIDD_SOCKET_PATH UUIDD_DIR "/request" +#define UUIDD_PIDFILE_PATH UUIDD_DIR "/uuidd.pid" +#define UUIDD_PATH "/usr/sbin/uuidd" + +#define UUIDD_OP_GETPID 0 +#define UUIDD_OP_GET_MAXOP 1 +#define UUIDD_OP_TIME_UUID 2 +#define UUIDD_OP_RANDOM_UUID 3 +#define UUIDD_OP_BULK_TIME_UUID 4 +#define UUIDD_OP_BULK_RANDOM_UUID 5 +#define UUIDD_MAX_OP UUIDD_OP_BULK_RANDOM_UUID + +extern int __uuid_generate_time(uuid_t out, int *num); +extern void __uuid_generate_random(uuid_t out, int *num); + +#endif /* _UUID_UUID_H */ diff --git a/src/rigraph/vendor/uuid/win32/config.h b/src/rigraph/vendor/uuid/win32/config.h new file mode 100644 index 0000000..ffb2aba --- /dev/null +++ b/src/rigraph/vendor/uuid/win32/config.h @@ -0,0 +1,84 @@ +/* src/config.h. Generated from config.h.in by configure. */ +/* src/config.h.in. Generated from configure.ac by autoheader. */ +/* -- reflects MinGW + Win32 -- */ + +/* Define to 1 if you have the header file. */ +#define HAVE_INTTYPES_H 1 + +/* Define to 1 if you have the `jrand48' function. */ +/* #undef HAVE_JRAND48 */ + +/* Define to 1 if you have the header file. */ +#define HAVE_MEMORY_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_NETINET_IN_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_NET_IF_DL_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_NET_IF_H */ + +/* Define if struct sockaddr contains sa_len */ +/* #undef HAVE_SA_LEN */ + +/* Define to 1 if you have the header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRINGS_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_FILE_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_IOCTL_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_SOCKET_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_SOCKIO_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_SYSCALL_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TIME_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_UN_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_UNISTD_H 1 + +/* Define to the address where bug reports for this package should be sent. */ +#define PACKAGE_BUGREPORT "Simon.Urbanek@r-project.org" + +/* Define to the full name of this package. */ +#define PACKAGE_NAME "uuid" + +/* Define to the full name and version of this package. */ +#define PACKAGE_STRING "uuid 0.1" + +/* Define to the one symbol short name of this package. */ +#define PACKAGE_TARNAME "uuid" + +/* Define to the version of this package. */ +#define PACKAGE_VERSION "0.1" + +/* Define to 1 if you have the ANSI C header files. */ +#define STDC_HEADERS 1 From 6dc9f000a2db79582494a6c00f31ec368de4e312 Mon Sep 17 00:00:00 2001 From: evanbiederstedt Date: Wed, 6 Apr 2022 02:17:20 -0400 Subject: [PATCH 07/20] compiles --- DESCRIPTION | 1 + configure | 30 +++++++++++++++--------------- src/rigraph/Makefile | 23 ++++++++++++----------- 3 files changed, 28 insertions(+), 26 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index c663717..9d88fdd 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -12,6 +12,7 @@ Depends: R (>= 3.5.0), Matrix Imports: graphics, grDevices, + igraph, Matrix.utils, parallel, Rcpp (>= 1.0.5), diff --git a/configure b/configure index 5ebacec..f0502ed 100755 --- a/configure +++ b/configure @@ -2262,7 +2262,7 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu -ac_config_headers="$ac_config_headers src/config.h" +ac_config_headers="$ac_config_headers src/rigraph/config.h" : ${R_HOME=`R RHOME`} @@ -2996,7 +2996,7 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext #include #include struct stat; -/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ +/* Most of the following tests are stolen from RCS 5.7's src/rigraph/conf.sh. */ struct buf { int x; }; FILE * (*rcsopen) (struct buf *, struct stat *, int); static char *e (p, i) @@ -4353,7 +4353,7 @@ fi $as_echo "#define IGRAPH_THREAD_LOCAL /**/" >>confdefs.h -ac_config_files="$ac_config_files src/Makevars.tmp:src/Makevars.in" +ac_config_files="$ac_config_files src/rigraph/Makevars.tmp:src/rigraph/Makevars.in" cat >confcache <<\_ACEOF @@ -5045,8 +5045,8 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 for ac_config_target in $ac_config_targets do case $ac_config_target in - "src/config.h") CONFIG_HEADERS="$CONFIG_HEADERS src/config.h" ;; - "src/Makevars.tmp") CONFIG_FILES="$CONFIG_FILES src/Makevars.tmp:src/Makevars.in" ;; + "src/rigraph/config.h") CONFIG_HEADERS="$CONFIG_HEADERS src/rigraph/config.h" ;; + "src/rigraph/Makevars.tmp") CONFIG_FILES="$CONFIG_FILES src/rigraph/Makevars.tmp:src/rigraph/Makevars.in" ;; *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;; esac @@ -5594,17 +5594,17 @@ $as_echo "$as_me: $ac_file is unchanged" >&6;} case $ac_file$ac_mode in - "src/Makevars.tmp":F) - if test -f src/Makevars && cmp -s src/Makevars.tmp src/Makevars; then - { $as_echo "$as_me:${as_lineno-$LINENO}: creating src/Makevars" >&5 -$as_echo "$as_me: creating src/Makevars" >&6;} - { $as_echo "$as_me:${as_lineno-$LINENO}: src/Makevars is unchanged" >&5 -$as_echo "$as_me: src/Makevars is unchanged" >&6;} - rm src/Makevars.tmp + "src/rigraph/Makevars.tmp":F) + if test -f src/rigraph/Makevars && cmp -s src/rigraph/Makevars.tmp src/rigraph/Makevars; then + { $as_echo "$as_me:${as_lineno-$LINENO}: creating src/rigraph/Makevars" >&5 +$as_echo "$as_me: creating src/rigraph/Makevars" >&6;} + { $as_echo "$as_me:${as_lineno-$LINENO}: src/rigraph/Makevars is unchanged" >&5 +$as_echo "$as_me: src/rigraph/Makevars is unchanged" >&6;} + rm src/rigraph/Makevars.tmp else - { $as_echo "$as_me:${as_lineno-$LINENO}: creating src/Makevars" >&5 -$as_echo "$as_me: creating src/Makevars" >&6;} - mv src/Makevars.tmp src/Makevars + { $as_echo "$as_me:${as_lineno-$LINENO}: creating src/rigraph/Makevars" >&5 +$as_echo "$as_me: creating src/rigraph/Makevars" >&6;} + mv src/rigraph/Makevars.tmp src/rigraph/Makevars fi ;; diff --git a/src/rigraph/Makefile b/src/rigraph/Makefile index e2751dd..7809c87 100644 --- a/src/rigraph/Makefile +++ b/src/rigraph/Makefile @@ -1,15 +1,16 @@ -CXX_STD=CXX11 +CXX ?= g++ +PKG_CXXFLAGS = -O3 -std=c++11 -pthread -fPIC $(SHLIB_OPENMP_CXXFLAGS) +PKG_CXXFLAGS += -I"./include" -I"../rigraph/include" +LDFLAGS += -lpthread -PKG_CFLAGS=$(C_VISIBILITY) -PKG_CXXFLAGS=$(CXX_VISIBILITY) -PKG_FFLAGS=$(F_VISIBILITY) -PKG_CPPFLAGS=-DUSING_R -I. -Icore -Iinclude -Ivendor \ - @XML2_CPPFLAGS@ -DNDEBUG -DNTIMER -DNPRINT \ - -DPACKAGE_VERSION=\"@PACKAGE_VERSION@\" -DINTERNAL_ARPACK \ - -DPRPACK_IGRAPH_SUPPORT -DIGRAPH_THREAD_LOCAL=/**/ -PKG_LIBS=@XML2_LIBS@ @GMP_LIBS@ @GLPK_LIBS@ $(LAPACK_LIBS) $(BLAS_LIBS) $(FLIBS) +LIB = ../libigraph.a +lib: $(LIB) + +$(LIB): rinterface.o + ar rvs $@ $? + +clean: + @-rm -f *.o $(LIB) -all: $(SHLIB) -OBJECTS=core/centrality/betweenness.o core/centrality/centrality_other.o core/centrality/centralization.o core/centrality/closeness.o core/centrality/coreness.o core/centrality/prpack.o core/centrality/prpack/prpack_base_graph.o core/centrality/prpack/prpack_igraph_graph.o core/centrality/prpack/prpack_preprocessed_ge_graph.o core/centrality/prpack/prpack_preprocessed_gs_graph.o core/centrality/prpack/prpack_preprocessed_scc_graph.o core/centrality/prpack/prpack_preprocessed_schur_graph.o core/centrality/prpack/prpack_result.o core/centrality/prpack/prpack_solver.o core/centrality/prpack/prpack_utils.o core/cliques/cliquer/cliquer.o core/cliques/cliquer/cliquer_graph.o core/cliques/cliquer/reorder.o core/cliques/cliquer_wrapper.o core/cliques/cliques.o core/cliques/glet.o core/cliques/maximal_cliques.o core/community/community_misc.o core/community/edge_betweenness.o core/community/fast_modularity.o core/community/fluid.o core/community/infomap/infomap.o core/community/infomap/infomap_FlowGraph.o core/community/infomap/infomap_Greedy.o core/community/infomap/infomap_Node.o core/community/label_propagation.o core/community/leading_eigenvector.o core/community/leiden.o core/community/louvain.o core/community/modularity.o core/community/optimal_modularity.o core/community/spinglass/NetDataTypes.o core/community/spinglass/NetRoutines.o core/community/spinglass/clustertool.o core/community/spinglass/pottsmodel_2.o core/community/walktrap/walktrap.o core/community/walktrap/walktrap_communities.o core/community/walktrap/walktrap_graph.o core/community/walktrap/walktrap_heap.o core/connectivity/cohesive_blocks.o core/connectivity/components.o core/connectivity/separators.o core/constructors/adjacency.o core/constructors/atlas.o core/constructors/basic_constructors.o core/constructors/de_bruijn.o core/constructors/famous.o core/constructors/full.o core/constructors/kautz.o core/constructors/lcf.o core/constructors/linegraph.o core/constructors/prufer.o core/constructors/regular.o core/core/array.o core/core/buckets.o core/core/cutheap.o core/core/dqueue.o core/core/error.o core/core/estack.o core/core/fixed_vectorlist.o core/core/grid.o core/core/heap.o core/core/indheap.o core/core/interruption.o core/core/marked_queue.o core/core/matrix.o core/core/memory.o core/core/printing.o core/core/progress.o core/core/psumtree.o core/core/set.o core/core/sparsemat.o core/core/spmatrix.o core/core/stack.o core/core/statusbar.o core/core/strvector.o core/core/trie.o core/core/vector.o core/core/vector_ptr.o core/flow/flow.o core/flow/st-cuts.o core/games/barabasi.o core/games/callaway_traits.o core/games/citations.o core/games/correlated.o core/games/degree_sequence.o core/games/degree_sequence_vl/gengraph_box_list.o core/games/degree_sequence_vl/gengraph_degree_sequence.o core/games/degree_sequence_vl/gengraph_graph_molloy_hash.o core/games/degree_sequence_vl/gengraph_graph_molloy_optimized.o core/games/degree_sequence_vl/gengraph_mr-connected.o core/games/degree_sequence_vl/gengraph_powerlaw.o core/games/degree_sequence_vl/gengraph_random.o core/games/dotproduct.o core/games/erdos_renyi.o core/games/establishment.o core/games/forestfire.o core/games/grg.o core/games/growing_random.o core/games/islands.o core/games/k_regular.o core/games/preference.o core/games/recent_degree.o core/games/sbm.o core/games/static_fitness.o core/games/tree.o core/games/watts_strogatz.o core/graph/adjlist.o core/graph/attributes.o core/graph/basic_query.o core/graph/cattributes.o core/graph/iterators.o core/graph/type_indexededgelist.o core/graph/visitors.o core/hrg/hrg.o core/hrg/hrg_types.o core/internal/glpk_support.o core/internal/hacks.o core/internal/lsap.o core/internal/qsort.o core/internal/qsort_r.o core/internal/zeroin.o core/io/dimacs.o core/io/dl-lexer.o core/io/dl-parser.o core/io/dl.o core/io/dot.o core/io/edgelist.o core/io/gml-lexer.o core/io/gml-parser.o core/io/gml-tree.o core/io/gml.o core/io/graphdb.o core/io/graphml.o core/io/leda.o core/io/lgl-lexer.o core/io/lgl-parser.o core/io/lgl.o core/io/ncol-lexer.o core/io/ncol-parser.o core/io/ncol.o core/io/pajek-lexer.o core/io/pajek-parser.o core/io/pajek.o core/isomorphism/bliss.o core/isomorphism/bliss/defs.o core/isomorphism/bliss/graph.o core/isomorphism/bliss/heap.o core/isomorphism/bliss/orbit.o core/isomorphism/bliss/partition.o core/isomorphism/bliss/uintseqhash.o core/isomorphism/bliss/utils.o core/isomorphism/isoclasses.o core/isomorphism/isomorphism_misc.o core/isomorphism/lad.o core/isomorphism/queries.o core/isomorphism/vf2.o core/layout/circular.o core/layout/davidson_harel.o core/layout/drl/DensityGrid.o core/layout/drl/DensityGrid_3d.o core/layout/drl/drl_graph.o core/layout/drl/drl_graph_3d.o core/layout/drl/drl_layout.o core/layout/drl/drl_layout_3d.o core/layout/drl/drl_parse.o core/layout/fruchterman_reingold.o core/layout/gem.o core/layout/graphopt.o core/layout/kamada_kawai.o core/layout/large_graph.o core/layout/layout_bipartite.o core/layout/layout_grid.o core/layout/layout_random.o core/layout/mds.o core/layout/merge_dla.o core/layout/merge_grid.o core/layout/reingold_tilford.o core/layout/sugiyama.o core/linalg/arpack.o core/linalg/blas.o core/linalg/eigen.o core/linalg/lapack.o core/math/bfgs.o core/math/complex.o core/math/utils.o core/misc/bipartite.o core/misc/chordality.o core/misc/cocitation.o core/misc/coloring.o core/misc/conversion.o core/misc/degree_sequence.o core/misc/embedding.o core/misc/feedback_arc_set.o core/misc/graphicality.o core/misc/matching.o core/misc/microscopic_update.o core/misc/mixing.o core/misc/motifs.o core/misc/other.o core/misc/scan.o core/misc/sir.o core/misc/spanning_trees.o core/operators/add_edge.o core/operators/complementer.o core/operators/compose.o core/operators/connect_neighborhood.o core/operators/contract.o core/operators/difference.o core/operators/disjoint_union.o core/operators/intersection.o core/operators/misc_internal.o core/operators/permute.o core/operators/rewire.o core/operators/rewire_edges.o core/operators/simplify.o core/operators/subgraph.o core/operators/union.o core/paths/all_shortest_paths.o core/paths/bellman_ford.o core/paths/dijkstra.o core/paths/distances.o core/paths/eulerian.o core/paths/histogram.o core/paths/johnson.o core/paths/random_walk.o core/paths/shortest_paths.o core/paths/simple_paths.o core/paths/unweighted.o core/properties/basic_properties.o core/properties/constraint.o core/properties/convergence_degree.o core/properties/dag.o core/properties/degrees.o core/properties/girth.o core/properties/loops.o core/properties/multiplicity.o core/properties/neighborhood.o core/properties/spectral.o core/properties/trees.o core/properties/triangles.o core/random/random.o core/scg/scg.o core/scg/scg_approximate_methods.o core/scg/scg_exact_scg.o core/scg/scg_kmeans.o core/scg/scg_optimal_method.o core/scg/scg_utils.o core/version.o vendor/cs/cs_add.o vendor/cs/cs_amd.o vendor/cs/cs_chol.o vendor/cs/cs_cholsol.o vendor/cs/cs_compress.o vendor/cs/cs_counts.o vendor/cs/cs_cumsum.o vendor/cs/cs_dfs.o vendor/cs/cs_dmperm.o vendor/cs/cs_droptol.o vendor/cs/cs_dropzeros.o vendor/cs/cs_dupl.o vendor/cs/cs_entry.o vendor/cs/cs_ereach.o vendor/cs/cs_etree.o vendor/cs/cs_fkeep.o vendor/cs/cs_gaxpy.o vendor/cs/cs_happly.o vendor/cs/cs_house.o vendor/cs/cs_ipvec.o vendor/cs/cs_leaf.o vendor/cs/cs_load.o vendor/cs/cs_lsolve.o vendor/cs/cs_ltsolve.o vendor/cs/cs_lu.o vendor/cs/cs_lusol.o vendor/cs/cs_malloc.o vendor/cs/cs_maxtrans.o vendor/cs/cs_multiply.o vendor/cs/cs_norm.o vendor/cs/cs_permute.o vendor/cs/cs_pinv.o vendor/cs/cs_post.o vendor/cs/cs_print.o vendor/cs/cs_pvec.o vendor/cs/cs_qr.o vendor/cs/cs_qrsol.o vendor/cs/cs_randperm.o vendor/cs/cs_reach.o vendor/cs/cs_scatter.o vendor/cs/cs_scc.o vendor/cs/cs_schol.o vendor/cs/cs_spsolve.o vendor/cs/cs_sqr.o vendor/cs/cs_symperm.o vendor/cs/cs_tdfs.o vendor/cs/cs_transpose.o vendor/cs/cs_updown.o vendor/cs/cs_usolve.o vendor/cs/cs_util.o vendor/cs/cs_utsolve.o vendor/mini-gmp/mini-gmp.o vendor/plfit/gss.o vendor/plfit/hzeta.o vendor/plfit/kolmogorov.o vendor/plfit/lbfgs.o vendor/plfit/mt.o vendor/plfit/options.o vendor/plfit/platform.o vendor/plfit/plfit.o vendor/plfit/plfit_error.o vendor/plfit/rbinom.o vendor/plfit/sampling.o vendor/arpack/dgetv0.o vendor/arpack/dlaqrb.o vendor/arpack/dmout.o vendor/arpack/dnaitr.o vendor/arpack/dnapps.o vendor/arpack/dnaup2.o vendor/arpack/dnaupd.o vendor/arpack/dnconv.o vendor/arpack/dneigh.o vendor/arpack/dneupd.o vendor/arpack/dngets.o vendor/arpack/dsaitr.o vendor/arpack/dsapps.o vendor/arpack/dsaup2.o vendor/arpack/dsaupd.o vendor/arpack/dsconv.o vendor/arpack/dseigt.o vendor/arpack/dsesrt.o vendor/arpack/dseupd.o vendor/arpack/dsgets.o vendor/arpack/dsortc.o vendor/arpack/dsortr.o vendor/arpack/dstatn.o vendor/arpack/dstats.o vendor/arpack/dstqrb.o vendor/arpack/dvout.o vendor/arpack/ivout.o vendor/arpack/second.o vendor/arpack/wrap.o vendor/simpleraytracer/Color.o vendor/simpleraytracer/Light.o vendor/simpleraytracer/Point.o vendor/simpleraytracer/RIgraphRay.o vendor/simpleraytracer/Ray.o vendor/simpleraytracer/RayTracer.o vendor/simpleraytracer/RayVector.o vendor/simpleraytracer/Shape.o vendor/simpleraytracer/Sphere.o vendor/simpleraytracer/Triangle.o vendor/simpleraytracer/unit_limiter.o vendor/uuid/R.o vendor/uuid/clear.o vendor/uuid/compare.o vendor/uuid/copy.o vendor/uuid/gen_uuid.o vendor/uuid/isnull.o vendor/uuid/pack.o vendor/uuid/parse.o vendor/uuid/unpack.o vendor/uuid/unparse.o rinterface.o rinterface_extra.o rrandom.o lazyeval.o From 681feecf1c21687bf93b0a3f5dbe83d3c205b311 Mon Sep 17 00:00:00 2001 From: evanbiederstedt Date: Thu, 7 Apr 2022 18:05:26 -0400 Subject: [PATCH 08/20] lets try again --- cleanup | 9 - cleanup.win | 9 - configure | 5660 ----------------- configure.ac | 181 - configure.win | 0 src/.DS_Store | Bin 0 -> 6148 bytes src/rigraph/Makefile | 16 - src/rigraph/Makevars.in | 15 - src/rigraph/Makevars.ucrt | 11 - src/rigraph/Makevars.win | 10 - src/rigraph/config.h | 0 src/rigraph/config.h.in | 140 - src/rigraph/igraph-win.def | 3 - src/rigraph/include/igraph_version.h | 2 +- src/rigraph/vendor/arpack/debug.h | 16 - src/rigraph/vendor/arpack/dgetv0.f | 419 -- src/rigraph/vendor/arpack/dlaqrb.f | 521 -- src/rigraph/vendor/arpack/dmout.f | 167 - src/rigraph/vendor/arpack/dnaitr.f | 840 --- src/rigraph/vendor/arpack/dnapps.f | 647 -- src/rigraph/vendor/arpack/dnaup2.f | 838 --- src/rigraph/vendor/arpack/dnaupd.f | 655 -- src/rigraph/vendor/arpack/dnconv.f | 146 - src/rigraph/vendor/arpack/dneigh.f | 315 - src/rigraph/vendor/arpack/dneupd.f | 1044 --- src/rigraph/vendor/arpack/dngets.f | 231 - src/rigraph/vendor/arpack/dsaitr.f | 854 --- src/rigraph/vendor/arpack/dsapps.f | 516 -- src/rigraph/vendor/arpack/dsaup2.f | 853 --- src/rigraph/vendor/arpack/dsaupd.f | 653 -- src/rigraph/vendor/arpack/dsconv.f | 138 - src/rigraph/vendor/arpack/dseigt.f | 181 - src/rigraph/vendor/arpack/dsesrt.f | 217 - src/rigraph/vendor/arpack/dseupd.f | 905 --- src/rigraph/vendor/arpack/dsgets.f | 220 - src/rigraph/vendor/arpack/dsortc.f | 344 - src/rigraph/vendor/arpack/dsortr.f | 218 - src/rigraph/vendor/arpack/dstatn.f | 61 - src/rigraph/vendor/arpack/dstats.f | 47 - src/rigraph/vendor/arpack/dstqrb.f | 594 -- src/rigraph/vendor/arpack/dvout.f | 122 - src/rigraph/vendor/arpack/ivout.f | 120 - src/rigraph/vendor/arpack/second.f | 35 - src/rigraph/vendor/arpack/stat.h | 21 - src/rigraph/vendor/arpack/wrap.f | 151 - src/rigraph/vendor/cs/SuiteSparse_config.h | 221 - src/rigraph/vendor/cs/cs.h | 758 --- src/rigraph/vendor/cs/cs_add.c | 28 - src/rigraph/vendor/cs/cs_amd.c | 364 -- src/rigraph/vendor/cs/cs_chol.c | 59 - src/rigraph/vendor/cs/cs_cholsol.c | 26 - src/rigraph/vendor/cs/cs_compress.c | 22 - src/rigraph/vendor/cs/cs_counts.c | 61 - src/rigraph/vendor/cs/cs_cumsum.c | 17 - src/rigraph/vendor/cs/cs_dfs.c | 36 - src/rigraph/vendor/cs/cs_dmperm.c | 144 - src/rigraph/vendor/cs/cs_droptol.c | 9 - src/rigraph/vendor/cs/cs_dropzeros.c | 9 - src/rigraph/vendor/cs/cs_dupl.c | 34 - src/rigraph/vendor/cs/cs_entry.c | 13 - src/rigraph/vendor/cs/cs_ereach.c | 23 - src/rigraph/vendor/cs/cs_etree.c | 30 - src/rigraph/vendor/cs/cs_fkeep.c | 25 - src/rigraph/vendor/cs/cs_gaxpy.c | 17 - src/rigraph/vendor/cs/cs_happly.c | 19 - src/rigraph/vendor/cs/cs_house.c | 30 - src/rigraph/vendor/cs/cs_ipvec.c | 9 - src/rigraph/vendor/cs/cs_leaf.c | 22 - src/rigraph/vendor/cs/cs_load.c | 26 - src/rigraph/vendor/cs/cs_lsolve.c | 18 - src/rigraph/vendor/cs/cs_ltsolve.c | 18 - src/rigraph/vendor/cs/cs_lu.c | 88 - src/rigraph/vendor/cs/cs_lusol.c | 26 - src/rigraph/vendor/cs/cs_malloc.c | 35 - src/rigraph/vendor/cs/cs_maxtrans.c | 92 - src/rigraph/vendor/cs/cs_multiply.c | 35 - src/rigraph/vendor/cs/cs_norm.c | 16 - src/rigraph/vendor/cs/cs_permute.c | 25 - src/rigraph/vendor/cs/cs_pinv.c | 11 - src/rigraph/vendor/cs/cs_post.c | 24 - src/rigraph/vendor/cs/cs_print.c | 55 - src/rigraph/vendor/cs/cs_pvec.c | 9 - src/rigraph/vendor/cs/cs_qr.c | 74 - src/rigraph/vendor/cs/cs_qrsol.c | 53 - src/rigraph/vendor/cs/cs_randperm.c | 28 - src/rigraph/vendor/cs/cs_reach.c | 19 - src/rigraph/vendor/cs/cs_scatter.c | 22 - src/rigraph/vendor/cs/cs_scc.c | 41 - src/rigraph/vendor/cs/cs_schol.c | 26 - src/rigraph/vendor/cs/cs_spsolve.c | 28 - src/rigraph/vendor/cs/cs_sqr.c | 87 - src/rigraph/vendor/cs/cs_symperm.c | 39 - src/rigraph/vendor/cs/cs_tdfs.c | 24 - src/rigraph/vendor/cs/cs_transpose.c | 25 - src/rigraph/vendor/cs/cs_updown.c | 48 - src/rigraph/vendor/cs/cs_usolve.c | 18 - src/rigraph/vendor/cs/cs_util.c | 120 - src/rigraph/vendor/cs/cs_utsolve.c | 18 - src/rigraph/vendor/mini-gmp/mini-gmp.c | 4578 ------------- src/rigraph/vendor/mini-gmp/mini-gmp.h | 305 - src/rigraph/vendor/plfit/arithmetic_ansi.h | 133 - .../vendor/plfit/arithmetic_sse_double.h | 294 - .../vendor/plfit/arithmetic_sse_float.h | 291 - src/rigraph/vendor/plfit/gss.c | 153 - src/rigraph/vendor/plfit/gss.h | 146 - src/rigraph/vendor/plfit/hzeta.c | 651 -- src/rigraph/vendor/plfit/hzeta.h | 96 - src/rigraph/vendor/plfit/kolmogorov.c | 66 - src/rigraph/vendor/plfit/kolmogorov.h | 43 - src/rigraph/vendor/plfit/lbfgs.c | 1378 ---- src/rigraph/vendor/plfit/lbfgs.h | 736 --- src/rigraph/vendor/plfit/mt.c | 93 - src/rigraph/vendor/plfit/options.c | 52 - src/rigraph/vendor/plfit/platform.c | 36 - src/rigraph/vendor/plfit/platform.h | 69 - src/rigraph/vendor/plfit/plfit.c | 1342 ---- src/rigraph/vendor/plfit/plfit.h | 140 - src/rigraph/vendor/plfit/plfit_error.c | 66 - src/rigraph/vendor/plfit/plfit_error.h | 86 - src/rigraph/vendor/plfit/plfit_mt.h | 101 - src/rigraph/vendor/plfit/plfit_sampling.h | 177 - src/rigraph/vendor/plfit/plfit_version.h | 28 - src/rigraph/vendor/plfit/rbinom.c | 208 - src/rigraph/vendor/plfit/sampling.c | 312 - src/rigraph/vendor/simpleraytracer/Color.cpp | 96 - src/rigraph/vendor/simpleraytracer/Color.h | 40 - src/rigraph/vendor/simpleraytracer/Light.cpp | 46 - src/rigraph/vendor/simpleraytracer/Light.h | 39 - src/rigraph/vendor/simpleraytracer/Point.cpp | 106 - src/rigraph/vendor/simpleraytracer/Point.h | 45 - .../vendor/simpleraytracer/RIgraphRay.cpp | 93 - src/rigraph/vendor/simpleraytracer/Ray.cpp | 44 - src/rigraph/vendor/simpleraytracer/Ray.h | 33 - .../vendor/simpleraytracer/RayTracer.cpp | 266 - .../vendor/simpleraytracer/RayTracer.h | 63 - .../vendor/simpleraytracer/RayVector.cpp | 128 - .../vendor/simpleraytracer/RayVector.h | 49 - src/rigraph/vendor/simpleraytracer/Shape.cpp | 106 - src/rigraph/vendor/simpleraytracer/Shape.h | 65 - src/rigraph/vendor/simpleraytracer/Sphere.cpp | 70 - src/rigraph/vendor/simpleraytracer/Sphere.h | 31 - .../vendor/simpleraytracer/Triangle.cpp | 90 - src/rigraph/vendor/simpleraytracer/Triangle.h | 27 - .../vendor/simpleraytracer/unit_limiter.cpp | 15 - .../vendor/simpleraytracer/unit_limiter.h | 10 - src/rigraph/vendor/uuid/COPYING | 28 - src/rigraph/vendor/uuid/Makevars.in | 2 - src/rigraph/vendor/uuid/Makevars.win | 1 - src/rigraph/vendor/uuid/R.c | 25 - src/rigraph/vendor/uuid/clear.c | 43 - src/rigraph/vendor/uuid/compare.c | 55 - src/rigraph/vendor/uuid/config.h.in | 82 - src/rigraph/vendor/uuid/copy.c | 45 - src/rigraph/vendor/uuid/gen_uuid.c | 536 -- src/rigraph/vendor/uuid/isnull.c | 48 - src/rigraph/vendor/uuid/pack.c | 69 - src/rigraph/vendor/uuid/parse.c | 79 - src/rigraph/vendor/uuid/unpack.c | 63 - src/rigraph/vendor/uuid/unparse.c | 76 - src/rigraph/vendor/uuid/uuid.h | 104 - src/rigraph/vendor/uuid/uuidP.h | 63 - src/rigraph/vendor/uuid/uuidd.h | 54 - src/rigraph/vendor/uuid/win32/config.h | 84 - 163 files changed, 1 insertion(+), 35767 deletions(-) delete mode 100755 cleanup delete mode 100644 cleanup.win delete mode 100755 configure delete mode 100644 configure.ac delete mode 100644 configure.win create mode 100644 src/.DS_Store delete mode 100644 src/rigraph/Makefile delete mode 100644 src/rigraph/Makevars.in delete mode 100644 src/rigraph/Makevars.ucrt delete mode 100644 src/rigraph/Makevars.win delete mode 100644 src/rigraph/config.h delete mode 100644 src/rigraph/config.h.in delete mode 100644 src/rigraph/igraph-win.def delete mode 100644 src/rigraph/vendor/arpack/debug.h delete mode 100644 src/rigraph/vendor/arpack/dgetv0.f delete mode 100644 src/rigraph/vendor/arpack/dlaqrb.f delete mode 100644 src/rigraph/vendor/arpack/dmout.f delete mode 100644 src/rigraph/vendor/arpack/dnaitr.f delete mode 100644 src/rigraph/vendor/arpack/dnapps.f delete mode 100644 src/rigraph/vendor/arpack/dnaup2.f delete mode 100644 src/rigraph/vendor/arpack/dnaupd.f delete mode 100644 src/rigraph/vendor/arpack/dnconv.f delete mode 100644 src/rigraph/vendor/arpack/dneigh.f delete mode 100644 src/rigraph/vendor/arpack/dneupd.f delete mode 100644 src/rigraph/vendor/arpack/dngets.f delete mode 100644 src/rigraph/vendor/arpack/dsaitr.f delete mode 100644 src/rigraph/vendor/arpack/dsapps.f delete mode 100644 src/rigraph/vendor/arpack/dsaup2.f delete mode 100644 src/rigraph/vendor/arpack/dsaupd.f delete mode 100644 src/rigraph/vendor/arpack/dsconv.f delete mode 100644 src/rigraph/vendor/arpack/dseigt.f delete mode 100644 src/rigraph/vendor/arpack/dsesrt.f delete mode 100644 src/rigraph/vendor/arpack/dseupd.f delete mode 100644 src/rigraph/vendor/arpack/dsgets.f delete mode 100644 src/rigraph/vendor/arpack/dsortc.f delete mode 100644 src/rigraph/vendor/arpack/dsortr.f delete mode 100644 src/rigraph/vendor/arpack/dstatn.f delete mode 100644 src/rigraph/vendor/arpack/dstats.f delete mode 100644 src/rigraph/vendor/arpack/dstqrb.f delete mode 100644 src/rigraph/vendor/arpack/dvout.f delete mode 100644 src/rigraph/vendor/arpack/ivout.f delete mode 100644 src/rigraph/vendor/arpack/second.f delete mode 100644 src/rigraph/vendor/arpack/stat.h delete mode 100644 src/rigraph/vendor/arpack/wrap.f delete mode 100644 src/rigraph/vendor/cs/SuiteSparse_config.h delete mode 100644 src/rigraph/vendor/cs/cs.h delete mode 100644 src/rigraph/vendor/cs/cs_add.c delete mode 100644 src/rigraph/vendor/cs/cs_amd.c delete mode 100644 src/rigraph/vendor/cs/cs_chol.c delete mode 100644 src/rigraph/vendor/cs/cs_cholsol.c delete mode 100644 src/rigraph/vendor/cs/cs_compress.c delete mode 100644 src/rigraph/vendor/cs/cs_counts.c delete mode 100644 src/rigraph/vendor/cs/cs_cumsum.c delete mode 100644 src/rigraph/vendor/cs/cs_dfs.c delete mode 100644 src/rigraph/vendor/cs/cs_dmperm.c delete mode 100644 src/rigraph/vendor/cs/cs_droptol.c delete mode 100644 src/rigraph/vendor/cs/cs_dropzeros.c delete mode 100644 src/rigraph/vendor/cs/cs_dupl.c delete mode 100644 src/rigraph/vendor/cs/cs_entry.c delete mode 100644 src/rigraph/vendor/cs/cs_ereach.c delete mode 100644 src/rigraph/vendor/cs/cs_etree.c delete mode 100644 src/rigraph/vendor/cs/cs_fkeep.c delete mode 100644 src/rigraph/vendor/cs/cs_gaxpy.c delete mode 100644 src/rigraph/vendor/cs/cs_happly.c delete mode 100644 src/rigraph/vendor/cs/cs_house.c delete mode 100644 src/rigraph/vendor/cs/cs_ipvec.c delete mode 100644 src/rigraph/vendor/cs/cs_leaf.c delete mode 100644 src/rigraph/vendor/cs/cs_load.c delete mode 100644 src/rigraph/vendor/cs/cs_lsolve.c delete mode 100644 src/rigraph/vendor/cs/cs_ltsolve.c delete mode 100644 src/rigraph/vendor/cs/cs_lu.c delete mode 100644 src/rigraph/vendor/cs/cs_lusol.c delete mode 100644 src/rigraph/vendor/cs/cs_malloc.c delete mode 100644 src/rigraph/vendor/cs/cs_maxtrans.c delete mode 100644 src/rigraph/vendor/cs/cs_multiply.c delete mode 100644 src/rigraph/vendor/cs/cs_norm.c delete mode 100644 src/rigraph/vendor/cs/cs_permute.c delete mode 100644 src/rigraph/vendor/cs/cs_pinv.c delete mode 100644 src/rigraph/vendor/cs/cs_post.c delete mode 100644 src/rigraph/vendor/cs/cs_print.c delete mode 100644 src/rigraph/vendor/cs/cs_pvec.c delete mode 100644 src/rigraph/vendor/cs/cs_qr.c delete mode 100644 src/rigraph/vendor/cs/cs_qrsol.c delete mode 100644 src/rigraph/vendor/cs/cs_randperm.c delete mode 100644 src/rigraph/vendor/cs/cs_reach.c delete mode 100644 src/rigraph/vendor/cs/cs_scatter.c delete mode 100644 src/rigraph/vendor/cs/cs_scc.c delete mode 100644 src/rigraph/vendor/cs/cs_schol.c delete mode 100644 src/rigraph/vendor/cs/cs_spsolve.c delete mode 100644 src/rigraph/vendor/cs/cs_sqr.c delete mode 100644 src/rigraph/vendor/cs/cs_symperm.c delete mode 100644 src/rigraph/vendor/cs/cs_tdfs.c delete mode 100644 src/rigraph/vendor/cs/cs_transpose.c delete mode 100644 src/rigraph/vendor/cs/cs_updown.c delete mode 100644 src/rigraph/vendor/cs/cs_usolve.c delete mode 100644 src/rigraph/vendor/cs/cs_util.c delete mode 100644 src/rigraph/vendor/cs/cs_utsolve.c delete mode 100644 src/rigraph/vendor/mini-gmp/mini-gmp.c delete mode 100644 src/rigraph/vendor/mini-gmp/mini-gmp.h delete mode 100644 src/rigraph/vendor/plfit/arithmetic_ansi.h delete mode 100644 src/rigraph/vendor/plfit/arithmetic_sse_double.h delete mode 100644 src/rigraph/vendor/plfit/arithmetic_sse_float.h delete mode 100644 src/rigraph/vendor/plfit/gss.c delete mode 100644 src/rigraph/vendor/plfit/gss.h delete mode 100644 src/rigraph/vendor/plfit/hzeta.c delete mode 100644 src/rigraph/vendor/plfit/hzeta.h delete mode 100644 src/rigraph/vendor/plfit/kolmogorov.c delete mode 100644 src/rigraph/vendor/plfit/kolmogorov.h delete mode 100644 src/rigraph/vendor/plfit/lbfgs.c delete mode 100644 src/rigraph/vendor/plfit/lbfgs.h delete mode 100644 src/rigraph/vendor/plfit/mt.c delete mode 100644 src/rigraph/vendor/plfit/options.c delete mode 100644 src/rigraph/vendor/plfit/platform.c delete mode 100644 src/rigraph/vendor/plfit/platform.h delete mode 100644 src/rigraph/vendor/plfit/plfit.c delete mode 100644 src/rigraph/vendor/plfit/plfit.h delete mode 100644 src/rigraph/vendor/plfit/plfit_error.c delete mode 100644 src/rigraph/vendor/plfit/plfit_error.h delete mode 100644 src/rigraph/vendor/plfit/plfit_mt.h delete mode 100644 src/rigraph/vendor/plfit/plfit_sampling.h delete mode 100644 src/rigraph/vendor/plfit/plfit_version.h delete mode 100644 src/rigraph/vendor/plfit/rbinom.c delete mode 100644 src/rigraph/vendor/plfit/sampling.c delete mode 100644 src/rigraph/vendor/simpleraytracer/Color.cpp delete mode 100644 src/rigraph/vendor/simpleraytracer/Color.h delete mode 100644 src/rigraph/vendor/simpleraytracer/Light.cpp delete mode 100644 src/rigraph/vendor/simpleraytracer/Light.h delete mode 100644 src/rigraph/vendor/simpleraytracer/Point.cpp delete mode 100644 src/rigraph/vendor/simpleraytracer/Point.h delete mode 100644 src/rigraph/vendor/simpleraytracer/RIgraphRay.cpp delete mode 100644 src/rigraph/vendor/simpleraytracer/Ray.cpp delete mode 100644 src/rigraph/vendor/simpleraytracer/Ray.h delete mode 100644 src/rigraph/vendor/simpleraytracer/RayTracer.cpp delete mode 100644 src/rigraph/vendor/simpleraytracer/RayTracer.h delete mode 100644 src/rigraph/vendor/simpleraytracer/RayVector.cpp delete mode 100644 src/rigraph/vendor/simpleraytracer/RayVector.h delete mode 100644 src/rigraph/vendor/simpleraytracer/Shape.cpp delete mode 100644 src/rigraph/vendor/simpleraytracer/Shape.h delete mode 100644 src/rigraph/vendor/simpleraytracer/Sphere.cpp delete mode 100644 src/rigraph/vendor/simpleraytracer/Sphere.h delete mode 100644 src/rigraph/vendor/simpleraytracer/Triangle.cpp delete mode 100644 src/rigraph/vendor/simpleraytracer/Triangle.h delete mode 100644 src/rigraph/vendor/simpleraytracer/unit_limiter.cpp delete mode 100644 src/rigraph/vendor/simpleraytracer/unit_limiter.h delete mode 100644 src/rigraph/vendor/uuid/COPYING delete mode 100644 src/rigraph/vendor/uuid/Makevars.in delete mode 100644 src/rigraph/vendor/uuid/Makevars.win delete mode 100644 src/rigraph/vendor/uuid/R.c delete mode 100644 src/rigraph/vendor/uuid/clear.c delete mode 100644 src/rigraph/vendor/uuid/compare.c delete mode 100644 src/rigraph/vendor/uuid/config.h.in delete mode 100644 src/rigraph/vendor/uuid/copy.c delete mode 100644 src/rigraph/vendor/uuid/gen_uuid.c delete mode 100644 src/rigraph/vendor/uuid/isnull.c delete mode 100644 src/rigraph/vendor/uuid/pack.c delete mode 100644 src/rigraph/vendor/uuid/parse.c delete mode 100644 src/rigraph/vendor/uuid/unpack.c delete mode 100644 src/rigraph/vendor/uuid/unparse.c delete mode 100644 src/rigraph/vendor/uuid/uuid.h delete mode 100644 src/rigraph/vendor/uuid/uuidP.h delete mode 100644 src/rigraph/vendor/uuid/uuidd.h delete mode 100644 src/rigraph/vendor/uuid/win32/config.h diff --git a/cleanup b/cleanup deleted file mode 100755 index 8de4092..0000000 --- a/cleanup +++ /dev/null @@ -1,9 +0,0 @@ -#! /bin/sh - -rm -f src/rigraph/config.h src/rigraph/Makevars -touch src/rigraph/config.h - -# Object files cause problems on Github Actions where they get included -# in the source package that is re-generated from the original source, so -# we remove them here in the cleanup step -find src -name *.o | xargs rm diff --git a/cleanup.win b/cleanup.win deleted file mode 100644 index 8de4092..0000000 --- a/cleanup.win +++ /dev/null @@ -1,9 +0,0 @@ -#! /bin/sh - -rm -f src/rigraph/config.h src/rigraph/Makevars -touch src/rigraph/config.h - -# Object files cause problems on Github Actions where they get included -# in the source package that is re-generated from the original source, so -# we remove them here in the cleanup step -find src -name *.o | xargs rm diff --git a/configure b/configure deleted file mode 100755 index f0502ed..0000000 --- a/configure +++ /dev/null @@ -1,5660 +0,0 @@ -#! /bin/sh -# Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.69 for igraph 1.3.0.9016. -# -# Report bugs to . -# -# -# Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc. -# -# -# This configure script is free software; the Free Software Foundation -# gives unlimited permission to copy, distribute and modify it. -## -------------------- ## -## M4sh Initialization. ## -## -------------------- ## - -# Be more Bourne compatible -DUALCASE=1; export DUALCASE # for MKS sh -if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : - emulate sh - NULLCMD=: - # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which - # is contrary to our usage. Disable this feature. - alias -g '${1+"$@"}'='"$@"' - setopt NO_GLOB_SUBST -else - case `(set -o) 2>/dev/null` in #( - *posix*) : - set -o posix ;; #( - *) : - ;; -esac -fi - - -as_nl=' -' -export as_nl -# Printing a long string crashes Solaris 7 /usr/bin/printf. -as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' -as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo -as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo -# Prefer a ksh shell builtin over an external printf program on Solaris, -# but without wasting forks for bash or zsh. -if test -z "$BASH_VERSION$ZSH_VERSION" \ - && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then - as_echo='print -r --' - as_echo_n='print -rn --' -elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then - as_echo='printf %s\n' - as_echo_n='printf %s' -else - if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then - as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' - as_echo_n='/usr/ucb/echo -n' - else - as_echo_body='eval expr "X$1" : "X\\(.*\\)"' - as_echo_n_body='eval - arg=$1; - case $arg in #( - *"$as_nl"*) - expr "X$arg" : "X\\(.*\\)$as_nl"; - arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; - esac; - expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" - ' - export as_echo_n_body - as_echo_n='sh -c $as_echo_n_body as_echo' - fi - export as_echo_body - as_echo='sh -c $as_echo_body as_echo' -fi - -# The user is always right. -if test "${PATH_SEPARATOR+set}" != set; then - PATH_SEPARATOR=: - (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { - (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || - PATH_SEPARATOR=';' - } -fi - - -# IFS -# We need space, tab and new line, in precisely that order. Quoting is -# there to prevent editors from complaining about space-tab. -# (If _AS_PATH_WALK were called with IFS unset, it would disable word -# splitting by setting IFS to empty value.) -IFS=" "" $as_nl" - -# Find who we are. Look in the path if we contain no directory separator. -as_myself= -case $0 in #(( - *[\\/]* ) as_myself=$0 ;; - *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break - done -IFS=$as_save_IFS - - ;; -esac -# We did not find ourselves, most probably we were run as `sh COMMAND' -# in which case we are not to be found in the path. -if test "x$as_myself" = x; then - as_myself=$0 -fi -if test ! -f "$as_myself"; then - $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 - exit 1 -fi - -# Unset variables that we do not need and which cause bugs (e.g. in -# pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" -# suppresses any "Segmentation fault" message there. '((' could -# trigger a bug in pdksh 5.2.14. -for as_var in BASH_ENV ENV MAIL MAILPATH -do eval test x\${$as_var+set} = xset \ - && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : -done -PS1='$ ' -PS2='> ' -PS4='+ ' - -# NLS nuisances. -LC_ALL=C -export LC_ALL -LANGUAGE=C -export LANGUAGE - -# CDPATH. -(unset CDPATH) >/dev/null 2>&1 && unset CDPATH - -# Use a proper internal environment variable to ensure we don't fall - # into an infinite loop, continuously re-executing ourselves. - if test x"${_as_can_reexec}" != xno && test "x$CONFIG_SHELL" != x; then - _as_can_reexec=no; export _as_can_reexec; - # We cannot yet assume a decent shell, so we have to provide a -# neutralization value for shells without unset; and this also -# works around shells that cannot unset nonexistent variables. -# Preserve -v and -x to the replacement shell. -BASH_ENV=/dev/null -ENV=/dev/null -(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV -case $- in # (((( - *v*x* | *x*v* ) as_opts=-vx ;; - *v* ) as_opts=-v ;; - *x* ) as_opts=-x ;; - * ) as_opts= ;; -esac -exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} -# Admittedly, this is quite paranoid, since all the known shells bail -# out after a failed `exec'. -$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 -as_fn_exit 255 - fi - # We don't want this to propagate to other subprocesses. - { _as_can_reexec=; unset _as_can_reexec;} -if test "x$CONFIG_SHELL" = x; then - as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then : - emulate sh - NULLCMD=: - # Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which - # is contrary to our usage. Disable this feature. - alias -g '\${1+\"\$@\"}'='\"\$@\"' - setopt NO_GLOB_SUBST -else - case \`(set -o) 2>/dev/null\` in #( - *posix*) : - set -o posix ;; #( - *) : - ;; -esac -fi -" - as_required="as_fn_return () { (exit \$1); } -as_fn_success () { as_fn_return 0; } -as_fn_failure () { as_fn_return 1; } -as_fn_ret_success () { return 0; } -as_fn_ret_failure () { return 1; } - -exitcode=0 -as_fn_success || { exitcode=1; echo as_fn_success failed.; } -as_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; } -as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; } -as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; } -if ( set x; as_fn_ret_success y && test x = \"\$1\" ); then : - -else - exitcode=1; echo positional parameters were not saved. -fi -test x\$exitcode = x0 || exit 1 -test -x / || exit 1" - as_suggested=" as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO - as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO - eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" && - test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1 -test \$(( 1 + 1 )) = 2 || exit 1" - if (eval "$as_required") 2>/dev/null; then : - as_have_required=yes -else - as_have_required=no -fi - if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null; then : - -else - as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -as_found=false -for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - as_found=: - case $as_dir in #( - /*) - for as_base in sh bash ksh sh5; do - # Try only shells that exist, to save several forks. - as_shell=$as_dir/$as_base - if { test -f "$as_shell" || test -f "$as_shell.exe"; } && - { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$as_shell"; } 2>/dev/null; then : - CONFIG_SHELL=$as_shell as_have_required=yes - if { $as_echo "$as_bourne_compatible""$as_suggested" | as_run=a "$as_shell"; } 2>/dev/null; then : - break 2 -fi -fi - done;; - esac - as_found=false -done -$as_found || { if { test -f "$SHELL" || test -f "$SHELL.exe"; } && - { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$SHELL"; } 2>/dev/null; then : - CONFIG_SHELL=$SHELL as_have_required=yes -fi; } -IFS=$as_save_IFS - - - if test "x$CONFIG_SHELL" != x; then : - export CONFIG_SHELL - # We cannot yet assume a decent shell, so we have to provide a -# neutralization value for shells without unset; and this also -# works around shells that cannot unset nonexistent variables. -# Preserve -v and -x to the replacement shell. -BASH_ENV=/dev/null -ENV=/dev/null -(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV -case $- in # (((( - *v*x* | *x*v* ) as_opts=-vx ;; - *v* ) as_opts=-v ;; - *x* ) as_opts=-x ;; - * ) as_opts= ;; -esac -exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} -# Admittedly, this is quite paranoid, since all the known shells bail -# out after a failed `exec'. -$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 -exit 255 -fi - - if test x$as_have_required = xno; then : - $as_echo "$0: This script requires a shell more modern than all" - $as_echo "$0: the shells that I found on your system." - if test x${ZSH_VERSION+set} = xset ; then - $as_echo "$0: In particular, zsh $ZSH_VERSION has bugs and should" - $as_echo "$0: be upgraded to zsh 4.3.4 or later." - else - $as_echo "$0: Please tell bug-autoconf@gnu.org and ntamas@gmail.com -$0: about your system, including any error possibly output -$0: before this message. Then install a modern shell, or -$0: manually run the script under such a shell if you do -$0: have one." - fi - exit 1 -fi -fi -fi -SHELL=${CONFIG_SHELL-/bin/sh} -export SHELL -# Unset more variables known to interfere with behavior of common tools. -CLICOLOR_FORCE= GREP_OPTIONS= -unset CLICOLOR_FORCE GREP_OPTIONS - -## --------------------- ## -## M4sh Shell Functions. ## -## --------------------- ## -# as_fn_unset VAR -# --------------- -# Portably unset VAR. -as_fn_unset () -{ - { eval $1=; unset $1;} -} -as_unset=as_fn_unset - -# as_fn_set_status STATUS -# ----------------------- -# Set $? to STATUS, without forking. -as_fn_set_status () -{ - return $1 -} # as_fn_set_status - -# as_fn_exit STATUS -# ----------------- -# Exit the shell with STATUS, even in a "trap 0" or "set -e" context. -as_fn_exit () -{ - set +e - as_fn_set_status $1 - exit $1 -} # as_fn_exit - -# as_fn_mkdir_p -# ------------- -# Create "$as_dir" as a directory, including parents if necessary. -as_fn_mkdir_p () -{ - - case $as_dir in #( - -*) as_dir=./$as_dir;; - esac - test -d "$as_dir" || eval $as_mkdir_p || { - as_dirs= - while :; do - case $as_dir in #( - *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( - *) as_qdir=$as_dir;; - esac - as_dirs="'$as_qdir' $as_dirs" - as_dir=`$as_dirname -- "$as_dir" || -$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ - X"$as_dir" : 'X\(//\)[^/]' \| \ - X"$as_dir" : 'X\(//\)$' \| \ - X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || -$as_echo X"$as_dir" | - sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ - s//\1/ - q - } - /^X\(\/\/\)[^/].*/{ - s//\1/ - q - } - /^X\(\/\/\)$/{ - s//\1/ - q - } - /^X\(\/\).*/{ - s//\1/ - q - } - s/.*/./; q'` - test -d "$as_dir" && break - done - test -z "$as_dirs" || eval "mkdir $as_dirs" - } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" - - -} # as_fn_mkdir_p - -# as_fn_executable_p FILE -# ----------------------- -# Test if FILE is an executable regular file. -as_fn_executable_p () -{ - test -f "$1" && test -x "$1" -} # as_fn_executable_p -# as_fn_append VAR VALUE -# ---------------------- -# Append the text in VALUE to the end of the definition contained in VAR. Take -# advantage of any shell optimizations that allow amortized linear growth over -# repeated appends, instead of the typical quadratic growth present in naive -# implementations. -if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : - eval 'as_fn_append () - { - eval $1+=\$2 - }' -else - as_fn_append () - { - eval $1=\$$1\$2 - } -fi # as_fn_append - -# as_fn_arith ARG... -# ------------------ -# Perform arithmetic evaluation on the ARGs, and store the result in the -# global $as_val. Take advantage of shells that can avoid forks. The arguments -# must be portable across $(()) and expr. -if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : - eval 'as_fn_arith () - { - as_val=$(( $* )) - }' -else - as_fn_arith () - { - as_val=`expr "$@" || test $? -eq 1` - } -fi # as_fn_arith - - -# as_fn_error STATUS ERROR [LINENO LOG_FD] -# ---------------------------------------- -# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are -# provided, also output the error to LOG_FD, referencing LINENO. Then exit the -# script with STATUS, using 1 if that was 0. -as_fn_error () -{ - as_status=$1; test $as_status -eq 0 && as_status=1 - if test "$4"; then - as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 - fi - $as_echo "$as_me: error: $2" >&2 - as_fn_exit $as_status -} # as_fn_error - -if expr a : '\(a\)' >/dev/null 2>&1 && - test "X`expr 00001 : '.*\(...\)'`" = X001; then - as_expr=expr -else - as_expr=false -fi - -if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then - as_basename=basename -else - as_basename=false -fi - -if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then - as_dirname=dirname -else - as_dirname=false -fi - -as_me=`$as_basename -- "$0" || -$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ - X"$0" : 'X\(//\)$' \| \ - X"$0" : 'X\(/\)' \| . 2>/dev/null || -$as_echo X/"$0" | - sed '/^.*\/\([^/][^/]*\)\/*$/{ - s//\1/ - q - } - /^X\/\(\/\/\)$/{ - s//\1/ - q - } - /^X\/\(\/\).*/{ - s//\1/ - q - } - s/.*/./; q'` - -# Avoid depending upon Character Ranges. -as_cr_letters='abcdefghijklmnopqrstuvwxyz' -as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' -as_cr_Letters=$as_cr_letters$as_cr_LETTERS -as_cr_digits='0123456789' -as_cr_alnum=$as_cr_Letters$as_cr_digits - - - as_lineno_1=$LINENO as_lineno_1a=$LINENO - as_lineno_2=$LINENO as_lineno_2a=$LINENO - eval 'test "x$as_lineno_1'$as_run'" != "x$as_lineno_2'$as_run'" && - test "x`expr $as_lineno_1'$as_run' + 1`" = "x$as_lineno_2'$as_run'"' || { - # Blame Lee E. McMahon (1931-1989) for sed's syntax. :-) - sed -n ' - p - /[$]LINENO/= - ' <$as_myself | - sed ' - s/[$]LINENO.*/&-/ - t lineno - b - :lineno - N - :loop - s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/ - t loop - s/-\n.*// - ' >$as_me.lineno && - chmod +x "$as_me.lineno" || - { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; } - - # If we had to re-execute with $CONFIG_SHELL, we're ensured to have - # already done that, so ensure we don't try to do so again and fall - # in an infinite loop. This has already happened in practice. - _as_can_reexec=no; export _as_can_reexec - # Don't try to exec as it changes $[0], causing all sort of problems - # (the dirname of $[0] is not the place where we might find the - # original and so on. Autoconf is especially sensitive to this). - . "./$as_me.lineno" - # Exit status is that of the last command. - exit -} - -ECHO_C= ECHO_N= ECHO_T= -case `echo -n x` in #((((( --n*) - case `echo 'xy\c'` in - *c*) ECHO_T=' ';; # ECHO_T is single tab character. - xy) ECHO_C='\c';; - *) echo `echo ksh88 bug on AIX 6.1` > /dev/null - ECHO_T=' ';; - esac;; -*) - ECHO_N='-n';; -esac - -rm -f conf$$ conf$$.exe conf$$.file -if test -d conf$$.dir; then - rm -f conf$$.dir/conf$$.file -else - rm -f conf$$.dir - mkdir conf$$.dir 2>/dev/null -fi -if (echo >conf$$.file) 2>/dev/null; then - if ln -s conf$$.file conf$$ 2>/dev/null; then - as_ln_s='ln -s' - # ... but there are two gotchas: - # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. - # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. - # In both cases, we have to default to `cp -pR'. - ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || - as_ln_s='cp -pR' - elif ln conf$$.file conf$$ 2>/dev/null; then - as_ln_s=ln - else - as_ln_s='cp -pR' - fi -else - as_ln_s='cp -pR' -fi -rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file -rmdir conf$$.dir 2>/dev/null - -if mkdir -p . 2>/dev/null; then - as_mkdir_p='mkdir -p "$as_dir"' -else - test -d ./-p && rmdir ./-p - as_mkdir_p=false -fi - -as_test_x='test -x' -as_executable_p=as_fn_executable_p - -# Sed expression to map a string onto a valid CPP name. -as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" - -# Sed expression to map a string onto a valid variable name. -as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" - - -test -n "$DJDIR" || exec 7<&0 &1 - -# Name of the host. -# hostname on some systems (SVR3.2, old GNU/Linux) returns a bogus exit status, -# so uname gets run too. -ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` - -# -# Initializations. -# -ac_default_prefix=/usr/local -ac_clean_files= -ac_config_libobj_dir=. -LIBOBJS= -cross_compiling=no -subdirs= -MFLAGS= -MAKEFLAGS= - -# Identity of this package. -PACKAGE_NAME='igraph' -PACKAGE_TARNAME='igraph' -PACKAGE_VERSION='1.3.0.9016' -PACKAGE_STRING='igraph 1.3.0.9016' -PACKAGE_BUGREPORT='ntamas@gmail.com' -PACKAGE_URL='' - -ac_unique_file="src/rigraph/rinterface.c" -# Factoring default headers for most tests. -ac_includes_default="\ -#include -#ifdef HAVE_SYS_TYPES_H -# include -#endif -#ifdef HAVE_SYS_STAT_H -# include -#endif -#ifdef STDC_HEADERS -# include -# include -#else -# ifdef HAVE_STDLIB_H -# include -# endif -#endif -#ifdef HAVE_STRING_H -# if !defined STDC_HEADERS && defined HAVE_MEMORY_H -# include -# endif -# include -#endif -#ifdef HAVE_STRINGS_H -# include -#endif -#ifdef HAVE_INTTYPES_H -# include -#endif -#ifdef HAVE_STDINT_H -# include -#endif -#ifdef HAVE_UNISTD_H -# include -#endif" - -ac_subst_vars='LTLIBOBJS -LIBOBJS -GLPK_LIBS -HAVE_GLPK -GMP_LIBS -INTERNAL_GMP -HAVE_GMP -XML2_CPPFLAGS -XML2_LIBS -HAVE_LIBXML -XML2CONFIG -EGREP -GREP -CXXCPP -ac_ct_FC -FCFLAGS -FC -ac_ct_CXX -CXXFLAGS -CXX -OBJEXT -EXEEXT -ac_ct_CC -CPPFLAGS -LDFLAGS -CFLAGS -CC -target_alias -host_alias -build_alias -LIBS -ECHO_T -ECHO_N -ECHO_C -DEFS -mandir -localedir -libdir -psdir -pdfdir -dvidir -htmldir -infodir -docdir -oldincludedir -includedir -runstatedir -localstatedir -sharedstatedir -sysconfdir -datadir -datarootdir -libexecdir -sbindir -bindir -program_transform_name -prefix -exec_prefix -PACKAGE_URL -PACKAGE_BUGREPORT -PACKAGE_STRING -PACKAGE_VERSION -PACKAGE_TARNAME -PACKAGE_NAME -PATH_SEPARATOR -SHELL' -ac_subst_files='' -ac_user_opts=' -enable_option_checking -enable_graphml -enable_glpk -' - ac_precious_vars='build_alias -host_alias -target_alias -CC -CFLAGS -LDFLAGS -LIBS -CPPFLAGS -CXX -CXXFLAGS -CCC -FC -FCFLAGS -CXXCPP' - - -# Initialize some variables set by options. -ac_init_help= -ac_init_version=false -ac_unrecognized_opts= -ac_unrecognized_sep= -# The variables have the same names as the options, with -# dashes changed to underlines. -cache_file=/dev/null -exec_prefix=NONE -no_create= -no_recursion= -prefix=NONE -program_prefix=NONE -program_suffix=NONE -program_transform_name=s,x,x, -silent= -site= -srcdir= -verbose= -x_includes=NONE -x_libraries=NONE - -# Installation directory options. -# These are left unexpanded so users can "make install exec_prefix=/foo" -# and all the variables that are supposed to be based on exec_prefix -# by default will actually change. -# Use braces instead of parens because sh, perl, etc. also accept them. -# (The list follows the same order as the GNU Coding Standards.) -bindir='${exec_prefix}/bin' -sbindir='${exec_prefix}/sbin' -libexecdir='${exec_prefix}/libexec' -datarootdir='${prefix}/share' -datadir='${datarootdir}' -sysconfdir='${prefix}/etc' -sharedstatedir='${prefix}/com' -localstatedir='${prefix}/var' -runstatedir='${localstatedir}/run' -includedir='${prefix}/include' -oldincludedir='/usr/include' -docdir='${datarootdir}/doc/${PACKAGE_TARNAME}' -infodir='${datarootdir}/info' -htmldir='${docdir}' -dvidir='${docdir}' -pdfdir='${docdir}' -psdir='${docdir}' -libdir='${exec_prefix}/lib' -localedir='${datarootdir}/locale' -mandir='${datarootdir}/man' - -ac_prev= -ac_dashdash= -for ac_option -do - # If the previous option needs an argument, assign it. - if test -n "$ac_prev"; then - eval $ac_prev=\$ac_option - ac_prev= - continue - fi - - case $ac_option in - *=?*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;; - *=) ac_optarg= ;; - *) ac_optarg=yes ;; - esac - - # Accept the important Cygnus configure options, so we can diagnose typos. - - case $ac_dashdash$ac_option in - --) - ac_dashdash=yes ;; - - -bindir | --bindir | --bindi | --bind | --bin | --bi) - ac_prev=bindir ;; - -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) - bindir=$ac_optarg ;; - - -build | --build | --buil | --bui | --bu) - ac_prev=build_alias ;; - -build=* | --build=* | --buil=* | --bui=* | --bu=*) - build_alias=$ac_optarg ;; - - -cache-file | --cache-file | --cache-fil | --cache-fi \ - | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) - ac_prev=cache_file ;; - -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ - | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) - cache_file=$ac_optarg ;; - - --config-cache | -C) - cache_file=config.cache ;; - - -datadir | --datadir | --datadi | --datad) - ac_prev=datadir ;; - -datadir=* | --datadir=* | --datadi=* | --datad=*) - datadir=$ac_optarg ;; - - -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \ - | --dataroo | --dataro | --datar) - ac_prev=datarootdir ;; - -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \ - | --dataroot=* | --dataroo=* | --dataro=* | --datar=*) - datarootdir=$ac_optarg ;; - - -disable-* | --disable-*) - ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'` - # Reject names that are not valid shell variable names. - expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && - as_fn_error $? "invalid feature name: $ac_useropt" - ac_useropt_orig=$ac_useropt - ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` - case $ac_user_opts in - *" -"enable_$ac_useropt" -"*) ;; - *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig" - ac_unrecognized_sep=', ';; - esac - eval enable_$ac_useropt=no ;; - - -docdir | --docdir | --docdi | --doc | --do) - ac_prev=docdir ;; - -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*) - docdir=$ac_optarg ;; - - -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv) - ac_prev=dvidir ;; - -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*) - dvidir=$ac_optarg ;; - - -enable-* | --enable-*) - ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` - # Reject names that are not valid shell variable names. - expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && - as_fn_error $? "invalid feature name: $ac_useropt" - ac_useropt_orig=$ac_useropt - ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` - case $ac_user_opts in - *" -"enable_$ac_useropt" -"*) ;; - *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig" - ac_unrecognized_sep=', ';; - esac - eval enable_$ac_useropt=\$ac_optarg ;; - - -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ - | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ - | --exec | --exe | --ex) - ac_prev=exec_prefix ;; - -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ - | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ - | --exec=* | --exe=* | --ex=*) - exec_prefix=$ac_optarg ;; - - -gas | --gas | --ga | --g) - # Obsolete; use --with-gas. - with_gas=yes ;; - - -help | --help | --hel | --he | -h) - ac_init_help=long ;; - -help=r* | --help=r* | --hel=r* | --he=r* | -hr*) - ac_init_help=recursive ;; - -help=s* | --help=s* | --hel=s* | --he=s* | -hs*) - ac_init_help=short ;; - - -host | --host | --hos | --ho) - ac_prev=host_alias ;; - -host=* | --host=* | --hos=* | --ho=*) - host_alias=$ac_optarg ;; - - -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht) - ac_prev=htmldir ;; - -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \ - | --ht=*) - htmldir=$ac_optarg ;; - - -includedir | --includedir | --includedi | --included | --include \ - | --includ | --inclu | --incl | --inc) - ac_prev=includedir ;; - -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ - | --includ=* | --inclu=* | --incl=* | --inc=*) - includedir=$ac_optarg ;; - - -infodir | --infodir | --infodi | --infod | --info | --inf) - ac_prev=infodir ;; - -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) - infodir=$ac_optarg ;; - - -libdir | --libdir | --libdi | --libd) - ac_prev=libdir ;; - -libdir=* | --libdir=* | --libdi=* | --libd=*) - libdir=$ac_optarg ;; - - -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ - | --libexe | --libex | --libe) - ac_prev=libexecdir ;; - -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ - | --libexe=* | --libex=* | --libe=*) - libexecdir=$ac_optarg ;; - - -localedir | --localedir | --localedi | --localed | --locale) - ac_prev=localedir ;; - -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*) - localedir=$ac_optarg ;; - - -localstatedir | --localstatedir | --localstatedi | --localstated \ - | --localstate | --localstat | --localsta | --localst | --locals) - ac_prev=localstatedir ;; - -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ - | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*) - localstatedir=$ac_optarg ;; - - -mandir | --mandir | --mandi | --mand | --man | --ma | --m) - ac_prev=mandir ;; - -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) - mandir=$ac_optarg ;; - - -nfp | --nfp | --nf) - # Obsolete; use --without-fp. - with_fp=no ;; - - -no-create | --no-create | --no-creat | --no-crea | --no-cre \ - | --no-cr | --no-c | -n) - no_create=yes ;; - - -no-recursion | --no-recursion | --no-recursio | --no-recursi \ - | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) - no_recursion=yes ;; - - -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ - | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ - | --oldin | --oldi | --old | --ol | --o) - ac_prev=oldincludedir ;; - -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ - | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ - | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) - oldincludedir=$ac_optarg ;; - - -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) - ac_prev=prefix ;; - -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) - prefix=$ac_optarg ;; - - -program-prefix | --program-prefix | --program-prefi | --program-pref \ - | --program-pre | --program-pr | --program-p) - ac_prev=program_prefix ;; - -program-prefix=* | --program-prefix=* | --program-prefi=* \ - | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) - program_prefix=$ac_optarg ;; - - -program-suffix | --program-suffix | --program-suffi | --program-suff \ - | --program-suf | --program-su | --program-s) - ac_prev=program_suffix ;; - -program-suffix=* | --program-suffix=* | --program-suffi=* \ - | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) - program_suffix=$ac_optarg ;; - - -program-transform-name | --program-transform-name \ - | --program-transform-nam | --program-transform-na \ - | --program-transform-n | --program-transform- \ - | --program-transform | --program-transfor \ - | --program-transfo | --program-transf \ - | --program-trans | --program-tran \ - | --progr-tra | --program-tr | --program-t) - ac_prev=program_transform_name ;; - -program-transform-name=* | --program-transform-name=* \ - | --program-transform-nam=* | --program-transform-na=* \ - | --program-transform-n=* | --program-transform-=* \ - | --program-transform=* | --program-transfor=* \ - | --program-transfo=* | --program-transf=* \ - | --program-trans=* | --program-tran=* \ - | --progr-tra=* | --program-tr=* | --program-t=*) - program_transform_name=$ac_optarg ;; - - -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd) - ac_prev=pdfdir ;; - -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*) - pdfdir=$ac_optarg ;; - - -psdir | --psdir | --psdi | --psd | --ps) - ac_prev=psdir ;; - -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*) - psdir=$ac_optarg ;; - - -q | -quiet | --quiet | --quie | --qui | --qu | --q \ - | -silent | --silent | --silen | --sile | --sil) - silent=yes ;; - - -runstatedir | --runstatedir | --runstatedi | --runstated \ - | --runstate | --runstat | --runsta | --runst | --runs \ - | --run | --ru | --r) - ac_prev=runstatedir ;; - -runstatedir=* | --runstatedir=* | --runstatedi=* | --runstated=* \ - | --runstate=* | --runstat=* | --runsta=* | --runst=* | --runs=* \ - | --run=* | --ru=* | --r=*) - runstatedir=$ac_optarg ;; - - -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) - ac_prev=sbindir ;; - -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ - | --sbi=* | --sb=*) - sbindir=$ac_optarg ;; - - -sharedstatedir | --sharedstatedir | --sharedstatedi \ - | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ - | --sharedst | --shareds | --shared | --share | --shar \ - | --sha | --sh) - ac_prev=sharedstatedir ;; - -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ - | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ - | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ - | --sha=* | --sh=*) - sharedstatedir=$ac_optarg ;; - - -site | --site | --sit) - ac_prev=site ;; - -site=* | --site=* | --sit=*) - site=$ac_optarg ;; - - -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) - ac_prev=srcdir ;; - -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) - srcdir=$ac_optarg ;; - - -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ - | --syscon | --sysco | --sysc | --sys | --sy) - ac_prev=sysconfdir ;; - -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ - | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) - sysconfdir=$ac_optarg ;; - - -target | --target | --targe | --targ | --tar | --ta | --t) - ac_prev=target_alias ;; - -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) - target_alias=$ac_optarg ;; - - -v | -verbose | --verbose | --verbos | --verbo | --verb) - verbose=yes ;; - - -version | --version | --versio | --versi | --vers | -V) - ac_init_version=: ;; - - -with-* | --with-*) - ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` - # Reject names that are not valid shell variable names. - expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && - as_fn_error $? "invalid package name: $ac_useropt" - ac_useropt_orig=$ac_useropt - ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` - case $ac_user_opts in - *" -"with_$ac_useropt" -"*) ;; - *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig" - ac_unrecognized_sep=', ';; - esac - eval with_$ac_useropt=\$ac_optarg ;; - - -without-* | --without-*) - ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'` - # Reject names that are not valid shell variable names. - expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && - as_fn_error $? "invalid package name: $ac_useropt" - ac_useropt_orig=$ac_useropt - ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` - case $ac_user_opts in - *" -"with_$ac_useropt" -"*) ;; - *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig" - ac_unrecognized_sep=', ';; - esac - eval with_$ac_useropt=no ;; - - --x) - # Obsolete; use --with-x. - with_x=yes ;; - - -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ - | --x-incl | --x-inc | --x-in | --x-i) - ac_prev=x_includes ;; - -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ - | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) - x_includes=$ac_optarg ;; - - -x-libraries | --x-libraries | --x-librarie | --x-librari \ - | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) - ac_prev=x_libraries ;; - -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ - | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) - x_libraries=$ac_optarg ;; - - -*) as_fn_error $? "unrecognized option: \`$ac_option' -Try \`$0 --help' for more information" - ;; - - *=*) - ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` - # Reject names that are not valid shell variable names. - case $ac_envvar in #( - '' | [0-9]* | *[!_$as_cr_alnum]* ) - as_fn_error $? "invalid variable name: \`$ac_envvar'" ;; - esac - eval $ac_envvar=\$ac_optarg - export $ac_envvar ;; - - *) - # FIXME: should be removed in autoconf 3.0. - $as_echo "$as_me: WARNING: you should use --build, --host, --target" >&2 - expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && - $as_echo "$as_me: WARNING: invalid host type: $ac_option" >&2 - : "${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}" - ;; - - esac -done - -if test -n "$ac_prev"; then - ac_option=--`echo $ac_prev | sed 's/_/-/g'` - as_fn_error $? "missing argument to $ac_option" -fi - -if test -n "$ac_unrecognized_opts"; then - case $enable_option_checking in - no) ;; - fatal) as_fn_error $? "unrecognized options: $ac_unrecognized_opts" ;; - *) $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;; - esac -fi - -# Check all directory arguments for consistency. -for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ - datadir sysconfdir sharedstatedir localstatedir includedir \ - oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ - libdir localedir mandir runstatedir -do - eval ac_val=\$$ac_var - # Remove trailing slashes. - case $ac_val in - */ ) - ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'` - eval $ac_var=\$ac_val;; - esac - # Be sure to have absolute directory names. - case $ac_val in - [\\/$]* | ?:[\\/]* ) continue;; - NONE | '' ) case $ac_var in *prefix ) continue;; esac;; - esac - as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val" -done - -# There might be people who depend on the old broken behavior: `$host' -# used to hold the argument of --host etc. -# FIXME: To remove some day. -build=$build_alias -host=$host_alias -target=$target_alias - -# FIXME: To remove some day. -if test "x$host_alias" != x; then - if test "x$build_alias" = x; then - cross_compiling=maybe - elif test "x$build_alias" != "x$host_alias"; then - cross_compiling=yes - fi -fi - -ac_tool_prefix= -test -n "$host_alias" && ac_tool_prefix=$host_alias- - -test "$silent" = yes && exec 6>/dev/null - - -ac_pwd=`pwd` && test -n "$ac_pwd" && -ac_ls_di=`ls -di .` && -ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` || - as_fn_error $? "working directory cannot be determined" -test "X$ac_ls_di" = "X$ac_pwd_ls_di" || - as_fn_error $? "pwd does not report name of working directory" - - -# Find the source files, if location was not specified. -if test -z "$srcdir"; then - ac_srcdir_defaulted=yes - # Try the directory containing this script, then the parent directory. - ac_confdir=`$as_dirname -- "$as_myself" || -$as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ - X"$as_myself" : 'X\(//\)[^/]' \| \ - X"$as_myself" : 'X\(//\)$' \| \ - X"$as_myself" : 'X\(/\)' \| . 2>/dev/null || -$as_echo X"$as_myself" | - sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ - s//\1/ - q - } - /^X\(\/\/\)[^/].*/{ - s//\1/ - q - } - /^X\(\/\/\)$/{ - s//\1/ - q - } - /^X\(\/\).*/{ - s//\1/ - q - } - s/.*/./; q'` - srcdir=$ac_confdir - if test ! -r "$srcdir/$ac_unique_file"; then - srcdir=.. - fi -else - ac_srcdir_defaulted=no -fi -if test ! -r "$srcdir/$ac_unique_file"; then - test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .." - as_fn_error $? "cannot find sources ($ac_unique_file) in $srcdir" -fi -ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work" -ac_abs_confdir=`( - cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error $? "$ac_msg" - pwd)` -# When building in place, set srcdir=. -if test "$ac_abs_confdir" = "$ac_pwd"; then - srcdir=. -fi -# Remove unnecessary trailing slashes from srcdir. -# Double slashes in file names in object file debugging info -# mess up M-x gdb in Emacs. -case $srcdir in -*/) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;; -esac -for ac_var in $ac_precious_vars; do - eval ac_env_${ac_var}_set=\${${ac_var}+set} - eval ac_env_${ac_var}_value=\$${ac_var} - eval ac_cv_env_${ac_var}_set=\${${ac_var}+set} - eval ac_cv_env_${ac_var}_value=\$${ac_var} -done - -# -# Report the --help message. -# -if test "$ac_init_help" = "long"; then - # Omit some internal or obsolete options to make the list less imposing. - # This message is too long to be a string in the A/UX 3.1 sh. - cat <<_ACEOF -\`configure' configures igraph 1.3.0.9016 to adapt to many kinds of systems. - -Usage: $0 [OPTION]... [VAR=VALUE]... - -To assign environment variables (e.g., CC, CFLAGS...), specify them as -VAR=VALUE. See below for descriptions of some of the useful variables. - -Defaults for the options are specified in brackets. - -Configuration: - -h, --help display this help and exit - --help=short display options specific to this package - --help=recursive display the short help of all the included packages - -V, --version display version information and exit - -q, --quiet, --silent do not print \`checking ...' messages - --cache-file=FILE cache test results in FILE [disabled] - -C, --config-cache alias for \`--cache-file=config.cache' - -n, --no-create do not create output files - --srcdir=DIR find the sources in DIR [configure dir or \`..'] - -Installation directories: - --prefix=PREFIX install architecture-independent files in PREFIX - [$ac_default_prefix] - --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX - [PREFIX] - -By default, \`make install' will install all the files in -\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify -an installation prefix other than \`$ac_default_prefix' using \`--prefix', -for instance \`--prefix=\$HOME'. - -For better control, use the options below. - -Fine tuning of the installation directories: - --bindir=DIR user executables [EPREFIX/bin] - --sbindir=DIR system admin executables [EPREFIX/sbin] - --libexecdir=DIR program executables [EPREFIX/libexec] - --sysconfdir=DIR read-only single-machine data [PREFIX/etc] - --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] - --localstatedir=DIR modifiable single-machine data [PREFIX/var] - --runstatedir=DIR modifiable per-process data [LOCALSTATEDIR/run] - --libdir=DIR object code libraries [EPREFIX/lib] - --includedir=DIR C header files [PREFIX/include] - --oldincludedir=DIR C header files for non-gcc [/usr/include] - --datarootdir=DIR read-only arch.-independent data root [PREFIX/share] - --datadir=DIR read-only architecture-independent data [DATAROOTDIR] - --infodir=DIR info documentation [DATAROOTDIR/info] - --localedir=DIR locale-dependent data [DATAROOTDIR/locale] - --mandir=DIR man documentation [DATAROOTDIR/man] - --docdir=DIR documentation root [DATAROOTDIR/doc/igraph] - --htmldir=DIR html documentation [DOCDIR] - --dvidir=DIR dvi documentation [DOCDIR] - --pdfdir=DIR pdf documentation [DOCDIR] - --psdir=DIR ps documentation [DOCDIR] -_ACEOF - - cat <<\_ACEOF -_ACEOF -fi - -if test -n "$ac_init_help"; then - case $ac_init_help in - short | recursive ) echo "Configuration of igraph 1.3.0.9016:";; - esac - cat <<\_ACEOF - -Optional Features: - --disable-option-checking ignore unrecognized --enable/--with options - --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) - --enable-FEATURE[=ARG] include FEATURE [ARG=yes] - --disable-graphml Disable support for GraphML format - --disable-glpk Compile without the GLPK library - -Some influential environment variables: - CC C compiler command - CFLAGS C compiler flags - LDFLAGS linker flags, e.g. -L if you have libraries in a - nonstandard directory - LIBS libraries to pass to the linker, e.g. -l - CPPFLAGS (Objective) C/C++ preprocessor flags, e.g. -I if - you have headers in a nonstandard directory - CXX C++ compiler command - CXXFLAGS C++ compiler flags - FC Fortran compiler command - FCFLAGS Fortran compiler flags - CXXCPP C++ preprocessor - -Use these variables to override the choices made by `configure' or to help -it to find libraries and programs with nonstandard names/locations. - -Report bugs to . -_ACEOF -ac_status=$? -fi - -if test "$ac_init_help" = "recursive"; then - # If there are subdirs, report their specific --help. - for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue - test -d "$ac_dir" || - { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } || - continue - ac_builddir=. - -case "$ac_dir" in -.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; -*) - ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` - # A ".." for each directory in $ac_dir_suffix. - ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` - case $ac_top_builddir_sub in - "") ac_top_builddir_sub=. ac_top_build_prefix= ;; - *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; - esac ;; -esac -ac_abs_top_builddir=$ac_pwd -ac_abs_builddir=$ac_pwd$ac_dir_suffix -# for backward compatibility: -ac_top_builddir=$ac_top_build_prefix - -case $srcdir in - .) # We are building in place. - ac_srcdir=. - ac_top_srcdir=$ac_top_builddir_sub - ac_abs_top_srcdir=$ac_pwd ;; - [\\/]* | ?:[\\/]* ) # Absolute name. - ac_srcdir=$srcdir$ac_dir_suffix; - ac_top_srcdir=$srcdir - ac_abs_top_srcdir=$srcdir ;; - *) # Relative name. - ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix - ac_top_srcdir=$ac_top_build_prefix$srcdir - ac_abs_top_srcdir=$ac_pwd/$srcdir ;; -esac -ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix - - cd "$ac_dir" || { ac_status=$?; continue; } - # Check for guested configure. - if test -f "$ac_srcdir/configure.gnu"; then - echo && - $SHELL "$ac_srcdir/configure.gnu" --help=recursive - elif test -f "$ac_srcdir/configure"; then - echo && - $SHELL "$ac_srcdir/configure" --help=recursive - else - $as_echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2 - fi || ac_status=$? - cd "$ac_pwd" || { ac_status=$?; break; } - done -fi - -test -n "$ac_init_help" && exit $ac_status -if $ac_init_version; then - cat <<\_ACEOF -igraph configure 1.3.0.9016 -generated by GNU Autoconf 2.69 - -Copyright (C) 2012 Free Software Foundation, Inc. -This configure script is free software; the Free Software Foundation -gives unlimited permission to copy, distribute and modify it. -_ACEOF - exit -fi - -## ------------------------ ## -## Autoconf initialization. ## -## ------------------------ ## - -# ac_fn_c_try_compile LINENO -# -------------------------- -# Try to compile conftest.$ac_ext, and return whether this succeeded. -ac_fn_c_try_compile () -{ - as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - rm -f conftest.$ac_objext - if { { ac_try="$ac_compile" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 - (eval "$ac_compile") 2>conftest.err - ac_status=$? - if test -s conftest.err; then - grep -v '^ *+' conftest.err >conftest.er1 - cat conftest.er1 >&5 - mv -f conftest.er1 conftest.err - fi - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; } && { - test -z "$ac_c_werror_flag" || - test ! -s conftest.err - } && test -s conftest.$ac_objext; then : - ac_retval=0 -else - $as_echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - ac_retval=1 -fi - eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno - as_fn_set_status $ac_retval - -} # ac_fn_c_try_compile - -# ac_fn_cxx_try_compile LINENO -# ---------------------------- -# Try to compile conftest.$ac_ext, and return whether this succeeded. -ac_fn_cxx_try_compile () -{ - as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - rm -f conftest.$ac_objext - if { { ac_try="$ac_compile" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 - (eval "$ac_compile") 2>conftest.err - ac_status=$? - if test -s conftest.err; then - grep -v '^ *+' conftest.err >conftest.er1 - cat conftest.er1 >&5 - mv -f conftest.er1 conftest.err - fi - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; } && { - test -z "$ac_cxx_werror_flag" || - test ! -s conftest.err - } && test -s conftest.$ac_objext; then : - ac_retval=0 -else - $as_echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - ac_retval=1 -fi - eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno - as_fn_set_status $ac_retval - -} # ac_fn_cxx_try_compile - -# ac_fn_fc_try_compile LINENO -# --------------------------- -# Try to compile conftest.$ac_ext, and return whether this succeeded. -ac_fn_fc_try_compile () -{ - as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - rm -f conftest.$ac_objext - if { { ac_try="$ac_compile" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 - (eval "$ac_compile") 2>conftest.err - ac_status=$? - if test -s conftest.err; then - grep -v '^ *+' conftest.err >conftest.er1 - cat conftest.er1 >&5 - mv -f conftest.er1 conftest.err - fi - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; } && { - test -z "$ac_fc_werror_flag" || - test ! -s conftest.err - } && test -s conftest.$ac_objext; then : - ac_retval=0 -else - $as_echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - ac_retval=1 -fi - eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno - as_fn_set_status $ac_retval - -} # ac_fn_fc_try_compile - -# ac_fn_cxx_try_link LINENO -# ------------------------- -# Try to link conftest.$ac_ext, and return whether this succeeded. -ac_fn_cxx_try_link () -{ - as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - rm -f conftest.$ac_objext conftest$ac_exeext - if { { ac_try="$ac_link" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 - (eval "$ac_link") 2>conftest.err - ac_status=$? - if test -s conftest.err; then - grep -v '^ *+' conftest.err >conftest.er1 - cat conftest.er1 >&5 - mv -f conftest.er1 conftest.err - fi - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; } && { - test -z "$ac_cxx_werror_flag" || - test ! -s conftest.err - } && test -s conftest$ac_exeext && { - test "$cross_compiling" = yes || - test -x conftest$ac_exeext - }; then : - ac_retval=0 -else - $as_echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - ac_retval=1 -fi - # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information - # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would - # interfere with the next link command; also delete a directory that is - # left behind by Apple's compiler. We do this before executing the actions. - rm -rf conftest.dSYM conftest_ipa8_conftest.oo - eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno - as_fn_set_status $ac_retval - -} # ac_fn_cxx_try_link - -# ac_fn_cxx_check_func LINENO FUNC VAR -# ------------------------------------ -# Tests whether FUNC exists, setting the cache variable VAR accordingly -ac_fn_cxx_check_func () -{ - as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 -$as_echo_n "checking for $2... " >&6; } -if eval \${$3+:} false; then : - $as_echo_n "(cached) " >&6 -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -/* Define $2 to an innocuous variant, in case declares $2. - For example, HP-UX 11i declares gettimeofday. */ -#define $2 innocuous_$2 - -/* System header to define __stub macros and hopefully few prototypes, - which can conflict with char $2 (); below. - Prefer to if __STDC__ is defined, since - exists even on freestanding compilers. */ - -#ifdef __STDC__ -# include -#else -# include -#endif - -#undef $2 - -/* Override any GCC internal prototype to avoid an error. - Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -#ifdef __cplusplus -extern "C" -#endif -char $2 (); -/* The GNU C library defines this for functions which it implements - to always fail with ENOSYS. Some functions are actually named - something starting with __ and the normal name is an alias. */ -#if defined __stub_$2 || defined __stub___$2 -choke me -#endif - -int -main () -{ -return $2 (); - ; - return 0; -} -_ACEOF -if ac_fn_cxx_try_link "$LINENO"; then : - eval "$3=yes" -else - eval "$3=no" -fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext -fi -eval ac_res=\$$3 - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 -$as_echo "$ac_res" >&6; } - eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno - -} # ac_fn_cxx_check_func - -# ac_fn_cxx_try_cpp LINENO -# ------------------------ -# Try to preprocess conftest.$ac_ext, and return whether this succeeded. -ac_fn_cxx_try_cpp () -{ - as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - if { { ac_try="$ac_cpp conftest.$ac_ext" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 - (eval "$ac_cpp conftest.$ac_ext") 2>conftest.err - ac_status=$? - if test -s conftest.err; then - grep -v '^ *+' conftest.err >conftest.er1 - cat conftest.er1 >&5 - mv -f conftest.er1 conftest.err - fi - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; } > conftest.i && { - test -z "$ac_cxx_preproc_warn_flag$ac_cxx_werror_flag" || - test ! -s conftest.err - }; then : - ac_retval=0 -else - $as_echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - ac_retval=1 -fi - eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno - as_fn_set_status $ac_retval - -} # ac_fn_cxx_try_cpp - -# ac_fn_cxx_check_header_mongrel LINENO HEADER VAR INCLUDES -# --------------------------------------------------------- -# Tests whether HEADER exists, giving a warning if it cannot be compiled using -# the include files in INCLUDES and setting the cache variable VAR -# accordingly. -ac_fn_cxx_check_header_mongrel () -{ - as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - if eval \${$3+:} false; then : - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 -$as_echo_n "checking for $2... " >&6; } -if eval \${$3+:} false; then : - $as_echo_n "(cached) " >&6 -fi -eval ac_res=\$$3 - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 -$as_echo "$ac_res" >&6; } -else - # Is the header compilable? -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 usability" >&5 -$as_echo_n "checking $2 usability... " >&6; } -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -$4 -#include <$2> -_ACEOF -if ac_fn_cxx_try_compile "$LINENO"; then : - ac_header_compiler=yes -else - ac_header_compiler=no -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_compiler" >&5 -$as_echo "$ac_header_compiler" >&6; } - -# Is the header present? -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 presence" >&5 -$as_echo_n "checking $2 presence... " >&6; } -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include <$2> -_ACEOF -if ac_fn_cxx_try_cpp "$LINENO"; then : - ac_header_preproc=yes -else - ac_header_preproc=no -fi -rm -f conftest.err conftest.i conftest.$ac_ext -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_preproc" >&5 -$as_echo "$ac_header_preproc" >&6; } - -# So? What about this header? -case $ac_header_compiler:$ac_header_preproc:$ac_cxx_preproc_warn_flag in #(( - yes:no: ) - { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&5 -$as_echo "$as_me: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&2;} - { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 -$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} - ;; - no:yes:* ) - { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: present but cannot be compiled" >&5 -$as_echo "$as_me: WARNING: $2: present but cannot be compiled" >&2;} - { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: check for missing prerequisite headers?" >&5 -$as_echo "$as_me: WARNING: $2: check for missing prerequisite headers?" >&2;} - { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: see the Autoconf documentation" >&5 -$as_echo "$as_me: WARNING: $2: see the Autoconf documentation" >&2;} - { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&5 -$as_echo "$as_me: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&2;} - { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 -$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} -( $as_echo "## ------------------------------- ## -## Report this to ntamas@gmail.com ## -## ------------------------------- ##" - ) | sed "s/^/$as_me: WARNING: /" >&2 - ;; -esac - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 -$as_echo_n "checking for $2... " >&6; } -if eval \${$3+:} false; then : - $as_echo_n "(cached) " >&6 -else - eval "$3=\$ac_header_compiler" -fi -eval ac_res=\$$3 - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 -$as_echo "$ac_res" >&6; } -fi - eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno - -} # ac_fn_cxx_check_header_mongrel - -# ac_fn_cxx_try_run LINENO -# ------------------------ -# Try to link conftest.$ac_ext, and return whether this succeeded. Assumes -# that executables *can* be run. -ac_fn_cxx_try_run () -{ - as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - if { { ac_try="$ac_link" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 - (eval "$ac_link") 2>&5 - ac_status=$? - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; } && { ac_try='./conftest$ac_exeext' - { { case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 - (eval "$ac_try") 2>&5 - ac_status=$? - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; }; }; then : - ac_retval=0 -else - $as_echo "$as_me: program exited with status $ac_status" >&5 - $as_echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - ac_retval=$ac_status -fi - rm -rf conftest.dSYM conftest_ipa8_conftest.oo - eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno - as_fn_set_status $ac_retval - -} # ac_fn_cxx_try_run - -# ac_fn_cxx_check_header_compile LINENO HEADER VAR INCLUDES -# --------------------------------------------------------- -# Tests whether HEADER exists and can be compiled using the include files in -# INCLUDES, setting the cache variable VAR accordingly. -ac_fn_cxx_check_header_compile () -{ - as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 -$as_echo_n "checking for $2... " >&6; } -if eval \${$3+:} false; then : - $as_echo_n "(cached) " >&6 -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -$4 -#include <$2> -_ACEOF -if ac_fn_cxx_try_compile "$LINENO"; then : - eval "$3=yes" -else - eval "$3=no" -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -fi -eval ac_res=\$$3 - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 -$as_echo "$ac_res" >&6; } - eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno - -} # ac_fn_cxx_check_header_compile - -# ac_fn_cxx_check_member LINENO AGGR MEMBER VAR INCLUDES -# ------------------------------------------------------ -# Tries to find if the field MEMBER exists in type AGGR, after including -# INCLUDES, setting cache variable VAR accordingly. -ac_fn_cxx_check_member () -{ - as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2.$3" >&5 -$as_echo_n "checking for $2.$3... " >&6; } -if eval \${$4+:} false; then : - $as_echo_n "(cached) " >&6 -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -$5 -int -main () -{ -static $2 ac_aggr; -if (ac_aggr.$3) -return 0; - ; - return 0; -} -_ACEOF -if ac_fn_cxx_try_compile "$LINENO"; then : - eval "$4=yes" -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -$5 -int -main () -{ -static $2 ac_aggr; -if (sizeof ac_aggr.$3) -return 0; - ; - return 0; -} -_ACEOF -if ac_fn_cxx_try_compile "$LINENO"; then : - eval "$4=yes" -else - eval "$4=no" -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -fi -eval ac_res=\$$4 - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 -$as_echo "$ac_res" >&6; } - eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno - -} # ac_fn_cxx_check_member -cat >config.log <<_ACEOF -This file contains any messages produced by compilers while -running configure, to aid debugging if configure makes a mistake. - -It was created by igraph $as_me 1.3.0.9016, which was -generated by GNU Autoconf 2.69. Invocation command line was - - $ $0 $@ - -_ACEOF -exec 5>>config.log -{ -cat <<_ASUNAME -## --------- ## -## Platform. ## -## --------- ## - -hostname = `(hostname || uname -n) 2>/dev/null | sed 1q` -uname -m = `(uname -m) 2>/dev/null || echo unknown` -uname -r = `(uname -r) 2>/dev/null || echo unknown` -uname -s = `(uname -s) 2>/dev/null || echo unknown` -uname -v = `(uname -v) 2>/dev/null || echo unknown` - -/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown` -/bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` - -/bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` -/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` -/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` -/usr/bin/hostinfo = `(/usr/bin/hostinfo) 2>/dev/null || echo unknown` -/bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` -/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` -/bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` - -_ASUNAME - -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - $as_echo "PATH: $as_dir" - done -IFS=$as_save_IFS - -} >&5 - -cat >&5 <<_ACEOF - - -## ----------- ## -## Core tests. ## -## ----------- ## - -_ACEOF - - -# Keep a trace of the command line. -# Strip out --no-create and --no-recursion so they do not pile up. -# Strip out --silent because we don't want to record it for future runs. -# Also quote any args containing shell meta-characters. -# Make two passes to allow for proper duplicate-argument suppression. -ac_configure_args= -ac_configure_args0= -ac_configure_args1= -ac_must_keep_next=false -for ac_pass in 1 2 -do - for ac_arg - do - case $ac_arg in - -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;; - -q | -quiet | --quiet | --quie | --qui | --qu | --q \ - | -silent | --silent | --silen | --sile | --sil) - continue ;; - *\'*) - ac_arg=`$as_echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; - esac - case $ac_pass in - 1) as_fn_append ac_configure_args0 " '$ac_arg'" ;; - 2) - as_fn_append ac_configure_args1 " '$ac_arg'" - if test $ac_must_keep_next = true; then - ac_must_keep_next=false # Got value, back to normal. - else - case $ac_arg in - *=* | --config-cache | -C | -disable-* | --disable-* \ - | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \ - | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \ - | -with-* | --with-* | -without-* | --without-* | --x) - case "$ac_configure_args0 " in - "$ac_configure_args1"*" '$ac_arg' "* ) continue ;; - esac - ;; - -* ) ac_must_keep_next=true ;; - esac - fi - as_fn_append ac_configure_args " '$ac_arg'" - ;; - esac - done -done -{ ac_configure_args0=; unset ac_configure_args0;} -{ ac_configure_args1=; unset ac_configure_args1;} - -# When interrupted or exit'd, cleanup temporary files, and complete -# config.log. We remove comments because anyway the quotes in there -# would cause problems or look ugly. -# WARNING: Use '\'' to represent an apostrophe within the trap. -# WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug. -trap 'exit_status=$? - # Save into config.log some information that might help in debugging. - { - echo - - $as_echo "## ---------------- ## -## Cache variables. ## -## ---------------- ##" - echo - # The following way of writing the cache mishandles newlines in values, -( - for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do - eval ac_val=\$$ac_var - case $ac_val in #( - *${as_nl}*) - case $ac_var in #( - *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 -$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; - esac - case $ac_var in #( - _ | IFS | as_nl) ;; #( - BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( - *) { eval $ac_var=; unset $ac_var;} ;; - esac ;; - esac - done - (set) 2>&1 | - case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #( - *${as_nl}ac_space=\ *) - sed -n \ - "s/'\''/'\''\\\\'\'''\''/g; - s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p" - ;; #( - *) - sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" - ;; - esac | - sort -) - echo - - $as_echo "## ----------------- ## -## Output variables. ## -## ----------------- ##" - echo - for ac_var in $ac_subst_vars - do - eval ac_val=\$$ac_var - case $ac_val in - *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; - esac - $as_echo "$ac_var='\''$ac_val'\''" - done | sort - echo - - if test -n "$ac_subst_files"; then - $as_echo "## ------------------- ## -## File substitutions. ## -## ------------------- ##" - echo - for ac_var in $ac_subst_files - do - eval ac_val=\$$ac_var - case $ac_val in - *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; - esac - $as_echo "$ac_var='\''$ac_val'\''" - done | sort - echo - fi - - if test -s confdefs.h; then - $as_echo "## ----------- ## -## confdefs.h. ## -## ----------- ##" - echo - cat confdefs.h - echo - fi - test "$ac_signal" != 0 && - $as_echo "$as_me: caught signal $ac_signal" - $as_echo "$as_me: exit $exit_status" - } >&5 - rm -f core *.core core.conftest.* && - rm -f -r conftest* confdefs* conf$$* $ac_clean_files && - exit $exit_status -' 0 -for ac_signal in 1 2 13 15; do - trap 'ac_signal='$ac_signal'; as_fn_exit 1' $ac_signal -done -ac_signal=0 - -# confdefs.h avoids OS command line length limits that DEFS can exceed. -rm -f -r conftest* confdefs.h - -$as_echo "/* confdefs.h */" > confdefs.h - -# Predefined preprocessor variables. - -cat >>confdefs.h <<_ACEOF -#define PACKAGE_NAME "$PACKAGE_NAME" -_ACEOF - -cat >>confdefs.h <<_ACEOF -#define PACKAGE_TARNAME "$PACKAGE_TARNAME" -_ACEOF - -cat >>confdefs.h <<_ACEOF -#define PACKAGE_VERSION "$PACKAGE_VERSION" -_ACEOF - -cat >>confdefs.h <<_ACEOF -#define PACKAGE_STRING "$PACKAGE_STRING" -_ACEOF - -cat >>confdefs.h <<_ACEOF -#define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT" -_ACEOF - -cat >>confdefs.h <<_ACEOF -#define PACKAGE_URL "$PACKAGE_URL" -_ACEOF - - -# Let the site file select an alternate cache file if it wants to. -# Prefer an explicitly selected file to automatically selected ones. -ac_site_file1=NONE -ac_site_file2=NONE -if test -n "$CONFIG_SITE"; then - # We do not want a PATH search for config.site. - case $CONFIG_SITE in #(( - -*) ac_site_file1=./$CONFIG_SITE;; - */*) ac_site_file1=$CONFIG_SITE;; - *) ac_site_file1=./$CONFIG_SITE;; - esac -elif test "x$prefix" != xNONE; then - ac_site_file1=$prefix/share/config.site - ac_site_file2=$prefix/etc/config.site -else - ac_site_file1=$ac_default_prefix/share/config.site - ac_site_file2=$ac_default_prefix/etc/config.site -fi -for ac_site_file in "$ac_site_file1" "$ac_site_file2" -do - test "x$ac_site_file" = xNONE && continue - if test /dev/null != "$ac_site_file" && test -r "$ac_site_file"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5 -$as_echo "$as_me: loading site script $ac_site_file" >&6;} - sed 's/^/| /' "$ac_site_file" >&5 - . "$ac_site_file" \ - || { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error $? "failed to load site script $ac_site_file -See \`config.log' for more details" "$LINENO" 5; } - fi -done - -if test -r "$cache_file"; then - # Some versions of bash will fail to source /dev/null (special files - # actually), so we avoid doing that. DJGPP emulates it as a regular file. - if test /dev/null != "$cache_file" && test -f "$cache_file"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5 -$as_echo "$as_me: loading cache $cache_file" >&6;} - case $cache_file in - [\\/]* | ?:[\\/]* ) . "$cache_file";; - *) . "./$cache_file";; - esac - fi -else - { $as_echo "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5 -$as_echo "$as_me: creating cache $cache_file" >&6;} - >$cache_file -fi - -# Check that the precious variables saved in the cache have kept the same -# value. -ac_cache_corrupted=false -for ac_var in $ac_precious_vars; do - eval ac_old_set=\$ac_cv_env_${ac_var}_set - eval ac_new_set=\$ac_env_${ac_var}_set - eval ac_old_val=\$ac_cv_env_${ac_var}_value - eval ac_new_val=\$ac_env_${ac_var}_value - case $ac_old_set,$ac_new_set in - set,) - { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 -$as_echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} - ac_cache_corrupted=: ;; - ,set) - { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5 -$as_echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} - ac_cache_corrupted=: ;; - ,);; - *) - if test "x$ac_old_val" != "x$ac_new_val"; then - # differences in whitespace do not lead to failure. - ac_old_val_w=`echo x $ac_old_val` - ac_new_val_w=`echo x $ac_new_val` - if test "$ac_old_val_w" != "$ac_new_val_w"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5 -$as_echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} - ac_cache_corrupted=: - else - { $as_echo "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5 -$as_echo "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;} - eval $ac_var=\$ac_old_val - fi - { $as_echo "$as_me:${as_lineno-$LINENO}: former value: \`$ac_old_val'" >&5 -$as_echo "$as_me: former value: \`$ac_old_val'" >&2;} - { $as_echo "$as_me:${as_lineno-$LINENO}: current value: \`$ac_new_val'" >&5 -$as_echo "$as_me: current value: \`$ac_new_val'" >&2;} - fi;; - esac - # Pass precious variables to config.status. - if test "$ac_new_set" = set; then - case $ac_new_val in - *\'*) ac_arg=$ac_var=`$as_echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; - *) ac_arg=$ac_var=$ac_new_val ;; - esac - case " $ac_configure_args " in - *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy. - *) as_fn_append ac_configure_args " '$ac_arg'" ;; - esac - fi -done -if $ac_cache_corrupted; then - { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} - { $as_echo "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5 -$as_echo "$as_me: error: changes in the environment can compromise the build" >&2;} - as_fn_error $? "run \`make distclean' and/or \`rm $cache_file' and start over" "$LINENO" 5 -fi -## -------------------- ## -## Main body of script. ## -## -------------------- ## - -ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu - - - -ac_config_headers="$ac_config_headers src/rigraph/config.h" - - -: ${R_HOME=`R RHOME`} -if test -z "${R_HOME}"; then - echo "could not determine R_HOME" - exit 1 -fi -CC=`"${R_HOME}/bin/R" CMD config CC` -CXX=`"${R_HOME}/bin/R" CMD config CXX11` -if test -z "$CXX"; then - as_fn_error $? "No C++11 compiler is available" "$LINENO" 5 -fi -CXX11STD=`"${R_HOME}/bin/R" CMD config CXX11STD` -CXX="${CXX} ${CXX11STD}" -FC=`"${R_HOME}/bin/R" CMD config FC` -CFLAGS=`"${R_HOME}/bin/R" CMD config CFLAGS` -CXXFLAGS=`"${R_HOME}/bin/R" CMD config CXX11FLAGS` -CPPFLAGS=`"${R_HOME}/bin/R" CMD config CPPFLAGS` -FCFLAGS=`"${R_HOME}/bin/R" CMD config FCFLAGS` -FLIBS=`"${R_HOME}/bin/R" CMD config FLIBS` -LDFLAGS=`"${R_HOME}/bin/R" CMD config LDFLAGS` - -ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu - -ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu -if test -n "$ac_tool_prefix"; then - # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. -set dummy ${ac_tool_prefix}gcc; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_CC+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$CC"; then - ac_cv_prog_CC="$CC" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_CC="${ac_tool_prefix}gcc" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -CC=$ac_cv_prog_CC -if test -n "$CC"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 -$as_echo "$CC" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - -fi -if test -z "$ac_cv_prog_CC"; then - ac_ct_CC=$CC - # Extract the first word of "gcc", so it can be a program name with args. -set dummy gcc; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_ac_ct_CC+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$ac_ct_CC"; then - ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_ac_ct_CC="gcc" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -ac_ct_CC=$ac_cv_prog_ac_ct_CC -if test -n "$ac_ct_CC"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 -$as_echo "$ac_ct_CC" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - if test "x$ac_ct_CC" = x; then - CC="" - else - case $cross_compiling:$ac_tool_warned in -yes:) -{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 -$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} -ac_tool_warned=yes ;; -esac - CC=$ac_ct_CC - fi -else - CC="$ac_cv_prog_CC" -fi - -if test -z "$CC"; then - if test -n "$ac_tool_prefix"; then - # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. -set dummy ${ac_tool_prefix}cc; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_CC+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$CC"; then - ac_cv_prog_CC="$CC" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_CC="${ac_tool_prefix}cc" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -CC=$ac_cv_prog_CC -if test -n "$CC"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 -$as_echo "$CC" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - - fi -fi -if test -z "$CC"; then - # Extract the first word of "cc", so it can be a program name with args. -set dummy cc; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_CC+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$CC"; then - ac_cv_prog_CC="$CC" # Let the user override the test. -else - ac_prog_rejected=no -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then - ac_prog_rejected=yes - continue - fi - ac_cv_prog_CC="cc" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -if test $ac_prog_rejected = yes; then - # We found a bogon in the path, so make sure we never use it. - set dummy $ac_cv_prog_CC - shift - if test $# != 0; then - # We chose a different compiler from the bogus one. - # However, it has the same basename, so the bogon will be chosen - # first if we set CC to just the basename; use the full file name. - shift - ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" - fi -fi -fi -fi -CC=$ac_cv_prog_CC -if test -n "$CC"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 -$as_echo "$CC" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - -fi -if test -z "$CC"; then - if test -n "$ac_tool_prefix"; then - for ac_prog in cl.exe - do - # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. -set dummy $ac_tool_prefix$ac_prog; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_CC+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$CC"; then - ac_cv_prog_CC="$CC" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_CC="$ac_tool_prefix$ac_prog" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -CC=$ac_cv_prog_CC -if test -n "$CC"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 -$as_echo "$CC" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - - test -n "$CC" && break - done -fi -if test -z "$CC"; then - ac_ct_CC=$CC - for ac_prog in cl.exe -do - # Extract the first word of "$ac_prog", so it can be a program name with args. -set dummy $ac_prog; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_ac_ct_CC+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$ac_ct_CC"; then - ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_ac_ct_CC="$ac_prog" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -ac_ct_CC=$ac_cv_prog_ac_ct_CC -if test -n "$ac_ct_CC"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 -$as_echo "$ac_ct_CC" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - - test -n "$ac_ct_CC" && break -done - - if test "x$ac_ct_CC" = x; then - CC="" - else - case $cross_compiling:$ac_tool_warned in -yes:) -{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 -$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} -ac_tool_warned=yes ;; -esac - CC=$ac_ct_CC - fi -fi - -fi - - -test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error $? "no acceptable C compiler found in \$PATH -See \`config.log' for more details" "$LINENO" 5; } - -# Provide some information about the compiler. -$as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5 -set X $ac_compile -ac_compiler=$2 -for ac_option in --version -v -V -qversion; do - { { ac_try="$ac_compiler $ac_option >&5" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 - (eval "$ac_compiler $ac_option >&5") 2>conftest.err - ac_status=$? - if test -s conftest.err; then - sed '10a\ -... rest of stderr output deleted ... - 10q' conftest.err >conftest.er1 - cat conftest.er1 >&5 - fi - rm -f conftest.er1 conftest.err - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; } -done - -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF -ac_clean_files_save=$ac_clean_files -ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out" -# Try to create an executable without -o first, disregard a.out. -# It will help us diagnose broken compilers, and finding out an intuition -# of exeext. -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler works" >&5 -$as_echo_n "checking whether the C compiler works... " >&6; } -ac_link_default=`$as_echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'` - -# The possible output files: -ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*" - -ac_rmfiles= -for ac_file in $ac_files -do - case $ac_file in - *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; - * ) ac_rmfiles="$ac_rmfiles $ac_file";; - esac -done -rm -f $ac_rmfiles - -if { { ac_try="$ac_link_default" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 - (eval "$ac_link_default") 2>&5 - ac_status=$? - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; }; then : - # Autoconf-2.13 could set the ac_cv_exeext variable to `no'. -# So ignore a value of `no', otherwise this would lead to `EXEEXT = no' -# in a Makefile. We should not override ac_cv_exeext if it was cached, -# so that the user can short-circuit this test for compilers unknown to -# Autoconf. -for ac_file in $ac_files '' -do - test -f "$ac_file" || continue - case $ac_file in - *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) - ;; - [ab].out ) - # We found the default executable, but exeext='' is most - # certainly right. - break;; - *.* ) - if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no; - then :; else - ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` - fi - # We set ac_cv_exeext here because the later test for it is not - # safe: cross compilers may not add the suffix if given an `-o' - # argument, so we may need to know it at that point already. - # Even if this section looks crufty: it has the advantage of - # actually working. - break;; - * ) - break;; - esac -done -test "$ac_cv_exeext" = no && ac_cv_exeext= - -else - ac_file='' -fi -if test -z "$ac_file"; then : - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -$as_echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error 77 "C compiler cannot create executables -See \`config.log' for more details" "$LINENO" 5; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 -$as_echo "yes" >&6; } -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5 -$as_echo_n "checking for C compiler default output file name... " >&6; } -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5 -$as_echo "$ac_file" >&6; } -ac_exeext=$ac_cv_exeext - -rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out -ac_clean_files=$ac_clean_files_save -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5 -$as_echo_n "checking for suffix of executables... " >&6; } -if { { ac_try="$ac_link" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 - (eval "$ac_link") 2>&5 - ac_status=$? - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; }; then : - # If both `conftest.exe' and `conftest' are `present' (well, observable) -# catch `conftest.exe'. For instance with Cygwin, `ls conftest' will -# work properly (i.e., refer to `conftest.exe'), while it won't with -# `rm'. -for ac_file in conftest.exe conftest conftest.*; do - test -f "$ac_file" || continue - case $ac_file in - *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; - *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` - break;; - * ) break;; - esac -done -else - { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error $? "cannot compute suffix of executables: cannot compile and link -See \`config.log' for more details" "$LINENO" 5; } -fi -rm -f conftest conftest$ac_cv_exeext -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5 -$as_echo "$ac_cv_exeext" >&6; } - -rm -f conftest.$ac_ext -EXEEXT=$ac_cv_exeext -ac_exeext=$EXEEXT -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include -int -main () -{ -FILE *f = fopen ("conftest.out", "w"); - return ferror (f) || fclose (f) != 0; - - ; - return 0; -} -_ACEOF -ac_clean_files="$ac_clean_files conftest.out" -# Check that the compiler produces executables we can run. If not, either -# the compiler is broken, or we cross compile. -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5 -$as_echo_n "checking whether we are cross compiling... " >&6; } -if test "$cross_compiling" != yes; then - { { ac_try="$ac_link" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 - (eval "$ac_link") 2>&5 - ac_status=$? - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; } - if { ac_try='./conftest$ac_cv_exeext' - { { case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 - (eval "$ac_try") 2>&5 - ac_status=$? - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; }; }; then - cross_compiling=no - else - if test "$cross_compiling" = maybe; then - cross_compiling=yes - else - { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error $? "cannot run C compiled programs. -If you meant to cross compile, use \`--host'. -See \`config.log' for more details" "$LINENO" 5; } - fi - fi -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5 -$as_echo "$cross_compiling" >&6; } - -rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out -ac_clean_files=$ac_clean_files_save -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5 -$as_echo_n "checking for suffix of object files... " >&6; } -if ${ac_cv_objext+:} false; then : - $as_echo_n "(cached) " >&6 -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF -rm -f conftest.o conftest.obj -if { { ac_try="$ac_compile" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 - (eval "$ac_compile") 2>&5 - ac_status=$? - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; }; then : - for ac_file in conftest.o conftest.obj conftest.*; do - test -f "$ac_file" || continue; - case $ac_file in - *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;; - *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` - break;; - esac -done -else - $as_echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error $? "cannot compute suffix of object files: cannot compile -See \`config.log' for more details" "$LINENO" 5; } -fi -rm -f conftest.$ac_cv_objext conftest.$ac_ext -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5 -$as_echo "$ac_cv_objext" >&6; } -OBJEXT=$ac_cv_objext -ac_objext=$OBJEXT -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5 -$as_echo_n "checking whether we are using the GNU C compiler... " >&6; } -if ${ac_cv_c_compiler_gnu+:} false; then : - $as_echo_n "(cached) " >&6 -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ -#ifndef __GNUC__ - choke me -#endif - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ac_compiler_gnu=yes -else - ac_compiler_gnu=no -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -ac_cv_c_compiler_gnu=$ac_compiler_gnu - -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5 -$as_echo "$ac_cv_c_compiler_gnu" >&6; } -if test $ac_compiler_gnu = yes; then - GCC=yes -else - GCC= -fi -ac_test_CFLAGS=${CFLAGS+set} -ac_save_CFLAGS=$CFLAGS -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5 -$as_echo_n "checking whether $CC accepts -g... " >&6; } -if ${ac_cv_prog_cc_g+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_save_c_werror_flag=$ac_c_werror_flag - ac_c_werror_flag=yes - ac_cv_prog_cc_g=no - CFLAGS="-g" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ac_cv_prog_cc_g=yes -else - CFLAGS="" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - -else - ac_c_werror_flag=$ac_save_c_werror_flag - CFLAGS="-g" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ac_cv_prog_cc_g=yes -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - ac_c_werror_flag=$ac_save_c_werror_flag -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5 -$as_echo "$ac_cv_prog_cc_g" >&6; } -if test "$ac_test_CFLAGS" = set; then - CFLAGS=$ac_save_CFLAGS -elif test $ac_cv_prog_cc_g = yes; then - if test "$GCC" = yes; then - CFLAGS="-g -O2" - else - CFLAGS="-g" - fi -else - if test "$GCC" = yes; then - CFLAGS="-O2" - else - CFLAGS= - fi -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5 -$as_echo_n "checking for $CC option to accept ISO C89... " >&6; } -if ${ac_cv_prog_cc_c89+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_cv_prog_cc_c89=no -ac_save_CC=$CC -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include -#include -struct stat; -/* Most of the following tests are stolen from RCS 5.7's src/rigraph/conf.sh. */ -struct buf { int x; }; -FILE * (*rcsopen) (struct buf *, struct stat *, int); -static char *e (p, i) - char **p; - int i; -{ - return p[i]; -} -static char *f (char * (*g) (char **, int), char **p, ...) -{ - char *s; - va_list v; - va_start (v,p); - s = g (p, va_arg (v,int)); - va_end (v); - return s; -} - -/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has - function prototypes and stuff, but not '\xHH' hex character constants. - These don't provoke an error unfortunately, instead are silently treated - as 'x'. The following induces an error, until -std is added to get - proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an - array size at least. It's necessary to write '\x00'==0 to get something - that's true only with -std. */ -int osf4_cc_array ['\x00' == 0 ? 1 : -1]; - -/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters - inside strings and character constants. */ -#define FOO(x) 'x' -int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1]; - -int test (int i, double x); -struct s1 {int (*f) (int a);}; -struct s2 {int (*f) (double a);}; -int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); -int argc; -char **argv; -int -main () -{ -return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; - ; - return 0; -} -_ACEOF -for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \ - -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" -do - CC="$ac_save_CC $ac_arg" - if ac_fn_c_try_compile "$LINENO"; then : - ac_cv_prog_cc_c89=$ac_arg -fi -rm -f core conftest.err conftest.$ac_objext - test "x$ac_cv_prog_cc_c89" != "xno" && break -done -rm -f conftest.$ac_ext -CC=$ac_save_CC - -fi -# AC_CACHE_VAL -case "x$ac_cv_prog_cc_c89" in - x) - { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 -$as_echo "none needed" >&6; } ;; - xno) - { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 -$as_echo "unsupported" >&6; } ;; - *) - CC="$CC $ac_cv_prog_cc_c89" - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5 -$as_echo "$ac_cv_prog_cc_c89" >&6; } ;; -esac -if test "x$ac_cv_prog_cc_c89" != xno; then : - -fi - -ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu - - -ac_ext=cpp -ac_cpp='$CXXCPP $CPPFLAGS' -ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_cxx_compiler_gnu - -ac_ext=cpp -ac_cpp='$CXXCPP $CPPFLAGS' -ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_cxx_compiler_gnu -if test -z "$CXX"; then - if test -n "$CCC"; then - CXX=$CCC - else - if test -n "$ac_tool_prefix"; then - for ac_prog in g++ c++ gpp aCC CC cxx cc++ cl.exe FCC KCC RCC xlC_r xlC - do - # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. -set dummy $ac_tool_prefix$ac_prog; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_CXX+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$CXX"; then - ac_cv_prog_CXX="$CXX" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_CXX="$ac_tool_prefix$ac_prog" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -CXX=$ac_cv_prog_CXX -if test -n "$CXX"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CXX" >&5 -$as_echo "$CXX" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - - test -n "$CXX" && break - done -fi -if test -z "$CXX"; then - ac_ct_CXX=$CXX - for ac_prog in g++ c++ gpp aCC CC cxx cc++ cl.exe FCC KCC RCC xlC_r xlC -do - # Extract the first word of "$ac_prog", so it can be a program name with args. -set dummy $ac_prog; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_ac_ct_CXX+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$ac_ct_CXX"; then - ac_cv_prog_ac_ct_CXX="$ac_ct_CXX" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_ac_ct_CXX="$ac_prog" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -ac_ct_CXX=$ac_cv_prog_ac_ct_CXX -if test -n "$ac_ct_CXX"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CXX" >&5 -$as_echo "$ac_ct_CXX" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - - test -n "$ac_ct_CXX" && break -done - - if test "x$ac_ct_CXX" = x; then - CXX="g++" - else - case $cross_compiling:$ac_tool_warned in -yes:) -{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 -$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} -ac_tool_warned=yes ;; -esac - CXX=$ac_ct_CXX - fi -fi - - fi -fi -# Provide some information about the compiler. -$as_echo "$as_me:${as_lineno-$LINENO}: checking for C++ compiler version" >&5 -set X $ac_compile -ac_compiler=$2 -for ac_option in --version -v -V -qversion; do - { { ac_try="$ac_compiler $ac_option >&5" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 - (eval "$ac_compiler $ac_option >&5") 2>conftest.err - ac_status=$? - if test -s conftest.err; then - sed '10a\ -... rest of stderr output deleted ... - 10q' conftest.err >conftest.er1 - cat conftest.er1 >&5 - fi - rm -f conftest.er1 conftest.err - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; } -done - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C++ compiler" >&5 -$as_echo_n "checking whether we are using the GNU C++ compiler... " >&6; } -if ${ac_cv_cxx_compiler_gnu+:} false; then : - $as_echo_n "(cached) " >&6 -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ -#ifndef __GNUC__ - choke me -#endif - - ; - return 0; -} -_ACEOF -if ac_fn_cxx_try_compile "$LINENO"; then : - ac_compiler_gnu=yes -else - ac_compiler_gnu=no -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -ac_cv_cxx_compiler_gnu=$ac_compiler_gnu - -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cxx_compiler_gnu" >&5 -$as_echo "$ac_cv_cxx_compiler_gnu" >&6; } -if test $ac_compiler_gnu = yes; then - GXX=yes -else - GXX= -fi -ac_test_CXXFLAGS=${CXXFLAGS+set} -ac_save_CXXFLAGS=$CXXFLAGS -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CXX accepts -g" >&5 -$as_echo_n "checking whether $CXX accepts -g... " >&6; } -if ${ac_cv_prog_cxx_g+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_save_cxx_werror_flag=$ac_cxx_werror_flag - ac_cxx_werror_flag=yes - ac_cv_prog_cxx_g=no - CXXFLAGS="-g" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF -if ac_fn_cxx_try_compile "$LINENO"; then : - ac_cv_prog_cxx_g=yes -else - CXXFLAGS="" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF -if ac_fn_cxx_try_compile "$LINENO"; then : - -else - ac_cxx_werror_flag=$ac_save_cxx_werror_flag - CXXFLAGS="-g" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF -if ac_fn_cxx_try_compile "$LINENO"; then : - ac_cv_prog_cxx_g=yes -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - ac_cxx_werror_flag=$ac_save_cxx_werror_flag -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cxx_g" >&5 -$as_echo "$ac_cv_prog_cxx_g" >&6; } -if test "$ac_test_CXXFLAGS" = set; then - CXXFLAGS=$ac_save_CXXFLAGS -elif test $ac_cv_prog_cxx_g = yes; then - if test "$GXX" = yes; then - CXXFLAGS="-g -O2" - else - CXXFLAGS="-g" - fi -else - if test "$GXX" = yes; then - CXXFLAGS="-O2" - else - CXXFLAGS= - fi -fi -ac_ext=cpp -ac_cpp='$CXXCPP $CPPFLAGS' -ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_cxx_compiler_gnu - - -# Fortran compiler, we need to check if it is the GNU compiler -ac_ext=${ac_fc_srcext-f} -ac_compile='$FC -c $FCFLAGS $ac_fcflags_srcext conftest.$ac_ext >&5' -ac_link='$FC -o conftest$ac_exeext $FCFLAGS $LDFLAGS $ac_fcflags_srcext conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_fc_compiler_gnu -if test -n "$ac_tool_prefix"; then - for ac_prog in gfortran g95 xlf95 f95 fort ifort ifc efc pgfortran pgf95 lf95 ftn nagfor xlf90 f90 pgf90 pghpf epcf90 g77 xlf f77 frt pgf77 cf77 fort77 fl32 af77 - do - # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. -set dummy $ac_tool_prefix$ac_prog; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_FC+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$FC"; then - ac_cv_prog_FC="$FC" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_FC="$ac_tool_prefix$ac_prog" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -FC=$ac_cv_prog_FC -if test -n "$FC"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $FC" >&5 -$as_echo "$FC" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - - test -n "$FC" && break - done -fi -if test -z "$FC"; then - ac_ct_FC=$FC - for ac_prog in gfortran g95 xlf95 f95 fort ifort ifc efc pgfortran pgf95 lf95 ftn nagfor xlf90 f90 pgf90 pghpf epcf90 g77 xlf f77 frt pgf77 cf77 fort77 fl32 af77 -do - # Extract the first word of "$ac_prog", so it can be a program name with args. -set dummy $ac_prog; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_ac_ct_FC+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$ac_ct_FC"; then - ac_cv_prog_ac_ct_FC="$ac_ct_FC" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_ac_ct_FC="$ac_prog" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -ac_ct_FC=$ac_cv_prog_ac_ct_FC -if test -n "$ac_ct_FC"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_FC" >&5 -$as_echo "$ac_ct_FC" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - - test -n "$ac_ct_FC" && break -done - - if test "x$ac_ct_FC" = x; then - FC="" - else - case $cross_compiling:$ac_tool_warned in -yes:) -{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 -$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} -ac_tool_warned=yes ;; -esac - FC=$ac_ct_FC - fi -fi - - -# Provide some information about the compiler. -$as_echo "$as_me:${as_lineno-$LINENO}: checking for Fortran compiler version" >&5 -set X $ac_compile -ac_compiler=$2 -for ac_option in --version -v -V -qversion; do - { { ac_try="$ac_compiler $ac_option >&5" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 - (eval "$ac_compiler $ac_option >&5") 2>conftest.err - ac_status=$? - if test -s conftest.err; then - sed '10a\ -... rest of stderr output deleted ... - 10q' conftest.err >conftest.er1 - cat conftest.er1 >&5 - fi - rm -f conftest.er1 conftest.err - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; } -done -rm -f a.out - -# If we don't use `.F' as extension, the preprocessor is not run on the -# input file. (Note that this only needs to work for GNU compilers.) -ac_save_ext=$ac_ext -ac_ext=F -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU Fortran compiler" >&5 -$as_echo_n "checking whether we are using the GNU Fortran compiler... " >&6; } -if ${ac_cv_fc_compiler_gnu+:} false; then : - $as_echo_n "(cached) " >&6 -else - cat > conftest.$ac_ext <<_ACEOF - program main -#ifndef __GNUC__ - choke me -#endif - - end -_ACEOF -if ac_fn_fc_try_compile "$LINENO"; then : - ac_compiler_gnu=yes -else - ac_compiler_gnu=no -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -ac_cv_fc_compiler_gnu=$ac_compiler_gnu - -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_fc_compiler_gnu" >&5 -$as_echo "$ac_cv_fc_compiler_gnu" >&6; } -ac_ext=$ac_save_ext -ac_test_FCFLAGS=${FCFLAGS+set} -ac_save_FCFLAGS=$FCFLAGS -FCFLAGS= -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $FC accepts -g" >&5 -$as_echo_n "checking whether $FC accepts -g... " >&6; } -if ${ac_cv_prog_fc_g+:} false; then : - $as_echo_n "(cached) " >&6 -else - FCFLAGS=-g -cat > conftest.$ac_ext <<_ACEOF - program main - - end -_ACEOF -if ac_fn_fc_try_compile "$LINENO"; then : - ac_cv_prog_fc_g=yes -else - ac_cv_prog_fc_g=no -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_fc_g" >&5 -$as_echo "$ac_cv_prog_fc_g" >&6; } -if test "$ac_test_FCFLAGS" = set; then - FCFLAGS=$ac_save_FCFLAGS -elif test $ac_cv_prog_fc_g = yes; then - if test "x$ac_cv_fc_compiler_gnu" = xyes; then - FCFLAGS="-g -O2" - else - FCFLAGS="-g" - fi -else - if test "x$ac_cv_fc_compiler_gnu" = xyes; then - FCFLAGS="-O2" - else - FCFLAGS= - fi -fi - -if test $ac_compiler_gnu = yes; then - GFC=yes -else - GFC= -fi -ac_ext=cpp -ac_cpp='$CXXCPP $CPPFLAGS' -ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_cxx_compiler_gnu - -if test "x$ac_cv_fc_compiler_gnu" = xyes; then - -$as_echo "#define HAVE_GFORTRAN 1" >>confdefs.h - -fi - -LIBS_SAVE=$LIBS -LIBS="$LIBS -lm" - -for ac_func in expm1 fmin finite log2 log1p rint rintf round stpcpy strcasecmp _stricmp strdup -do : - as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` -ac_fn_cxx_check_func "$LINENO" "$ac_func" "$as_ac_var" -if eval test \"x\$"$as_ac_var"\" = x"yes"; then : - cat >>confdefs.h <<_ACEOF -#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 -_ACEOF - -fi -done - -for ac_func in isfinite -do : - ac_fn_cxx_check_func "$LINENO" "isfinite" "ac_cv_func_isfinite" -if test "x$ac_cv_func_isfinite" = xyes; then : - cat >>confdefs.h <<_ACEOF -#define HAVE_ISFINITE 1 -_ACEOF - $as_echo "#define HAVE_ISFINITE 1" >>confdefs.h - -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include -int -main () -{ -double f = 0.0; isfinite(f) - ; - return 0; -} -_ACEOF -if ac_fn_cxx_try_link "$LINENO"; then : - $as_echo "#define HAVE_ISFINITE 1" >>confdefs.h - -else - { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error $? "isfinite() not available -See \`config.log' for more details" "$LINENO" 5; } - -fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext - -fi -done - -LIBS=$LIBS_SAVE - -ac_ext=cpp -ac_cpp='$CXXCPP $CPPFLAGS' -ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_cxx_compiler_gnu -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to run the C++ preprocessor" >&5 -$as_echo_n "checking how to run the C++ preprocessor... " >&6; } -if test -z "$CXXCPP"; then - if ${ac_cv_prog_CXXCPP+:} false; then : - $as_echo_n "(cached) " >&6 -else - # Double quotes because CXXCPP needs to be expanded - for CXXCPP in "$CXX -E" "/lib/cpp" - do - ac_preproc_ok=false -for ac_cxx_preproc_warn_flag in '' yes -do - # Use a header file that comes with gcc, so configuring glibc - # with a fresh cross-compiler works. - # Prefer to if __STDC__ is defined, since - # exists even on freestanding compilers. - # On the NeXT, cc -E runs the code through the compiler's parser, - # not just through cpp. "Syntax error" is here to catch this case. - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#ifdef __STDC__ -# include -#else -# include -#endif - Syntax error -_ACEOF -if ac_fn_cxx_try_cpp "$LINENO"; then : - -else - # Broken: fails on valid input. -continue -fi -rm -f conftest.err conftest.i conftest.$ac_ext - - # OK, works on sane cases. Now check whether nonexistent headers - # can be detected and how. - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include -_ACEOF -if ac_fn_cxx_try_cpp "$LINENO"; then : - # Broken: success on invalid input. -continue -else - # Passes both tests. -ac_preproc_ok=: -break -fi -rm -f conftest.err conftest.i conftest.$ac_ext - -done -# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. -rm -f conftest.i conftest.err conftest.$ac_ext -if $ac_preproc_ok; then : - break -fi - - done - ac_cv_prog_CXXCPP=$CXXCPP - -fi - CXXCPP=$ac_cv_prog_CXXCPP -else - ac_cv_prog_CXXCPP=$CXXCPP -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $CXXCPP" >&5 -$as_echo "$CXXCPP" >&6; } -ac_preproc_ok=false -for ac_cxx_preproc_warn_flag in '' yes -do - # Use a header file that comes with gcc, so configuring glibc - # with a fresh cross-compiler works. - # Prefer to if __STDC__ is defined, since - # exists even on freestanding compilers. - # On the NeXT, cc -E runs the code through the compiler's parser, - # not just through cpp. "Syntax error" is here to catch this case. - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#ifdef __STDC__ -# include -#else -# include -#endif - Syntax error -_ACEOF -if ac_fn_cxx_try_cpp "$LINENO"; then : - -else - # Broken: fails on valid input. -continue -fi -rm -f conftest.err conftest.i conftest.$ac_ext - - # OK, works on sane cases. Now check whether nonexistent headers - # can be detected and how. - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include -_ACEOF -if ac_fn_cxx_try_cpp "$LINENO"; then : - # Broken: success on invalid input. -continue -else - # Passes both tests. -ac_preproc_ok=: -break -fi -rm -f conftest.err conftest.i conftest.$ac_ext - -done -# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. -rm -f conftest.i conftest.err conftest.$ac_ext -if $ac_preproc_ok; then : - -else - { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error $? "C++ preprocessor \"$CXXCPP\" fails sanity check -See \`config.log' for more details" "$LINENO" 5; } -fi - -ac_ext=cpp -ac_cpp='$CXXCPP $CPPFLAGS' -ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_cxx_compiler_gnu - - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for grep that handles long lines and -e" >&5 -$as_echo_n "checking for grep that handles long lines and -e... " >&6; } -if ${ac_cv_path_GREP+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -z "$GREP"; then - ac_path_GREP_found=false - # Loop through the user's path and test for each of PROGNAME-LIST - as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_prog in grep ggrep; do - for ac_exec_ext in '' $ac_executable_extensions; do - ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext" - as_fn_executable_p "$ac_path_GREP" || continue -# Check for GNU ac_path_GREP and select it if it is found. - # Check for GNU $ac_path_GREP -case `"$ac_path_GREP" --version 2>&1` in -*GNU*) - ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;; -*) - ac_count=0 - $as_echo_n 0123456789 >"conftest.in" - while : - do - cat "conftest.in" "conftest.in" >"conftest.tmp" - mv "conftest.tmp" "conftest.in" - cp "conftest.in" "conftest.nl" - $as_echo 'GREP' >> "conftest.nl" - "$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break - diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break - as_fn_arith $ac_count + 1 && ac_count=$as_val - if test $ac_count -gt ${ac_path_GREP_max-0}; then - # Best one so far, save it but keep looking for a better one - ac_cv_path_GREP="$ac_path_GREP" - ac_path_GREP_max=$ac_count - fi - # 10*(2^10) chars as input seems more than enough - test $ac_count -gt 10 && break - done - rm -f conftest.in conftest.tmp conftest.nl conftest.out;; -esac - - $ac_path_GREP_found && break 3 - done - done - done -IFS=$as_save_IFS - if test -z "$ac_cv_path_GREP"; then - as_fn_error $? "no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 - fi -else - ac_cv_path_GREP=$GREP -fi - -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_GREP" >&5 -$as_echo "$ac_cv_path_GREP" >&6; } - GREP="$ac_cv_path_GREP" - - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for egrep" >&5 -$as_echo_n "checking for egrep... " >&6; } -if ${ac_cv_path_EGREP+:} false; then : - $as_echo_n "(cached) " >&6 -else - if echo a | $GREP -E '(a|b)' >/dev/null 2>&1 - then ac_cv_path_EGREP="$GREP -E" - else - if test -z "$EGREP"; then - ac_path_EGREP_found=false - # Loop through the user's path and test for each of PROGNAME-LIST - as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_prog in egrep; do - for ac_exec_ext in '' $ac_executable_extensions; do - ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext" - as_fn_executable_p "$ac_path_EGREP" || continue -# Check for GNU ac_path_EGREP and select it if it is found. - # Check for GNU $ac_path_EGREP -case `"$ac_path_EGREP" --version 2>&1` in -*GNU*) - ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;; -*) - ac_count=0 - $as_echo_n 0123456789 >"conftest.in" - while : - do - cat "conftest.in" "conftest.in" >"conftest.tmp" - mv "conftest.tmp" "conftest.in" - cp "conftest.in" "conftest.nl" - $as_echo 'EGREP' >> "conftest.nl" - "$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break - diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break - as_fn_arith $ac_count + 1 && ac_count=$as_val - if test $ac_count -gt ${ac_path_EGREP_max-0}; then - # Best one so far, save it but keep looking for a better one - ac_cv_path_EGREP="$ac_path_EGREP" - ac_path_EGREP_max=$ac_count - fi - # 10*(2^10) chars as input seems more than enough - test $ac_count -gt 10 && break - done - rm -f conftest.in conftest.tmp conftest.nl conftest.out;; -esac - - $ac_path_EGREP_found && break 3 - done - done - done -IFS=$as_save_IFS - if test -z "$ac_cv_path_EGREP"; then - as_fn_error $? "no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 - fi -else - ac_cv_path_EGREP=$EGREP -fi - - fi -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_EGREP" >&5 -$as_echo "$ac_cv_path_EGREP" >&6; } - EGREP="$ac_cv_path_EGREP" - - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ANSI C header files" >&5 -$as_echo_n "checking for ANSI C header files... " >&6; } -if ${ac_cv_header_stdc+:} false; then : - $as_echo_n "(cached) " >&6 -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include -#include -#include -#include - -int -main () -{ - - ; - return 0; -} -_ACEOF -if ac_fn_cxx_try_compile "$LINENO"; then : - ac_cv_header_stdc=yes -else - ac_cv_header_stdc=no -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - -if test $ac_cv_header_stdc = yes; then - # SunOS 4.x string.h does not declare mem*, contrary to ANSI. - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include - -_ACEOF -if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | - $EGREP "memchr" >/dev/null 2>&1; then : - -else - ac_cv_header_stdc=no -fi -rm -f conftest* - -fi - -if test $ac_cv_header_stdc = yes; then - # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include - -_ACEOF -if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | - $EGREP "free" >/dev/null 2>&1; then : - -else - ac_cv_header_stdc=no -fi -rm -f conftest* - -fi - -if test $ac_cv_header_stdc = yes; then - # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. - if test "$cross_compiling" = yes; then : - : -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include -#include -#if ((' ' & 0x0FF) == 0x020) -# define ISLOWER(c) ('a' <= (c) && (c) <= 'z') -# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) -#else -# define ISLOWER(c) \ - (('a' <= (c) && (c) <= 'i') \ - || ('j' <= (c) && (c) <= 'r') \ - || ('s' <= (c) && (c) <= 'z')) -# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c)) -#endif - -#define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) -int -main () -{ - int i; - for (i = 0; i < 256; i++) - if (XOR (islower (i), ISLOWER (i)) - || toupper (i) != TOUPPER (i)) - return 2; - return 0; -} -_ACEOF -if ac_fn_cxx_try_run "$LINENO"; then : - -else - ac_cv_header_stdc=no -fi -rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ - conftest.$ac_objext conftest.beam conftest.$ac_ext -fi - -fi -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdc" >&5 -$as_echo "$ac_cv_header_stdc" >&6; } -if test $ac_cv_header_stdc = yes; then - -$as_echo "#define STDC_HEADERS 1" >>confdefs.h - -fi - -# On IRIX 5.3, sys/types and inttypes.h are conflicting. -for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \ - inttypes.h stdint.h unistd.h -do : - as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` -ac_fn_cxx_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default -" -if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : - cat >>confdefs.h <<_ACEOF -#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 -_ACEOF - -fi - -done - - -ac_fn_cxx_check_header_mongrel "$LINENO" "sys/times.h" "ac_cv_header_sys_times_h" "$ac_includes_default" -if test "x$ac_cv_header_sys_times_h" = xyes; then : - -$as_echo "#define HAVE_TIMES_H 1" >>confdefs.h - -fi - - - -for ac_header in \ - net/if.h \ - netinet/in.h \ - net/if_dl.h \ - sys/sockio.h \ - sys/un.h \ - sys/socket.h \ - sys/ioctl.h \ - sys/time.h \ - sys/file.h \ - -do : - as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` -ac_fn_cxx_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default" -if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : - cat >>confdefs.h <<_ACEOF -#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 -_ACEOF - -fi - -done - - -ac_fn_cxx_check_member "$LINENO" "struct sockaddr" "sa_len" "ac_cv_member_struct_sockaddr_sa_len" "#include - #include -" -if test "x$ac_cv_member_struct_sockaddr_sa_len" = xyes; then : - -cat >>confdefs.h <<_ACEOF -#define HAVE_SA_LEN 1 -_ACEOF - -fi - - -graphml_support=yes -# Check whether --enable-graphml was given. -if test "${enable_graphml+set}" = set; then : - enableval=$enable_graphml; graphml_support=$enableval -else - graphml_support=yes -fi - - -HAVE_LIBXML=0 -if test $graphml_support = yes; then - # Extract the first word of "xml2-config", so it can be a program name with args. -set dummy xml2-config; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_path_XML2CONFIG+:} false; then : - $as_echo_n "(cached) " >&6 -else - case $XML2CONFIG in - [\\/]* | ?:[\\/]*) - ac_cv_path_XML2CONFIG="$XML2CONFIG" # Let the user override the test with a path. - ;; - *) - as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_path_XML2CONFIG="$as_dir/$ac_word$ac_exec_ext" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - - test -z "$ac_cv_path_XML2CONFIG" && ac_cv_path_XML2CONFIG="none" - ;; -esac -fi -XML2CONFIG=$ac_cv_path_XML2CONFIG -if test -n "$XML2CONFIG"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $XML2CONFIG" >&5 -$as_echo "$XML2CONFIG" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - - if test "$XML2CONFIG" = "none"; then - graphml_support=no - else - # xml2-config puts only preprocessor flags in --cflags (i.e. -D and -I) so - # we can put them in XML2_CPPFLAGS. This might not be true for other - # libraries, though. - XML2_CPPFLAGS=`$XML2CONFIG --cflags` - XML2_LIBS=`$XML2CONFIG --libs` - OLDLIBS=${LIBS} - LIBS=${XML2_LIBS} - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for xmlSAXUserParseFile in -lxml2" >&5 -$as_echo_n "checking for xmlSAXUserParseFile in -lxml2... " >&6; } -if ${ac_cv_lib_xml2_xmlSAXUserParseFile+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_check_lib_save_LIBS=$LIBS -LIBS="-lxml2 $LIBS" -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -/* Override any GCC internal prototype to avoid an error. - Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -#ifdef __cplusplus -extern "C" -#endif -char xmlSAXUserParseFile (); -int -main () -{ -return xmlSAXUserParseFile (); - ; - return 0; -} -_ACEOF -if ac_fn_cxx_try_link "$LINENO"; then : - ac_cv_lib_xml2_xmlSAXUserParseFile=yes -else - ac_cv_lib_xml2_xmlSAXUserParseFile=no -fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_xml2_xmlSAXUserParseFile" >&5 -$as_echo "$ac_cv_lib_xml2_xmlSAXUserParseFile" >&6; } -if test "x$ac_cv_lib_xml2_xmlSAXUserParseFile" = xyes; then : - - OLDCPPFLAGS=${CPPFLAGS} - CPPFLAGS=${XML2_CPPFLAGS} - ac_fn_cxx_check_header_mongrel "$LINENO" "libxml/parser.h" "ac_cv_header_libxml_parser_h" "$ac_includes_default" -if test "x$ac_cv_header_libxml_parser_h" = xyes; then : - - HAVE_LIBXML=1 - -$as_echo "#define HAVE_LIBXML 1" >>confdefs.h - - -else - - graphml_support=no - -fi - - - CPPFLAGS=${OLDCPPFLAGS} - -else - - graphml_support=no - -fi - - LIBS=${OLDLIBS} - fi -fi - - - - - -$as_echo "#define INTERNAL_GMP 1" >>confdefs.h - - -ac_ext=cpp -ac_cpp='$CXXCPP $CPPFLAGS' -ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_cxx_compiler_gnu - -HAVE_GMP=0 -INTERNAL_GMP=1 -GMP_LIBS="" -gmp_support=no -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for __gmpz_add in -lgmp" >&5 -$as_echo_n "checking for __gmpz_add in -lgmp... " >&6; } -if ${ac_cv_lib_gmp___gmpz_add+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_check_lib_save_LIBS=$LIBS -LIBS="-lgmp $LIBS" -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -/* Override any GCC internal prototype to avoid an error. - Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -#ifdef __cplusplus -extern "C" -#endif -char __gmpz_add (); -int -main () -{ -return __gmpz_add (); - ; - return 0; -} -_ACEOF -if ac_fn_cxx_try_link "$LINENO"; then : - ac_cv_lib_gmp___gmpz_add=yes -else - ac_cv_lib_gmp___gmpz_add=no -fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_gmp___gmpz_add" >&5 -$as_echo "$ac_cv_lib_gmp___gmpz_add" >&6; } -if test "x$ac_cv_lib_gmp___gmpz_add" = xyes; then : - - ac_fn_cxx_check_header_mongrel "$LINENO" "gmp.h" "ac_cv_header_gmp_h" "$ac_includes_default" -if test "x$ac_cv_header_gmp_h" = xyes; then : - - HAVE_GMP=1 - INTERNAL_GMP=0 - -$as_echo "#define HAVE_GMP 1" >>confdefs.h - - gmp_support=yes - GMP_LIBS="-lgmp" - -fi - - - -fi - - - - -ac_ext=cpp -ac_cpp='$CXXCPP $CPPFLAGS' -ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_cxx_compiler_gnu - - -HAVE_GLPK=0 -GLPK_LIBS="" -glpk_support=no -# Check whether --enable-glpk was given. -if test "${enable_glpk+set}" = set; then : - enableval=$enable_glpk; -fi - -if test "x$enable_gmp" != "xno"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for glp_read_mps in -lglpk" >&5 -$as_echo_n "checking for glp_read_mps in -lglpk... " >&6; } -if ${ac_cv_lib_glpk_glp_read_mps+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_check_lib_save_LIBS=$LIBS -LIBS="-lglpk $LIBS" -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -/* Override any GCC internal prototype to avoid an error. - Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -#ifdef __cplusplus -extern "C" -#endif -char glp_read_mps (); -int -main () -{ -return glp_read_mps (); - ; - return 0; -} -_ACEOF -if ac_fn_cxx_try_link "$LINENO"; then : - ac_cv_lib_glpk_glp_read_mps=yes -else - ac_cv_lib_glpk_glp_read_mps=no -fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_glpk_glp_read_mps" >&5 -$as_echo "$ac_cv_lib_glpk_glp_read_mps" >&6; } -if test "x$ac_cv_lib_glpk_glp_read_mps" = xyes; then : - - ac_fn_cxx_check_header_mongrel "$LINENO" "glpk.h" "ac_cv_header_glpk_h" "$ac_includes_default" -if test "x$ac_cv_header_glpk_h" = xyes; then : - - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - - #include - #if GLP_MAJOR_VERSION > 4 || (GLP_MAJOR_VERSION == 4 && GLP_MINOR_VERSION >= 57) - yes - #endif - -_ACEOF -if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | - $EGREP "yes" >/dev/null 2>&1; then : - - HAVE_GLPK=1 - -$as_echo "#define HAVE_GLPK 1" >>confdefs.h - - glpk_support=yes - GLPK_LIBS="-lglpk" - -fi -rm -f conftest* - - -fi - - - -fi - -fi - - - - -$as_echo "#define IGRAPH_THREAD_LOCAL /**/" >>confdefs.h - - -ac_config_files="$ac_config_files src/rigraph/Makevars.tmp:src/rigraph/Makevars.in" - - -cat >confcache <<\_ACEOF -# This file is a shell script that caches the results of configure -# tests run on this system so they can be shared between configure -# scripts and configure runs, see configure's option --config-cache. -# It is not useful on other systems. If it contains results you don't -# want to keep, you may remove or edit it. -# -# config.status only pays attention to the cache file if you give it -# the --recheck option to rerun configure. -# -# `ac_cv_env_foo' variables (set or unset) will be overridden when -# loading this file, other *unset* `ac_cv_foo' will be assigned the -# following values. - -_ACEOF - -# The following way of writing the cache mishandles newlines in values, -# but we know of no workaround that is simple, portable, and efficient. -# So, we kill variables containing newlines. -# Ultrix sh set writes to stderr and can't be redirected directly, -# and sets the high bit in the cache file unless we assign to the vars. -( - for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do - eval ac_val=\$$ac_var - case $ac_val in #( - *${as_nl}*) - case $ac_var in #( - *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 -$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; - esac - case $ac_var in #( - _ | IFS | as_nl) ;; #( - BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( - *) { eval $ac_var=; unset $ac_var;} ;; - esac ;; - esac - done - - (set) 2>&1 | - case $as_nl`(ac_space=' '; set) 2>&1` in #( - *${as_nl}ac_space=\ *) - # `set' does not quote correctly, so add quotes: double-quote - # substitution turns \\\\ into \\, and sed turns \\ into \. - sed -n \ - "s/'/'\\\\''/g; - s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" - ;; #( - *) - # `set' quotes correctly as required by POSIX, so do not add quotes. - sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" - ;; - esac | - sort -) | - sed ' - /^ac_cv_env_/b end - t clear - :clear - s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/ - t end - s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ - :end' >>confcache -if diff "$cache_file" confcache >/dev/null 2>&1; then :; else - if test -w "$cache_file"; then - if test "x$cache_file" != "x/dev/null"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5 -$as_echo "$as_me: updating cache $cache_file" >&6;} - if test ! -f "$cache_file" || test -h "$cache_file"; then - cat confcache >"$cache_file" - else - case $cache_file in #( - */* | ?:*) - mv -f confcache "$cache_file"$$ && - mv -f "$cache_file"$$ "$cache_file" ;; #( - *) - mv -f confcache "$cache_file" ;; - esac - fi - fi - else - { $as_echo "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5 -$as_echo "$as_me: not updating unwritable cache $cache_file" >&6;} - fi -fi -rm -f confcache - -test "x$prefix" = xNONE && prefix=$ac_default_prefix -# Let make expand exec_prefix. -test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' - -DEFS=-DHAVE_CONFIG_H - -ac_libobjs= -ac_ltlibobjs= -U= -for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue - # 1. Remove the extension, and $U if already installed. - ac_script='s/\$U\././;s/\.o$//;s/\.obj$//' - ac_i=`$as_echo "$ac_i" | sed "$ac_script"` - # 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR - # will be set to the directory where LIBOBJS objects are built. - as_fn_append ac_libobjs " \${LIBOBJDIR}$ac_i\$U.$ac_objext" - as_fn_append ac_ltlibobjs " \${LIBOBJDIR}$ac_i"'$U.lo' -done -LIBOBJS=$ac_libobjs - -LTLIBOBJS=$ac_ltlibobjs - - - -: "${CONFIG_STATUS=./config.status}" -ac_write_fail=0 -ac_clean_files_save=$ac_clean_files -ac_clean_files="$ac_clean_files $CONFIG_STATUS" -{ $as_echo "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5 -$as_echo "$as_me: creating $CONFIG_STATUS" >&6;} -as_write_fail=0 -cat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1 -#! $SHELL -# Generated by $as_me. -# Run this file to recreate the current configuration. -# Compiler output produced by configure, useful for debugging -# configure, is in config.log if it exists. - -debug=false -ac_cs_recheck=false -ac_cs_silent=false - -SHELL=\${CONFIG_SHELL-$SHELL} -export SHELL -_ASEOF -cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1 -## -------------------- ## -## M4sh Initialization. ## -## -------------------- ## - -# Be more Bourne compatible -DUALCASE=1; export DUALCASE # for MKS sh -if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : - emulate sh - NULLCMD=: - # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which - # is contrary to our usage. Disable this feature. - alias -g '${1+"$@"}'='"$@"' - setopt NO_GLOB_SUBST -else - case `(set -o) 2>/dev/null` in #( - *posix*) : - set -o posix ;; #( - *) : - ;; -esac -fi - - -as_nl=' -' -export as_nl -# Printing a long string crashes Solaris 7 /usr/bin/printf. -as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' -as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo -as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo -# Prefer a ksh shell builtin over an external printf program on Solaris, -# but without wasting forks for bash or zsh. -if test -z "$BASH_VERSION$ZSH_VERSION" \ - && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then - as_echo='print -r --' - as_echo_n='print -rn --' -elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then - as_echo='printf %s\n' - as_echo_n='printf %s' -else - if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then - as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' - as_echo_n='/usr/ucb/echo -n' - else - as_echo_body='eval expr "X$1" : "X\\(.*\\)"' - as_echo_n_body='eval - arg=$1; - case $arg in #( - *"$as_nl"*) - expr "X$arg" : "X\\(.*\\)$as_nl"; - arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; - esac; - expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" - ' - export as_echo_n_body - as_echo_n='sh -c $as_echo_n_body as_echo' - fi - export as_echo_body - as_echo='sh -c $as_echo_body as_echo' -fi - -# The user is always right. -if test "${PATH_SEPARATOR+set}" != set; then - PATH_SEPARATOR=: - (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { - (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || - PATH_SEPARATOR=';' - } -fi - - -# IFS -# We need space, tab and new line, in precisely that order. Quoting is -# there to prevent editors from complaining about space-tab. -# (If _AS_PATH_WALK were called with IFS unset, it would disable word -# splitting by setting IFS to empty value.) -IFS=" "" $as_nl" - -# Find who we are. Look in the path if we contain no directory separator. -as_myself= -case $0 in #(( - *[\\/]* ) as_myself=$0 ;; - *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break - done -IFS=$as_save_IFS - - ;; -esac -# We did not find ourselves, most probably we were run as `sh COMMAND' -# in which case we are not to be found in the path. -if test "x$as_myself" = x; then - as_myself=$0 -fi -if test ! -f "$as_myself"; then - $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 - exit 1 -fi - -# Unset variables that we do not need and which cause bugs (e.g. in -# pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" -# suppresses any "Segmentation fault" message there. '((' could -# trigger a bug in pdksh 5.2.14. -for as_var in BASH_ENV ENV MAIL MAILPATH -do eval test x\${$as_var+set} = xset \ - && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : -done -PS1='$ ' -PS2='> ' -PS4='+ ' - -# NLS nuisances. -LC_ALL=C -export LC_ALL -LANGUAGE=C -export LANGUAGE - -# CDPATH. -(unset CDPATH) >/dev/null 2>&1 && unset CDPATH - - -# as_fn_error STATUS ERROR [LINENO LOG_FD] -# ---------------------------------------- -# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are -# provided, also output the error to LOG_FD, referencing LINENO. Then exit the -# script with STATUS, using 1 if that was 0. -as_fn_error () -{ - as_status=$1; test $as_status -eq 0 && as_status=1 - if test "$4"; then - as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 - fi - $as_echo "$as_me: error: $2" >&2 - as_fn_exit $as_status -} # as_fn_error - - -# as_fn_set_status STATUS -# ----------------------- -# Set $? to STATUS, without forking. -as_fn_set_status () -{ - return $1 -} # as_fn_set_status - -# as_fn_exit STATUS -# ----------------- -# Exit the shell with STATUS, even in a "trap 0" or "set -e" context. -as_fn_exit () -{ - set +e - as_fn_set_status $1 - exit $1 -} # as_fn_exit - -# as_fn_unset VAR -# --------------- -# Portably unset VAR. -as_fn_unset () -{ - { eval $1=; unset $1;} -} -as_unset=as_fn_unset -# as_fn_append VAR VALUE -# ---------------------- -# Append the text in VALUE to the end of the definition contained in VAR. Take -# advantage of any shell optimizations that allow amortized linear growth over -# repeated appends, instead of the typical quadratic growth present in naive -# implementations. -if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : - eval 'as_fn_append () - { - eval $1+=\$2 - }' -else - as_fn_append () - { - eval $1=\$$1\$2 - } -fi # as_fn_append - -# as_fn_arith ARG... -# ------------------ -# Perform arithmetic evaluation on the ARGs, and store the result in the -# global $as_val. Take advantage of shells that can avoid forks. The arguments -# must be portable across $(()) and expr. -if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : - eval 'as_fn_arith () - { - as_val=$(( $* )) - }' -else - as_fn_arith () - { - as_val=`expr "$@" || test $? -eq 1` - } -fi # as_fn_arith - - -if expr a : '\(a\)' >/dev/null 2>&1 && - test "X`expr 00001 : '.*\(...\)'`" = X001; then - as_expr=expr -else - as_expr=false -fi - -if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then - as_basename=basename -else - as_basename=false -fi - -if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then - as_dirname=dirname -else - as_dirname=false -fi - -as_me=`$as_basename -- "$0" || -$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ - X"$0" : 'X\(//\)$' \| \ - X"$0" : 'X\(/\)' \| . 2>/dev/null || -$as_echo X/"$0" | - sed '/^.*\/\([^/][^/]*\)\/*$/{ - s//\1/ - q - } - /^X\/\(\/\/\)$/{ - s//\1/ - q - } - /^X\/\(\/\).*/{ - s//\1/ - q - } - s/.*/./; q'` - -# Avoid depending upon Character Ranges. -as_cr_letters='abcdefghijklmnopqrstuvwxyz' -as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' -as_cr_Letters=$as_cr_letters$as_cr_LETTERS -as_cr_digits='0123456789' -as_cr_alnum=$as_cr_Letters$as_cr_digits - -ECHO_C= ECHO_N= ECHO_T= -case `echo -n x` in #((((( --n*) - case `echo 'xy\c'` in - *c*) ECHO_T=' ';; # ECHO_T is single tab character. - xy) ECHO_C='\c';; - *) echo `echo ksh88 bug on AIX 6.1` > /dev/null - ECHO_T=' ';; - esac;; -*) - ECHO_N='-n';; -esac - -rm -f conf$$ conf$$.exe conf$$.file -if test -d conf$$.dir; then - rm -f conf$$.dir/conf$$.file -else - rm -f conf$$.dir - mkdir conf$$.dir 2>/dev/null -fi -if (echo >conf$$.file) 2>/dev/null; then - if ln -s conf$$.file conf$$ 2>/dev/null; then - as_ln_s='ln -s' - # ... but there are two gotchas: - # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. - # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. - # In both cases, we have to default to `cp -pR'. - ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || - as_ln_s='cp -pR' - elif ln conf$$.file conf$$ 2>/dev/null; then - as_ln_s=ln - else - as_ln_s='cp -pR' - fi -else - as_ln_s='cp -pR' -fi -rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file -rmdir conf$$.dir 2>/dev/null - - -# as_fn_mkdir_p -# ------------- -# Create "$as_dir" as a directory, including parents if necessary. -as_fn_mkdir_p () -{ - - case $as_dir in #( - -*) as_dir=./$as_dir;; - esac - test -d "$as_dir" || eval $as_mkdir_p || { - as_dirs= - while :; do - case $as_dir in #( - *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( - *) as_qdir=$as_dir;; - esac - as_dirs="'$as_qdir' $as_dirs" - as_dir=`$as_dirname -- "$as_dir" || -$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ - X"$as_dir" : 'X\(//\)[^/]' \| \ - X"$as_dir" : 'X\(//\)$' \| \ - X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || -$as_echo X"$as_dir" | - sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ - s//\1/ - q - } - /^X\(\/\/\)[^/].*/{ - s//\1/ - q - } - /^X\(\/\/\)$/{ - s//\1/ - q - } - /^X\(\/\).*/{ - s//\1/ - q - } - s/.*/./; q'` - test -d "$as_dir" && break - done - test -z "$as_dirs" || eval "mkdir $as_dirs" - } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" - - -} # as_fn_mkdir_p -if mkdir -p . 2>/dev/null; then - as_mkdir_p='mkdir -p "$as_dir"' -else - test -d ./-p && rmdir ./-p - as_mkdir_p=false -fi - - -# as_fn_executable_p FILE -# ----------------------- -# Test if FILE is an executable regular file. -as_fn_executable_p () -{ - test -f "$1" && test -x "$1" -} # as_fn_executable_p -as_test_x='test -x' -as_executable_p=as_fn_executable_p - -# Sed expression to map a string onto a valid CPP name. -as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" - -# Sed expression to map a string onto a valid variable name. -as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" - - -exec 6>&1 -## ----------------------------------- ## -## Main body of $CONFIG_STATUS script. ## -## ----------------------------------- ## -_ASEOF -test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1 - -cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 -# Save the log message, to keep $0 and so on meaningful, and to -# report actual input values of CONFIG_FILES etc. instead of their -# values after options handling. -ac_log=" -This file was extended by igraph $as_me 1.3.0.9016, which was -generated by GNU Autoconf 2.69. Invocation command line was - - CONFIG_FILES = $CONFIG_FILES - CONFIG_HEADERS = $CONFIG_HEADERS - CONFIG_LINKS = $CONFIG_LINKS - CONFIG_COMMANDS = $CONFIG_COMMANDS - $ $0 $@ - -on `(hostname || uname -n) 2>/dev/null | sed 1q` -" - -_ACEOF - -case $ac_config_files in *" -"*) set x $ac_config_files; shift; ac_config_files=$*;; -esac - -case $ac_config_headers in *" -"*) set x $ac_config_headers; shift; ac_config_headers=$*;; -esac - - -cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 -# Files that config.status was made for. -config_files="$ac_config_files" -config_headers="$ac_config_headers" - -_ACEOF - -cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 -ac_cs_usage="\ -\`$as_me' instantiates files and other configuration actions -from templates according to the current configuration. Unless the files -and actions are specified as TAGs, all are instantiated by default. - -Usage: $0 [OPTION]... [TAG]... - - -h, --help print this help, then exit - -V, --version print version number and configuration settings, then exit - --config print configuration, then exit - -q, --quiet, --silent - do not print progress messages - -d, --debug don't remove temporary files - --recheck update $as_me by reconfiguring in the same conditions - --file=FILE[:TEMPLATE] - instantiate the configuration file FILE - --header=FILE[:TEMPLATE] - instantiate the configuration header FILE - -Configuration files: -$config_files - -Configuration headers: -$config_headers - -Report bugs to ." - -_ACEOF -cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 -ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" -ac_cs_version="\\ -igraph config.status 1.3.0.9016 -configured by $0, generated by GNU Autoconf 2.69, - with options \\"\$ac_cs_config\\" - -Copyright (C) 2012 Free Software Foundation, Inc. -This config.status script is free software; the Free Software Foundation -gives unlimited permission to copy, distribute and modify it." - -ac_pwd='$ac_pwd' -srcdir='$srcdir' -test -n "\$AWK" || AWK=awk -_ACEOF - -cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 -# The default lists apply if the user does not specify any file. -ac_need_defaults=: -while test $# != 0 -do - case $1 in - --*=?*) - ac_option=`expr "X$1" : 'X\([^=]*\)='` - ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'` - ac_shift=: - ;; - --*=) - ac_option=`expr "X$1" : 'X\([^=]*\)='` - ac_optarg= - ac_shift=: - ;; - *) - ac_option=$1 - ac_optarg=$2 - ac_shift=shift - ;; - esac - - case $ac_option in - # Handling of the options. - -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) - ac_cs_recheck=: ;; - --version | --versio | --versi | --vers | --ver | --ve | --v | -V ) - $as_echo "$ac_cs_version"; exit ;; - --config | --confi | --conf | --con | --co | --c ) - $as_echo "$ac_cs_config"; exit ;; - --debug | --debu | --deb | --de | --d | -d ) - debug=: ;; - --file | --fil | --fi | --f ) - $ac_shift - case $ac_optarg in - *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; - '') as_fn_error $? "missing file argument" ;; - esac - as_fn_append CONFIG_FILES " '$ac_optarg'" - ac_need_defaults=false;; - --header | --heade | --head | --hea ) - $ac_shift - case $ac_optarg in - *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; - esac - as_fn_append CONFIG_HEADERS " '$ac_optarg'" - ac_need_defaults=false;; - --he | --h) - # Conflict between --help and --header - as_fn_error $? "ambiguous option: \`$1' -Try \`$0 --help' for more information.";; - --help | --hel | -h ) - $as_echo "$ac_cs_usage"; exit ;; - -q | -quiet | --quiet | --quie | --qui | --qu | --q \ - | -silent | --silent | --silen | --sile | --sil | --si | --s) - ac_cs_silent=: ;; - - # This is an error. - -*) as_fn_error $? "unrecognized option: \`$1' -Try \`$0 --help' for more information." ;; - - *) as_fn_append ac_config_targets " $1" - ac_need_defaults=false ;; - - esac - shift -done - -ac_configure_extra_args= - -if $ac_cs_silent; then - exec 6>/dev/null - ac_configure_extra_args="$ac_configure_extra_args --silent" -fi - -_ACEOF -cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 -if \$ac_cs_recheck; then - set X $SHELL '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion - shift - \$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6 - CONFIG_SHELL='$SHELL' - export CONFIG_SHELL - exec "\$@" -fi - -_ACEOF -cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 -exec 5>>config.log -{ - echo - sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX -## Running $as_me. ## -_ASBOX - $as_echo "$ac_log" -} >&5 - -_ACEOF -cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 -_ACEOF - -cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 - -# Handling of arguments. -for ac_config_target in $ac_config_targets -do - case $ac_config_target in - "src/rigraph/config.h") CONFIG_HEADERS="$CONFIG_HEADERS src/rigraph/config.h" ;; - "src/rigraph/Makevars.tmp") CONFIG_FILES="$CONFIG_FILES src/rigraph/Makevars.tmp:src/rigraph/Makevars.in" ;; - - *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;; - esac -done - - -# If the user did not use the arguments to specify the items to instantiate, -# then the envvar interface is used. Set only those that are not. -# We use the long form for the default assignment because of an extremely -# bizarre bug on SunOS 4.1.3. -if $ac_need_defaults; then - test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files - test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers -fi - -# Have a temporary directory for convenience. Make it in the build tree -# simply because there is no reason against having it here, and in addition, -# creating and moving files from /tmp can sometimes cause problems. -# Hook for its removal unless debugging. -# Note that there is a small window in which the directory will not be cleaned: -# after its creation but before its name has been assigned to `$tmp'. -$debug || -{ - tmp= ac_tmp= - trap 'exit_status=$? - : "${ac_tmp:=$tmp}" - { test ! -d "$ac_tmp" || rm -fr "$ac_tmp"; } && exit $exit_status -' 0 - trap 'as_fn_exit 1' 1 2 13 15 -} -# Create a (secure) tmp directory for tmp files. - -{ - tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` && - test -d "$tmp" -} || -{ - tmp=./conf$$-$RANDOM - (umask 077 && mkdir "$tmp") -} || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5 -ac_tmp=$tmp - -# Set up the scripts for CONFIG_FILES section. -# No need to generate them if there are no CONFIG_FILES. -# This happens for instance with `./config.status config.h'. -if test -n "$CONFIG_FILES"; then - - -ac_cr=`echo X | tr X '\015'` -# On cygwin, bash can eat \r inside `` if the user requested igncr. -# But we know of no other shell where ac_cr would be empty at this -# point, so we can use a bashism as a fallback. -if test "x$ac_cr" = x; then - eval ac_cr=\$\'\\r\' -fi -ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' /dev/null` -if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then - ac_cs_awk_cr='\\r' -else - ac_cs_awk_cr=$ac_cr -fi - -echo 'BEGIN {' >"$ac_tmp/subs1.awk" && -_ACEOF - - -{ - echo "cat >conf$$subs.awk <<_ACEOF" && - echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' && - echo "_ACEOF" -} >conf$$subs.sh || - as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 -ac_delim_num=`echo "$ac_subst_vars" | grep -c '^'` -ac_delim='%!_!# ' -for ac_last_try in false false false false false :; do - . ./conf$$subs.sh || - as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 - - ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X` - if test $ac_delim_n = $ac_delim_num; then - break - elif $ac_last_try; then - as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 - else - ac_delim="$ac_delim!$ac_delim _$ac_delim!! " - fi -done -rm -f conf$$subs.sh - -cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 -cat >>"\$ac_tmp/subs1.awk" <<\\_ACAWK && -_ACEOF -sed -n ' -h -s/^/S["/; s/!.*/"]=/ -p -g -s/^[^!]*!// -:repl -t repl -s/'"$ac_delim"'$// -t delim -:nl -h -s/\(.\{148\}\)..*/\1/ -t more1 -s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/ -p -n -b repl -:more1 -s/["\\]/\\&/g; s/^/"/; s/$/"\\/ -p -g -s/.\{148\}// -t nl -:delim -h -s/\(.\{148\}\)..*/\1/ -t more2 -s/["\\]/\\&/g; s/^/"/; s/$/"/ -p -b -:more2 -s/["\\]/\\&/g; s/^/"/; s/$/"\\/ -p -g -s/.\{148\}// -t delim -' >$CONFIG_STATUS || ac_write_fail=1 -rm -f conf$$subs.awk -cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 -_ACAWK -cat >>"\$ac_tmp/subs1.awk" <<_ACAWK && - for (key in S) S_is_set[key] = 1 - FS = "" - -} -{ - line = $ 0 - nfields = split(line, field, "@") - substed = 0 - len = length(field[1]) - for (i = 2; i < nfields; i++) { - key = field[i] - keylen = length(key) - if (S_is_set[key]) { - value = S[key] - line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3) - len += length(value) + length(field[++i]) - substed = 1 - } else - len += 1 + keylen - } - - print line -} - -_ACAWK -_ACEOF -cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 -if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then - sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g" -else - cat -fi < "$ac_tmp/subs1.awk" > "$ac_tmp/subs.awk" \ - || as_fn_error $? "could not setup config files machinery" "$LINENO" 5 -_ACEOF - -# VPATH may cause trouble with some makes, so we remove sole $(srcdir), -# ${srcdir} and @srcdir@ entries from VPATH if srcdir is ".", strip leading and -# trailing colons and then remove the whole line if VPATH becomes empty -# (actually we leave an empty line to preserve line numbers). -if test "x$srcdir" = x.; then - ac_vpsub='/^[ ]*VPATH[ ]*=[ ]*/{ -h -s/// -s/^/:/ -s/[ ]*$/:/ -s/:\$(srcdir):/:/g -s/:\${srcdir}:/:/g -s/:@srcdir@:/:/g -s/^:*// -s/:*$// -x -s/\(=[ ]*\).*/\1/ -G -s/\n// -s/^[^=]*=[ ]*$// -}' -fi - -cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 -fi # test -n "$CONFIG_FILES" - -# Set up the scripts for CONFIG_HEADERS section. -# No need to generate them if there are no CONFIG_HEADERS. -# This happens for instance with `./config.status Makefile'. -if test -n "$CONFIG_HEADERS"; then -cat >"$ac_tmp/defines.awk" <<\_ACAWK || -BEGIN { -_ACEOF - -# Transform confdefs.h into an awk script `defines.awk', embedded as -# here-document in config.status, that substitutes the proper values into -# config.h.in to produce config.h. - -# Create a delimiter string that does not exist in confdefs.h, to ease -# handling of long lines. -ac_delim='%!_!# ' -for ac_last_try in false false :; do - ac_tt=`sed -n "/$ac_delim/p" confdefs.h` - if test -z "$ac_tt"; then - break - elif $ac_last_try; then - as_fn_error $? "could not make $CONFIG_HEADERS" "$LINENO" 5 - else - ac_delim="$ac_delim!$ac_delim _$ac_delim!! " - fi -done - -# For the awk script, D is an array of macro values keyed by name, -# likewise P contains macro parameters if any. Preserve backslash -# newline sequences. - -ac_word_re=[_$as_cr_Letters][_$as_cr_alnum]* -sed -n ' -s/.\{148\}/&'"$ac_delim"'/g -t rset -:rset -s/^[ ]*#[ ]*define[ ][ ]*/ / -t def -d -:def -s/\\$// -t bsnl -s/["\\]/\\&/g -s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ -D["\1"]=" \3"/p -s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2"/p -d -:bsnl -s/["\\]/\\&/g -s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ -D["\1"]=" \3\\\\\\n"\\/p -t cont -s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2\\\\\\n"\\/p -t cont -d -:cont -n -s/.\{148\}/&'"$ac_delim"'/g -t clear -:clear -s/\\$// -t bsnlc -s/["\\]/\\&/g; s/^/"/; s/$/"/p -d -:bsnlc -s/["\\]/\\&/g; s/^/"/; s/$/\\\\\\n"\\/p -b cont -' >$CONFIG_STATUS || ac_write_fail=1 - -cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 - for (key in D) D_is_set[key] = 1 - FS = "" -} -/^[\t ]*#[\t ]*(define|undef)[\t ]+$ac_word_re([\t (]|\$)/ { - line = \$ 0 - split(line, arg, " ") - if (arg[1] == "#") { - defundef = arg[2] - mac1 = arg[3] - } else { - defundef = substr(arg[1], 2) - mac1 = arg[2] - } - split(mac1, mac2, "(") #) - macro = mac2[1] - prefix = substr(line, 1, index(line, defundef) - 1) - if (D_is_set[macro]) { - # Preserve the white space surrounding the "#". - print prefix "define", macro P[macro] D[macro] - next - } else { - # Replace #undef with comments. This is necessary, for example, - # in the case of _POSIX_SOURCE, which is predefined and required - # on some systems where configure will not decide to define it. - if (defundef == "undef") { - print "/*", prefix defundef, macro, "*/" - next - } - } -} -{ print } -_ACAWK -_ACEOF -cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 - as_fn_error $? "could not setup config headers machinery" "$LINENO" 5 -fi # test -n "$CONFIG_HEADERS" - - -eval set X " :F $CONFIG_FILES :H $CONFIG_HEADERS " -shift -for ac_tag -do - case $ac_tag in - :[FHLC]) ac_mode=$ac_tag; continue;; - esac - case $ac_mode$ac_tag in - :[FHL]*:*);; - :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;; - :[FH]-) ac_tag=-:-;; - :[FH]*) ac_tag=$ac_tag:$ac_tag.in;; - esac - ac_save_IFS=$IFS - IFS=: - set x $ac_tag - IFS=$ac_save_IFS - shift - ac_file=$1 - shift - - case $ac_mode in - :L) ac_source=$1;; - :[FH]) - ac_file_inputs= - for ac_f - do - case $ac_f in - -) ac_f="$ac_tmp/stdin";; - *) # Look for the file first in the build tree, then in the source tree - # (if the path is not absolute). The absolute path cannot be DOS-style, - # because $ac_f cannot contain `:'. - test -f "$ac_f" || - case $ac_f in - [\\/$]*) false;; - *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";; - esac || - as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;; - esac - case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac - as_fn_append ac_file_inputs " '$ac_f'" - done - - # Let's still pretend it is `configure' which instantiates (i.e., don't - # use $as_me), people would be surprised to read: - # /* config.h. Generated by config.status. */ - configure_input='Generated from '` - $as_echo "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g' - `' by configure.' - if test x"$ac_file" != x-; then - configure_input="$ac_file. $configure_input" - { $as_echo "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5 -$as_echo "$as_me: creating $ac_file" >&6;} - fi - # Neutralize special characters interpreted by sed in replacement strings. - case $configure_input in #( - *\&* | *\|* | *\\* ) - ac_sed_conf_input=`$as_echo "$configure_input" | - sed 's/[\\\\&|]/\\\\&/g'`;; #( - *) ac_sed_conf_input=$configure_input;; - esac - - case $ac_tag in - *:-:* | *:-) cat >"$ac_tmp/stdin" \ - || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; - esac - ;; - esac - - ac_dir=`$as_dirname -- "$ac_file" || -$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ - X"$ac_file" : 'X\(//\)[^/]' \| \ - X"$ac_file" : 'X\(//\)$' \| \ - X"$ac_file" : 'X\(/\)' \| . 2>/dev/null || -$as_echo X"$ac_file" | - sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ - s//\1/ - q - } - /^X\(\/\/\)[^/].*/{ - s//\1/ - q - } - /^X\(\/\/\)$/{ - s//\1/ - q - } - /^X\(\/\).*/{ - s//\1/ - q - } - s/.*/./; q'` - as_dir="$ac_dir"; as_fn_mkdir_p - ac_builddir=. - -case "$ac_dir" in -.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; -*) - ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` - # A ".." for each directory in $ac_dir_suffix. - ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` - case $ac_top_builddir_sub in - "") ac_top_builddir_sub=. ac_top_build_prefix= ;; - *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; - esac ;; -esac -ac_abs_top_builddir=$ac_pwd -ac_abs_builddir=$ac_pwd$ac_dir_suffix -# for backward compatibility: -ac_top_builddir=$ac_top_build_prefix - -case $srcdir in - .) # We are building in place. - ac_srcdir=. - ac_top_srcdir=$ac_top_builddir_sub - ac_abs_top_srcdir=$ac_pwd ;; - [\\/]* | ?:[\\/]* ) # Absolute name. - ac_srcdir=$srcdir$ac_dir_suffix; - ac_top_srcdir=$srcdir - ac_abs_top_srcdir=$srcdir ;; - *) # Relative name. - ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix - ac_top_srcdir=$ac_top_build_prefix$srcdir - ac_abs_top_srcdir=$ac_pwd/$srcdir ;; -esac -ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix - - - case $ac_mode in - :F) - # - # CONFIG_FILE - # - -_ACEOF - -cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 -# If the template does not know about datarootdir, expand it. -# FIXME: This hack should be removed a few years after 2.60. -ac_datarootdir_hack=; ac_datarootdir_seen= -ac_sed_dataroot=' -/datarootdir/ { - p - q -} -/@datadir@/p -/@docdir@/p -/@infodir@/p -/@localedir@/p -/@mandir@/p' -case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in -*datarootdir*) ac_datarootdir_seen=yes;; -*@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*) - { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5 -$as_echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;} -_ACEOF -cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 - ac_datarootdir_hack=' - s&@datadir@&$datadir&g - s&@docdir@&$docdir&g - s&@infodir@&$infodir&g - s&@localedir@&$localedir&g - s&@mandir@&$mandir&g - s&\\\${datarootdir}&$datarootdir&g' ;; -esac -_ACEOF - -# Neutralize VPATH when `$srcdir' = `.'. -# Shell code in configure.ac might set extrasub. -# FIXME: do we really want to maintain this feature? -cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 -ac_sed_extra="$ac_vpsub -$extrasub -_ACEOF -cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 -:t -/@[a-zA-Z_][a-zA-Z_0-9]*@/!b -s|@configure_input@|$ac_sed_conf_input|;t t -s&@top_builddir@&$ac_top_builddir_sub&;t t -s&@top_build_prefix@&$ac_top_build_prefix&;t t -s&@srcdir@&$ac_srcdir&;t t -s&@abs_srcdir@&$ac_abs_srcdir&;t t -s&@top_srcdir@&$ac_top_srcdir&;t t -s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t -s&@builddir@&$ac_builddir&;t t -s&@abs_builddir@&$ac_abs_builddir&;t t -s&@abs_top_builddir@&$ac_abs_top_builddir&;t t -$ac_datarootdir_hack -" -eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$ac_tmp/subs.awk" \ - >$ac_tmp/out || as_fn_error $? "could not create $ac_file" "$LINENO" 5 - -test -z "$ac_datarootdir_hack$ac_datarootdir_seen" && - { ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } && - { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' \ - "$ac_tmp/out"`; test -z "$ac_out"; } && - { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir' -which seems to be undefined. Please make sure it is defined" >&5 -$as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir' -which seems to be undefined. Please make sure it is defined" >&2;} - - rm -f "$ac_tmp/stdin" - case $ac_file in - -) cat "$ac_tmp/out" && rm -f "$ac_tmp/out";; - *) rm -f "$ac_file" && mv "$ac_tmp/out" "$ac_file";; - esac \ - || as_fn_error $? "could not create $ac_file" "$LINENO" 5 - ;; - :H) - # - # CONFIG_HEADER - # - if test x"$ac_file" != x-; then - { - $as_echo "/* $configure_input */" \ - && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" - } >"$ac_tmp/config.h" \ - || as_fn_error $? "could not create $ac_file" "$LINENO" 5 - if diff "$ac_file" "$ac_tmp/config.h" >/dev/null 2>&1; then - { $as_echo "$as_me:${as_lineno-$LINENO}: $ac_file is unchanged" >&5 -$as_echo "$as_me: $ac_file is unchanged" >&6;} - else - rm -f "$ac_file" - mv "$ac_tmp/config.h" "$ac_file" \ - || as_fn_error $? "could not create $ac_file" "$LINENO" 5 - fi - else - $as_echo "/* $configure_input */" \ - && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" \ - || as_fn_error $? "could not create -" "$LINENO" 5 - fi - ;; - - - esac - - - case $ac_file$ac_mode in - "src/rigraph/Makevars.tmp":F) - if test -f src/rigraph/Makevars && cmp -s src/rigraph/Makevars.tmp src/rigraph/Makevars; then - { $as_echo "$as_me:${as_lineno-$LINENO}: creating src/rigraph/Makevars" >&5 -$as_echo "$as_me: creating src/rigraph/Makevars" >&6;} - { $as_echo "$as_me:${as_lineno-$LINENO}: src/rigraph/Makevars is unchanged" >&5 -$as_echo "$as_me: src/rigraph/Makevars is unchanged" >&6;} - rm src/rigraph/Makevars.tmp - else - { $as_echo "$as_me:${as_lineno-$LINENO}: creating src/rigraph/Makevars" >&5 -$as_echo "$as_me: creating src/rigraph/Makevars" >&6;} - mv src/rigraph/Makevars.tmp src/rigraph/Makevars - fi - - ;; - - esac -done # for ac_tag - - -as_fn_exit 0 -_ACEOF -ac_clean_files=$ac_clean_files_save - -test $ac_write_fail = 0 || - as_fn_error $? "write failure creating $CONFIG_STATUS" "$LINENO" 5 - - -# configure is writing to config.log, and then calls config.status. -# config.status does its own redirection, appending to config.log. -# Unfortunately, on DOS this fails, as config.log is still kept open -# by configure, so config.status won't be able to write to it; its -# output is simply discarded. So we exec the FD to /dev/null, -# effectively closing config.log, so it can be properly (re)opened and -# appended to by config.status. When coming back to configure, we -# need to make the FD available again. -if test "$no_create" != yes; then - ac_cs_success=: - ac_config_status_args= - test "$silent" = yes && - ac_config_status_args="$ac_config_status_args --quiet" - exec 5>/dev/null - $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false - exec 5>>config.log - # Use ||, not &&, to avoid exiting from the if with $? = 1, which - # would make configure fail if this is the last instruction. - $ac_cs_success || as_fn_exit 1 -fi -if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then - { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5 -$as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;} -fi - - -echo "" -echo "*** Compiler settings used:" -echo " CC=${CC}" -echo " LD=${LD}" -echo " CFLAGS=${CFLAGS}" -echo " CPPFLAGS=${CPPFLAGS}" -echo " CXX=${CXX}" -echo " CXXFLAGS=${CXXFLAGS}" -echo " LDFLAGS=${LDFLAGS}" -echo " LIBS=${LIBS}" - diff --git a/configure.ac b/configure.ac deleted file mode 100644 index e683638..0000000 --- a/configure.ac +++ /dev/null @@ -1,181 +0,0 @@ -AC_INIT(igraph, 1.3.0.9016, ntamas@gmail.com) -AC_CONFIG_SRCDIR(src/rigraph/rinterface.c) -AC_CONFIG_HEADERS(src/rigraph/config.h) - -: ${R_HOME=`R RHOME`} -if test -z "${R_HOME}"; then - echo "could not determine R_HOME" - exit 1 -fi -CC=`"${R_HOME}/bin/R" CMD config CC` -CXX=`"${R_HOME}/bin/R" CMD config CXX11` -if test -z "$CXX"; then - AC_MSG_ERROR([No C++11 compiler is available]) -fi -CXX11STD=`"${R_HOME}/bin/R" CMD config CXX11STD` -CXX="${CXX} ${CXX11STD}" -FC=`"${R_HOME}/bin/R" CMD config FC` -CFLAGS=`"${R_HOME}/bin/R" CMD config CFLAGS` -CXXFLAGS=`"${R_HOME}/bin/R" CMD config CXX11FLAGS` -CPPFLAGS=`"${R_HOME}/bin/R" CMD config CPPFLAGS` -FCFLAGS=`"${R_HOME}/bin/R" CMD config FCFLAGS` -FLIBS=`"${R_HOME}/bin/R" CMD config FLIBS` -LDFLAGS=`"${R_HOME}/bin/R" CMD config LDFLAGS` - -AC_LANG(C) -AC_PROG_CC - -AC_LANG(C++) -AC_PROG_CXX - -# Fortran compiler, we need to check if it is the GNU compiler -AC_PROG_FC -if test "x$ac_cv_fc_compiler_gnu" = xyes; then - AC_DEFINE([HAVE_GFORTRAN], [1], [Define to 1 if using the GNU Fortran compiler]) -fi - -LIBS_SAVE=$LIBS -LIBS="$LIBS -lm" -AC_CHECK_FUNCS([expm1 fmin finite log2 log1p rint rintf round stpcpy strcasecmp _stricmp strdup]) -AC_CHECK_FUNCS(isfinite, - [AC_DEFINE(HAVE_ISFINITE, 1)], - [AC_LINK_IFELSE( - [AC_LANG_PROGRAM( - [[#include ]], - [[double f = 0.0; isfinite(f)]])], - [AC_DEFINE(HAVE_ISFINITE, 1)], - [AC_MSG_FAILURE([isfinite() not available])] - )] -) -LIBS=$LIBS_SAVE - -AC_CHECK_HEADER([sys/times.h], - [AC_DEFINE([HAVE_TIMES_H], [1], [Define to 1 if you have the sys/times.h header])]) - -AC_CHECK_HEADERS([ \ - net/if.h \ - netinet/in.h \ - net/if_dl.h \ - sys/sockio.h \ - sys/un.h \ - sys/socket.h \ - sys/ioctl.h \ - sys/time.h \ - sys/file.h \ - ]) - -AC_CHECK_MEMBER([struct sockaddr.sa_len], - AC_DEFINE_UNQUOTED([HAVE_SA_LEN], [1], [Define if struct sockaddr contains sa_len]), [], - [#include - #include ]) - -graphml_support=yes -AC_ARG_ENABLE(graphml, - AS_HELP_STRING([--disable-graphml], [Disable support for GraphML format]), - [graphml_support=$enableval], [graphml_support=yes]) - -HAVE_LIBXML=0 -if test $graphml_support = yes; then - AC_PATH_PROG([XML2CONFIG], [xml2-config], [none]) - if test "$XML2CONFIG" = "none"; then - graphml_support=no - else - # xml2-config puts only preprocessor flags in --cflags (i.e. -D and -I) so - # we can put them in XML2_CPPFLAGS. This might not be true for other - # libraries, though. - XML2_CPPFLAGS=`$XML2CONFIG --cflags` - XML2_LIBS=`$XML2CONFIG --libs` - OLDLIBS=${LIBS} - LIBS=${XML2_LIBS} - AC_CHECK_LIB([xml2], [xmlSAXUserParseFile], [ - OLDCPPFLAGS=${CPPFLAGS} - CPPFLAGS=${XML2_CPPFLAGS} - AC_CHECK_HEADER([libxml/parser.h], [ - HAVE_LIBXML=1 - AC_DEFINE([HAVE_LIBXML], [1], [Define to 1 if you have the libxml2 libraries installed]) - ], [ - graphml_support=no - ]) - CPPFLAGS=${OLDCPPFLAGS} - ], [ - graphml_support=no - ]) - LIBS=${OLDLIBS} - fi -fi -AC_SUBST(HAVE_LIBXML) -AC_SUBST(XML2_LIBS) -AC_SUBST(XML2_CPPFLAGS) - -AC_DEFINE([INTERNAL_GMP], [1], [Define to 1 if you use the vendored mini-GMP library]) - -AC_LANG_PUSH([C++]) -HAVE_GMP=0 -INTERNAL_GMP=1 -GMP_LIBS="" -gmp_support=no -AC_CHECK_LIB([gmp], [__gmpz_add], [ - AC_CHECK_HEADER([gmp.h], [ - HAVE_GMP=1 - INTERNAL_GMP=0 - AC_DEFINE([HAVE_GMP], [1], [Define to 1 if you have the GMP library]) - gmp_support=yes - GMP_LIBS="-lgmp" - ]) -]) -AC_SUBST(HAVE_GMP) -AC_SUBST(INTERNAL_GMP) -AC_SUBST(GMP_LIBS) -AC_LANG_POP([C++]) - -HAVE_GLPK=0 -GLPK_LIBS="" -glpk_support=no -AC_ARG_ENABLE(glpk, AS_HELP_STRING([--disable-glpk], [Compile without the GLPK library])) -if test "x$enable_gmp" != "xno"; then - AC_CHECK_LIB([glpk], [glp_read_mps], [ - AC_CHECK_HEADER([glpk.h], [ - AC_EGREP_CPP(yes, [ - #include - #if GLP_MAJOR_VERSION > 4 || (GLP_MAJOR_VERSION == 4 && GLP_MINOR_VERSION >= 57) - yes - #endif - ], [ - HAVE_GLPK=1 - AC_DEFINE([HAVE_GLPK], [1], [Define to 1 if you have the GLPK library]) - glpk_support=yes - GLPK_LIBS="-lglpk" - ]) - ]) - ]) -fi -AC_SUBST(HAVE_GLPK) -AC_SUBST(GLPK_LIBS) - -AC_DEFINE(IGRAPH_THREAD_LOCAL, [], [We don't care about thread-local storage in R]) - -AC_CONFIG_FILES([src/rigraph/Makevars.tmp:src/Makevars.in], [ - if test -f src/rigraph/Makevars && cmp -s src/rigraph/Makevars.tmp src/rigraph/Makevars; then - AC_MSG_NOTICE([creating src/rigraph/Makevars]) - AC_MSG_NOTICE([src/rigraph/Makevars is unchanged]) - rm src/rigraph/Makevars.tmp - else - AC_MSG_NOTICE([creating src/rigraph/Makevars]) - mv src/rigraph/Makevars.tmp src/rigraph/Makevars - fi - ] -) - -AC_OUTPUT - -echo "" -echo "*** Compiler settings used:" -echo " CC=${CC}" -echo " LD=${LD}" -echo " CFLAGS=${CFLAGS}" -echo " CPPFLAGS=${CPPFLAGS}" -echo " CXX=${CXX}" -echo " CXXFLAGS=${CXXFLAGS}" -echo " LDFLAGS=${LDFLAGS}" -echo " LIBS=${LIBS}" - diff --git a/configure.win b/configure.win deleted file mode 100644 index e69de29..0000000 diff --git a/src/.DS_Store b/src/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..cbc1e85bc7a887fa4e25d2822f7a84ba834ad5ef GIT binary patch literal 6148 zcmeHK%}T>S5Z<-brW7Fug&r5Y7ObrlikA@U3mDOZN=-;)@FZeqVE3DypWVy{*&oIjcNgIaV>V;Vf`-UZsSq?*x^_%3B3E_*GD%fJ6iy)I?j}k?xpd_s2{V=JX@}Lcnq#}YTJ?s5j_CJ? z>yB6rd)F&I)&U(}pV41OL;)S&5{SZ}W3bQ& z9uTfm0d*=jPYkZp!7fakW3bSu(-~JQ!#rl?^6|pe>R=ZtoN-4Z^~3-%u*pD84{bdE zFW{G{eB^JY(1;iy2L2fXygu?qE)-?X)^FwESu3DDKtsW}0u>O@H!cA%z header file. */ -#undef HAVE_INTTYPES_H - -/* Define to 1 if you have the `isfinite' function. */ -#undef HAVE_ISFINITE - -/* Define to 1 if you have the libxml2 libraries installed */ -#undef HAVE_LIBXML - -/* Define to 1 if you have the `log1p' function. */ -#undef HAVE_LOG1P - -/* Define to 1 if you have the `log2' function. */ -#undef HAVE_LOG2 - -/* Define to 1 if you have the header file. */ -#undef HAVE_MEMORY_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_NETINET_IN_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_NET_IF_DL_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_NET_IF_H - -/* Define to 1 if you have the `rint' function. */ -#undef HAVE_RINT - -/* Define to 1 if you have the `rintf' function. */ -#undef HAVE_RINTF - -/* Define to 1 if you have the `round' function. */ -#undef HAVE_ROUND - -/* Define if struct sockaddr contains sa_len */ -#undef HAVE_SA_LEN - -/* Define to 1 if you have the header file. */ -#undef HAVE_STDINT_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_STDLIB_H - -/* Define to 1 if you have the `stpcpy' function. */ -#undef HAVE_STPCPY - -/* Define to 1 if you have the `strcasecmp' function. */ -#undef HAVE_STRCASECMP - -/* Define to 1 if you have the `strdup' function. */ -#undef HAVE_STRDUP - -/* Define to 1 if you have the header file. */ -#undef HAVE_STRINGS_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_STRING_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_SYS_FILE_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_SYS_IOCTL_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_SYS_SOCKET_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_SYS_SOCKIO_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_SYS_STAT_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_SYS_TIME_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_SYS_TYPES_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_SYS_UN_H - -/* Define to 1 if you have the sys/times.h header */ -#undef HAVE_TIMES_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_UNISTD_H - -/* Define to 1 if you have the `_stricmp' function. */ -#undef HAVE__STRICMP - -/* We don't care about thread-local storage in R */ -#undef IGRAPH_THREAD_LOCAL - -/* Define to 1 if you use the vendored mini-GMP library */ -#undef INTERNAL_GMP - -/* Define to the address where bug reports for this package should be sent. */ -#undef PACKAGE_BUGREPORT - -/* Define to the full name of this package. */ -#undef PACKAGE_NAME - -/* Define to the full name and version of this package. */ -#undef PACKAGE_STRING - -/* Define to the one symbol short name of this package. */ -#undef PACKAGE_TARNAME - -/* Define to the home page for this package. */ -#undef PACKAGE_URL - -/* Define to the version of this package. */ -#undef PACKAGE_VERSION - -/* Define to 1 if you have the ANSI C header files. */ -#undef STDC_HEADERS -#define HAVE_ISFINITE HAVE_DECL_ISFINITE diff --git a/src/rigraph/igraph-win.def b/src/rigraph/igraph-win.def deleted file mode 100644 index a7a6ea2..0000000 --- a/src/rigraph/igraph-win.def +++ /dev/null @@ -1,3 +0,0 @@ -LIBRARY igraph.dll -EXPORTS - R_init_igraph diff --git a/src/rigraph/include/igraph_version.h b/src/rigraph/include/igraph_version.h index 222dd9d..05050e0 100644 --- a/src/rigraph/include/igraph_version.h +++ b/src/rigraph/include/igraph_version.h @@ -28,7 +28,7 @@ __BEGIN_DECLS -#define IGRAPH_VERSION "1.3.0.9016" +#define IGRAPH_VERSION "1.3.0.9023" #define IGRAPH_VERSION_MAJOR @PACKAGE_VERSION_MAJOR@ #define IGRAPH_VERSION_MINOR @PACKAGE_VERSION_MINOR@ #define IGRAPH_VERSION_PATCH @PACKAGE_VERSION_PATCH@ diff --git a/src/rigraph/vendor/arpack/debug.h b/src/rigraph/vendor/arpack/debug.h deleted file mode 100644 index 5eb0bb1..0000000 --- a/src/rigraph/vendor/arpack/debug.h +++ /dev/null @@ -1,16 +0,0 @@ -c -c\SCCS Information: @(#) -c FILE: debug.h SID: 2.3 DATE OF SID: 11/16/95 RELEASE: 2 -c -c %---------------------------------% -c | See debug.doc for documentation | -c %---------------------------------% - integer logfil, ndigit, mgetv0, - & msaupd, msaup2, msaitr, mseigt, msapps, msgets, mseupd, - & mnaupd, mnaup2, mnaitr, mneigh, mnapps, mngets, mneupd, - & mcaupd, mcaup2, mcaitr, mceigh, mcapps, mcgets, mceupd - common /debug/ - & logfil, ndigit, mgetv0, - & msaupd, msaup2, msaitr, mseigt, msapps, msgets, mseupd, - & mnaupd, mnaup2, mnaitr, mneigh, mnapps, mngets, mneupd, - & mcaupd, mcaup2, mcaitr, mceigh, mcapps, mcgets, mceupd diff --git a/src/rigraph/vendor/arpack/dgetv0.f b/src/rigraph/vendor/arpack/dgetv0.f deleted file mode 100644 index b64d47a..0000000 --- a/src/rigraph/vendor/arpack/dgetv0.f +++ /dev/null @@ -1,419 +0,0 @@ -c----------------------------------------------------------------------- -c\BeginDoc -c -c\Name: igraphdgetv0 -c -c\Description: -c Generate a random initial residual vector for the Arnoldi process. -c Force the residual vector to be in the range of the operator OP. -c -c\Usage: -c call igraphdgetv0 -c ( IDO, BMAT, ITRY, INITV, N, J, V, LDV, RESID, RNORM, -c IPNTR, WORKD, IERR ) -c -c\Arguments -c IDO Integer. (INPUT/OUTPUT) -c Reverse communication flag. IDO must be zero on the first -c call to igraphdgetv0. -c ------------------------------------------------------------- -c IDO = 0: first call to the reverse communication interface -c IDO = -1: compute Y = OP * X where -c IPNTR(1) is the pointer into WORKD for X, -c IPNTR(2) is the pointer into WORKD for Y. -c This is for the initialization phase to force the -c starting vector into the range of OP. -c IDO = 2: compute Y = B * X where -c IPNTR(1) is the pointer into WORKD for X, -c IPNTR(2) is the pointer into WORKD for Y. -c IDO = 99: done -c ------------------------------------------------------------- -c -c BMAT Character*1. (INPUT) -c BMAT specifies the type of the matrix B in the (generalized) -c eigenvalue problem A*x = lambda*B*x. -c B = 'I' -> standard eigenvalue problem A*x = lambda*x -c B = 'G' -> generalized eigenvalue problem A*x = lambda*B*x -c -c ITRY Integer. (INPUT) -c ITRY counts the number of times that igraphdgetv0 is called. -c It should be set to 1 on the initial call to igraphdgetv0. -c -c INITV Logical variable. (INPUT) -c .TRUE. => the initial residual vector is given in RESID. -c .FALSE. => generate a random initial residual vector. -c -c N Integer. (INPUT) -c Dimension of the problem. -c -c J Integer. (INPUT) -c Index of the residual vector to be generated, with respect to -c the Arnoldi process. J > 1 in case of a "restart". -c -c V Double precision N by J array. (INPUT) -c The first J-1 columns of V contain the current Arnoldi basis -c if this is a "restart". -c -c LDV Integer. (INPUT) -c Leading dimension of V exactly as declared in the calling -c program. -c -c RESID Double precision array of length N. (INPUT/OUTPUT) -c Initial residual vector to be generated. If RESID is -c provided, force RESID into the range of the operator OP. -c -c RNORM Double precision scalar. (OUTPUT) -c B-norm of the generated residual. -c -c IPNTR Integer array of length 3. (OUTPUT) -c -c WORKD Double precision work array of length 2*N. (REVERSE COMMUNICATION). -c On exit, WORK(1:N) = B*RESID to be used in SSAITR. -c -c IERR Integer. (OUTPUT) -c = 0: Normal exit. -c = -1: Cannot generate a nontrivial restarted residual vector -c in the range of the operator OP. -c -c\EndDoc -c -c----------------------------------------------------------------------- -c -c\BeginLib -c -c\Local variables: -c xxxxxx real -c -c\References: -c 1. D.C. Sorensen, "Implicit Application of Polynomial Filters in -c a k-Step Arnoldi Method", SIAM J. Matr. Anal. Apps., 13 (1992), -c pp 357-385. -c 2. R.B. Lehoucq, "Analysis and Implementation of an Implicitly -c Restarted Arnoldi Iteration", Rice University Technical Report -c TR95-13, Department of Computational and Applied Mathematics. -c -c\Routines called: -c igraphsecond ARPACK utility routine for timing. -c igraphdvout ARPACK utility routine for vector output. -c dlarnv LAPACK routine for generating a random vector. -c dgemv Level 2 BLAS routine for matrix vector multiplication. -c dcopy Level 1 BLAS that copies one vector to another. -c ddot Level 1 BLAS that computes the scalar product of two vectors. -c dnrm2 Level 1 BLAS that computes the norm of a vector. -c -c\Author -c Danny Sorensen Phuong Vu -c Richard Lehoucq CRPC / Rice University -c Dept. of Computational & Houston, Texas -c Applied Mathematics -c Rice University -c Houston, Texas -c -c\SCCS Information: @(#) -c FILE: getv0.F SID: 2.6 DATE OF SID: 8/27/96 RELEASE: 2 -c -c\EndLib -c -c----------------------------------------------------------------------- -c - subroutine igraphdgetv0 - & ( ido, bmat, itry, initv, n, j, v, ldv, resid, rnorm, - & ipntr, workd, ierr ) -c -c %----------------------------------------------------% -c | Include files for debugging and timing information | -c %----------------------------------------------------% -c - include 'debug.h' - include 'stat.h' -c -c %------------------% -c | Scalar Arguments | -c %------------------% -c - character bmat*1 - logical initv - integer ido, ierr, itry, j, ldv, n - Double precision - & rnorm -c -c %-----------------% -c | Array Arguments | -c %-----------------% -c - integer ipntr(3) - Double precision - & resid(n), v(ldv,j), workd(2*n) -c -c %------------% -c | Parameters | -c %------------% -c - Double precision - & one, zero - parameter (one = 1.0D+0, zero = 0.0D+0) -c -c %------------------------% -c | Local Scalars & Arrays | -c %------------------------% -c - logical first, inits, orth - integer idist, iseed(4), iter, msglvl, jj - Double precision - & rnorm0 - save first, iseed, inits, iter, msglvl, orth, rnorm0 -c -c %----------------------% -c | External Subroutines | -c %----------------------% -c - external dlarnv, igraphdvout, dcopy, dgemv, igraphsecond -c -c %--------------------% -c | External Functions | -c %--------------------% -c - Double precision - & ddot, dnrm2 - external ddot, dnrm2 -c -c %---------------------% -c | Intrinsic Functions | -c %---------------------% -c - intrinsic abs, sqrt -c -c %-----------------% -c | Data Statements | -c %-----------------% -c - data inits /.true./ -c -c %-----------------------% -c | Executable Statements | -c %-----------------------% -c -c -c %-----------------------------------% -c | Initialize the seed of the LAPACK | -c | random number generator | -c %-----------------------------------% -c - if (inits) then - iseed(1) = 1 - iseed(2) = 3 - iseed(3) = 5 - iseed(4) = 7 - inits = .false. - end if -c - if (ido .eq. 0) then -c -c %-------------------------------% -c | Initialize timing statistics | -c | & message level for debugging | -c %-------------------------------% -c - call igraphsecond (t0) - msglvl = mgetv0 -c - ierr = 0 - iter = 0 - first = .FALSE. - orth = .FALSE. -c -c %-----------------------------------------------------% -c | Possibly generate a random starting vector in RESID | -c | Use a LAPACK random number generator used by the | -c | matrix generation routines. | -c | idist = 1: uniform (0,1) distribution; | -c | idist = 2: uniform (-1,1) distribution; | -c | idist = 3: normal (0,1) distribution; | -c %-----------------------------------------------------% -c - if (.not.initv) then - idist = 2 - call dlarnv (idist, iseed, n, resid) - end if -c -c %----------------------------------------------------------% -c | Force the starting vector into the range of OP to handle | -c | the generalized problem when B is possibly (singular). | -c %----------------------------------------------------------% -c - call igraphsecond (t2) - if (bmat .eq. 'G') then - nopx = nopx + 1 - ipntr(1) = 1 - ipntr(2) = n + 1 - call dcopy (n, resid, 1, workd, 1) - ido = -1 - go to 9000 - end if - end if -c -c %-----------------------------------------% -c | Back from computing OP*(initial-vector) | -c %-----------------------------------------% -c - if (first) go to 20 -c -c %-----------------------------------------------% -c | Back from computing B*(orthogonalized-vector) | -c %-----------------------------------------------% -c - if (orth) go to 40 -c - if (bmat .eq. 'G') then - call igraphsecond (t3) - tmvopx = tmvopx + (t3 - t2) - end if -c -c %------------------------------------------------------% -c | Starting vector is now in the range of OP; r = OP*r; | -c | Compute B-norm of starting vector. | -c %------------------------------------------------------% -c - call igraphsecond (t2) - first = .TRUE. - if (bmat .eq. 'G') then - nbx = nbx + 1 - call dcopy (n, workd(n+1), 1, resid, 1) - ipntr(1) = n + 1 - ipntr(2) = 1 - ido = 2 - go to 9000 - else if (bmat .eq. 'I') then - call dcopy (n, resid, 1, workd, 1) - end if -c - 20 continue -c - if (bmat .eq. 'G') then - call igraphsecond (t3) - tmvbx = tmvbx + (t3 - t2) - end if -c - first = .FALSE. - if (bmat .eq. 'G') then - rnorm0 = ddot (n, resid, 1, workd, 1) - rnorm0 = sqrt(abs(rnorm0)) - else if (bmat .eq. 'I') then - rnorm0 = dnrm2(n, resid, 1) - end if - rnorm = rnorm0 -c -c %---------------------------------------------% -c | Exit if this is the very first Arnoldi step | -c %---------------------------------------------% -c - if (j .eq. 1) go to 50 -c -c %---------------------------------------------------------------- -c | Otherwise need to B-orthogonalize the starting vector against | -c | the current Arnoldi basis using Gram-Schmidt with iter. ref. | -c | This is the case where an invariant subspace is encountered | -c | in the middle of the Arnoldi factorization. | -c | | -c | s = V^{T}*B*r; r = r - V*s; | -c | | -c | Stopping criteria used for iter. ref. is discussed in | -c | Parlett's book, page 107 and in Gragg & Reichel TOMS paper. | -c %---------------------------------------------------------------% -c - orth = .TRUE. - 30 continue -c - call dgemv ('T', n, j-1, one, v, ldv, workd, 1, - & zero, workd(n+1), 1) - call dgemv ('N', n, j-1, -one, v, ldv, workd(n+1), 1, - & one, resid, 1) -c -c %----------------------------------------------------------% -c | Compute the B-norm of the orthogonalized starting vector | -c %----------------------------------------------------------% -c - call igraphsecond (t2) - if (bmat .eq. 'G') then - nbx = nbx + 1 - call dcopy (n, resid, 1, workd(n+1), 1) - ipntr(1) = n + 1 - ipntr(2) = 1 - ido = 2 - go to 9000 - else if (bmat .eq. 'I') then - call dcopy (n, resid, 1, workd, 1) - end if -c - 40 continue -c - if (bmat .eq. 'G') then - call igraphsecond (t3) - tmvbx = tmvbx + (t3 - t2) - end if -c - if (bmat .eq. 'G') then - rnorm = ddot (n, resid, 1, workd, 1) - rnorm = sqrt(abs(rnorm)) - else if (bmat .eq. 'I') then - rnorm = dnrm2(n, resid, 1) - end if -c -c %--------------------------------------% -c | Check for further orthogonalization. | -c %--------------------------------------% -c - if (msglvl .gt. 2) then - call igraphdvout (logfil, 1, [rnorm0], ndigit, - & '_getv0: re-orthonalization ; rnorm0 is') - call igraphdvout (logfil, 1, [rnorm], ndigit, - & '_getv0: re-orthonalization ; rnorm is') - end if -c - if (rnorm .gt. 0.717*rnorm0) go to 50 -c - iter = iter + 1 - if (iter .le. 1) then -c -c %-----------------------------------% -c | Perform iterative refinement step | -c %-----------------------------------% -c - rnorm0 = rnorm - go to 30 - else -c -c %------------------------------------% -c | Iterative refinement step "failed" | -c %------------------------------------% -c - do 45 jj = 1, n - resid(jj) = zero - 45 continue - rnorm = zero - ierr = -1 - end if -c - 50 continue -c - if (msglvl .gt. 0) then - call igraphdvout (logfil, 1, [rnorm], ndigit, - & '_getv0: B-norm of initial / restarted starting vector') - end if - if (msglvl .gt. 2) then - call igraphdvout (logfil, n, resid, ndigit, - & '_getv0: initial / restarted starting vector') - end if - ido = 99 -c - call igraphsecond (t1) - tgetv0 = tgetv0 + (t1 - t0) -c - 9000 continue - return -c -c %---------------% -c | End of igraphdgetv0 | -c %---------------% -c - end diff --git a/src/rigraph/vendor/arpack/dlaqrb.f b/src/rigraph/vendor/arpack/dlaqrb.f deleted file mode 100644 index 5fcefec..0000000 --- a/src/rigraph/vendor/arpack/dlaqrb.f +++ /dev/null @@ -1,521 +0,0 @@ -c----------------------------------------------------------------------- -c\BeginDoc -c -c\Name: igraphdlaqrb -c -c\Description: -c Compute the eigenvalues and the Schur decomposition of an upper -c Hessenberg submatrix in rows and columns ILO to IHI. Only the -c last component of the Schur vectors are computed. -c -c This is mostly a modification of the LAPACK routine dlahqr. -c -c\Usage: -c call igraphdlaqrb -c ( WANTT, N, ILO, IHI, H, LDH, WR, WI, Z, INFO ) -c -c\Arguments -c WANTT Logical variable. (INPUT) -c = .TRUE. : the full Schur form T is required; -c = .FALSE.: only eigenvalues are required. -c -c N Integer. (INPUT) -c The order of the matrix H. N >= 0. -c -c ILO Integer. (INPUT) -c IHI Integer. (INPUT) -c It is assumed that H is already upper quasi-triangular in -c rows and columns IHI+1:N, and that H(ILO,ILO-1) = 0 (unless -c ILO = 1). SLAQRB works primarily with the Hessenberg -c submatrix in rows and columns ILO to IHI, but applies -c transformations to all of H if WANTT is .TRUE.. -c 1 <= ILO <= max(1,IHI); IHI <= N. -c -c H Double precision array, dimension (LDH,N). (INPUT/OUTPUT) -c On entry, the upper Hessenberg matrix H. -c On exit, if WANTT is .TRUE., H is upper quasi-triangular in -c rows and columns ILO:IHI, with any 2-by-2 diagonal blocks in -c standard form. If WANTT is .FALSE., the contents of H are -c unspecified on exit. -c -c LDH Integer. (INPUT) -c The leading dimension of the array H. LDH >= max(1,N). -c -c WR Double precision array, dimension (N). (OUTPUT) -c WI Double precision array, dimension (N). (OUTPUT) -c The real and imaginary parts, respectively, of the computed -c eigenvalues ILO to IHI are stored in the corresponding -c elements of WR and WI. If two eigenvalues are computed as a -c complex conjugate pair, they are stored in consecutive -c elements of WR and WI, say the i-th and (i+1)th, with -c WI(i) > 0 and WI(i+1) < 0. If WANTT is .TRUE., the -c eigenvalues are stored in the same order as on the diagonal -c of the Schur form returned in H, with WR(i) = H(i,i), and, if -c H(i:i+1,i:i+1) is a 2-by-2 diagonal block, -c WI(i) = sqrt(H(i+1,i)*H(i,i+1)) and WI(i+1) = -WI(i). -c -c Z Double precision array, dimension (N). (OUTPUT) -c On exit Z contains the last components of the Schur vectors. -c -c INFO Integer. (OUPUT) -c = 0: successful exit -c > 0: SLAQRB failed to compute all the eigenvalues ILO to IHI -c in a total of 30*(IHI-ILO+1) iterations; if INFO = i, -c elements i+1:ihi of WR and WI contain those eigenvalues -c which have been successfully computed. -c -c\Remarks -c 1. None. -c -c----------------------------------------------------------------------- -c -c\BeginLib -c -c\Local variables: -c xxxxxx real -c -c\Routines called: -c dlabad LAPACK routine that computes machine constants. -c dlamch LAPACK routine that determines machine constants. -c dlanhs LAPACK routine that computes various norms of a matrix. -c dlanv2 LAPACK routine that computes the Schur factorization of -c 2 by 2 nonsymmetric matrix in standard form. -c dlarfg LAPACK Householder reflection construction routine. -c dcopy Level 1 BLAS that copies one vector to another. -c drot Level 1 BLAS that applies a rotation to a 2 by 2 matrix. - -c -c\Author -c Danny Sorensen Phuong Vu -c Richard Lehoucq CRPC / Rice University -c Dept. of Computational & Houston, Texas -c Applied Mathematics -c Rice University -c Houston, Texas -c -c\Revision history: -c xx/xx/92: Version ' 2.4' -c Modified from the LAPACK routine dlahqr so that only the -c last component of the Schur vectors are computed. -c -c\SCCS Information: @(#) -c FILE: laqrb.F SID: 2.2 DATE OF SID: 8/27/96 RELEASE: 2 -c -c\Remarks -c 1. None -c -c\EndLib -c -c----------------------------------------------------------------------- -c - subroutine igraphdlaqrb ( wantt, n, ilo, ihi, h, ldh, wr, wi, - & z, info ) -c -c %------------------% -c | Scalar Arguments | -c %------------------% -c - logical wantt - integer ihi, ilo, info, ldh, n -c -c %-----------------% -c | Array Arguments | -c %-----------------% -c - Double precision - & h( ldh, * ), wi( * ), wr( * ), z( * ) -c -c %------------% -c | Parameters | -c %------------% -c - Double precision - & zero, one, dat1, dat2 - parameter (zero = 0.0D+0, one = 1.0D+0, dat1 = 7.5D-1, - & dat2 = -4.375D-1) -c -c %------------------------% -c | Local Scalars & Arrays | -c %------------------------% -c - integer i, i1, i2, itn, its, j, k, l, m, nh, nr - Double precision - & cs, h00, h10, h11, h12, h21, h22, h33, h33s, - & h43h34, h44, h44s, ovfl, s, smlnum, sn, sum, - & t1, t2, t3, tst1, ulp, unfl, v1, v2, v3 - Double precision - & v( 3 ), work( 1 ) -c -c %--------------------% -c | External Functions | -c %--------------------% -c - Double precision - & dlamch, dlanhs - external dlamch, dlanhs -c -c %----------------------% -c | External Subroutines | -c %----------------------% -c - external dcopy, dlabad, dlanv2, dlarfg, drot -c -c %-----------------------% -c | Executable Statements | -c %-----------------------% -c - info = 0 -c -c %--------------------------% -c | Quick return if possible | -c %--------------------------% -c - if( n.eq.0 ) - & return - if( ilo.eq.ihi ) then - wr( ilo ) = h( ilo, ilo ) - wi( ilo ) = zero - return - end if -c -c %---------------------------------------------% -c | Initialize the vector of last components of | -c | the Schur vectors for accumulation. | -c %---------------------------------------------% -c - do 5 j = 1, n-1 - z(j) = zero - 5 continue - z(n) = one -c - nh = ihi - ilo + 1 -c -c %-------------------------------------------------------------% -c | Set machine-dependent constants for the stopping criterion. | -c | If norm(H) <= sqrt(OVFL), overflow should not occur. | -c %-------------------------------------------------------------% -c - unfl = dlamch( 'safe minimum' ) - ovfl = one / unfl - call dlabad( unfl, ovfl ) - ulp = dlamch( 'precision' ) - smlnum = unfl*( nh / ulp ) -c -c %---------------------------------------------------------------% -c | I1 and I2 are the indices of the first row and last column | -c | of H to which transformations must be applied. If eigenvalues | -c | only are computed, I1 and I2 are set inside the main loop. | -c | Zero out H(J+2,J) = ZERO for J=1:N if WANTT = .TRUE. | -c | else H(J+2,J) for J=ILO:IHI-ILO-1 if WANTT = .FALSE. | -c %---------------------------------------------------------------% -c - if( wantt ) then - i1 = 1 - i2 = n - do 8 i=1,i2-2 - h(i1+i+1,i) = zero - 8 continue - else - do 9 i=1, ihi-ilo-1 - h(ilo+i+1,ilo+i-1) = zero - 9 continue - end if -c -c %---------------------------------------------------% -c | ITN is the total number of QR iterations allowed. | -c %---------------------------------------------------% -c - itn = 30*nh -c -c ------------------------------------------------------------------ -c The main loop begins here. I is the loop index and decreases from -c IHI to ILO in steps of 1 or 2. Each iteration of the loop works -c with the active submatrix in rows and columns L to I. -c Eigenvalues I+1 to IHI have already converged. Either L = ILO or -c H(L,L-1) is negligible so that the matrix splits. -c ------------------------------------------------------------------ -c - i = ihi - 10 continue - l = ilo - if( i.lt.ilo ) - & go to 150 - -c %--------------------------------------------------------------% -c | Perform QR iterations on rows and columns ILO to I until a | -c | submatrix of order 1 or 2 splits off at the bottom because a | -c | subdiagonal element has become negligible. | -c %--------------------------------------------------------------% - - do 130 its = 0, itn -c -c %----------------------------------------------% -c | Look for a single small subdiagonal element. | -c %----------------------------------------------% -c - do 20 k = i, l + 1, -1 - tst1 = abs( h( k-1, k-1 ) ) + abs( h( k, k ) ) - if( tst1.eq.zero ) - & tst1 = dlanhs( '1', i-l+1, h( l, l ), ldh, work ) - if( abs( h( k, k-1 ) ).le.max( ulp*tst1, smlnum ) ) - & go to 30 - 20 continue - 30 continue - l = k - if( l.gt.ilo ) then -c -c %------------------------% -c | H(L,L-1) is negligible | -c %------------------------% -c - h( l, l-1 ) = zero - end if -c -c %-------------------------------------------------------------% -c | Exit from loop if a submatrix of order 1 or 2 has split off | -c %-------------------------------------------------------------% -c - if( l.ge.i-1 ) - & go to 140 -c -c %---------------------------------------------------------% -c | Now the active submatrix is in rows and columns L to I. | -c | If eigenvalues only are being computed, only the active | -c | submatrix need be transformed. | -c %---------------------------------------------------------% -c - if( .not.wantt ) then - i1 = l - i2 = i - end if -c - if( its.eq.10 .or. its.eq.20 ) then -c -c %-------------------% -c | Exceptional shift | -c %-------------------% -c - s = abs( h( i, i-1 ) ) + abs( h( i-1, i-2 ) ) - h44 = dat1*s - h33 = h44 - h43h34 = dat2*s*s -c - else -c -c %-----------------------------------------% -c | Prepare to use Wilkinson's double shift | -c %-----------------------------------------% -c - h44 = h( i, i ) - h33 = h( i-1, i-1 ) - h43h34 = h( i, i-1 )*h( i-1, i ) - end if -c -c %-----------------------------------------------------% -c | Look for two consecutive small subdiagonal elements | -c %-----------------------------------------------------% -c - do 40 m = i - 2, l, -1 -c -c %---------------------------------------------------------% -c | Determine the effect of starting the double-shift QR | -c | iteration at row M, and see if this would make H(M,M-1) | -c | negligible. | -c %---------------------------------------------------------% -c - h11 = h( m, m ) - h22 = h( m+1, m+1 ) - h21 = h( m+1, m ) - h12 = h( m, m+1 ) - h44s = h44 - h11 - h33s = h33 - h11 - v1 = ( h33s*h44s-h43h34 ) / h21 + h12 - v2 = h22 - h11 - h33s - h44s - v3 = h( m+2, m+1 ) - s = abs( v1 ) + abs( v2 ) + abs( v3 ) - v1 = v1 / s - v2 = v2 / s - v3 = v3 / s - v( 1 ) = v1 - v( 2 ) = v2 - v( 3 ) = v3 - if( m.eq.l ) - & go to 50 - h00 = h( m-1, m-1 ) - h10 = h( m, m-1 ) - tst1 = abs( v1 )*( abs( h00 )+abs( h11 )+abs( h22 ) ) - if( abs( h10 )*( abs( v2 )+abs( v3 ) ).le.ulp*tst1 ) - & go to 50 - 40 continue - 50 continue -c -c %----------------------% -c | Double-shift QR step | -c %----------------------% -c - do 120 k = m, i - 1 -c -c ------------------------------------------------------------ -c The first iteration of this loop determines a reflection G -c from the vector V and applies it from left and right to H, -c thus creating a nonzero bulge below the subdiagonal. -c -c Each subsequent iteration determines a reflection G to -c restore the Hessenberg form in the (K-1)th column, and thus -c chases the bulge one step toward the bottom of the active -c submatrix. NR is the order of G. -c ------------------------------------------------------------ -c - nr = min( 3, i-k+1 ) - if( k.gt.m ) - & call dcopy( nr, h( k, k-1 ), 1, v, 1 ) - call dlarfg( nr, v( 1 ), v( 2 ), 1, t1 ) - if( k.gt.m ) then - h( k, k-1 ) = v( 1 ) - h( k+1, k-1 ) = zero - if( k.lt.i-1 ) - & h( k+2, k-1 ) = zero - else if( m.gt.l ) then - h( k, k-1 ) = -h( k, k-1 ) - end if - v2 = v( 2 ) - t2 = t1*v2 - if( nr.eq.3 ) then - v3 = v( 3 ) - t3 = t1*v3 -c -c %------------------------------------------------% -c | Apply G from the left to transform the rows of | -c | the matrix in columns K to I2. | -c %------------------------------------------------% -c - do 60 j = k, i2 - sum = h( k, j ) + v2*h( k+1, j ) + v3*h( k+2, j ) - h( k, j ) = h( k, j ) - sum*t1 - h( k+1, j ) = h( k+1, j ) - sum*t2 - h( k+2, j ) = h( k+2, j ) - sum*t3 - 60 continue -c -c %----------------------------------------------------% -c | Apply G from the right to transform the columns of | -c | the matrix in rows I1 to min(K+3,I). | -c %----------------------------------------------------% -c - do 70 j = i1, min( k+3, i ) - sum = h( j, k ) + v2*h( j, k+1 ) + v3*h( j, k+2 ) - h( j, k ) = h( j, k ) - sum*t1 - h( j, k+1 ) = h( j, k+1 ) - sum*t2 - h( j, k+2 ) = h( j, k+2 ) - sum*t3 - 70 continue -c -c %----------------------------------% -c | Accumulate transformations for Z | -c %----------------------------------% -c - sum = z( k ) + v2*z( k+1 ) + v3*z( k+2 ) - z( k ) = z( k ) - sum*t1 - z( k+1 ) = z( k+1 ) - sum*t2 - z( k+2 ) = z( k+2 ) - sum*t3 - - else if( nr.eq.2 ) then -c -c %------------------------------------------------% -c | Apply G from the left to transform the rows of | -c | the matrix in columns K to I2. | -c %------------------------------------------------% -c - do 90 j = k, i2 - sum = h( k, j ) + v2*h( k+1, j ) - h( k, j ) = h( k, j ) - sum*t1 - h( k+1, j ) = h( k+1, j ) - sum*t2 - 90 continue -c -c %----------------------------------------------------% -c | Apply G from the right to transform the columns of | -c | the matrix in rows I1 to min(K+3,I). | -c %----------------------------------------------------% -c - do 100 j = i1, i - sum = h( j, k ) + v2*h( j, k+1 ) - h( j, k ) = h( j, k ) - sum*t1 - h( j, k+1 ) = h( j, k+1 ) - sum*t2 - 100 continue -c -c %----------------------------------% -c | Accumulate transformations for Z | -c %----------------------------------% -c - sum = z( k ) + v2*z( k+1 ) - z( k ) = z( k ) - sum*t1 - z( k+1 ) = z( k+1 ) - sum*t2 - end if - 120 continue - - 130 continue -c -c %-------------------------------------------------------% -c | Failure to converge in remaining number of iterations | -c %-------------------------------------------------------% -c - info = i - return - - 140 continue - - if( l.eq.i ) then -c -c %------------------------------------------------------% -c | H(I,I-1) is negligible: one eigenvalue has converged | -c %------------------------------------------------------% -c - wr( i ) = h( i, i ) - wi( i ) = zero - - else if( l.eq.i-1 ) then -c -c %--------------------------------------------------------% -c | H(I-1,I-2) is negligible; | -c | a pair of eigenvalues have converged. | -c | | -c | Transform the 2-by-2 submatrix to standard Schur form, | -c | and compute and store the eigenvalues. | -c %--------------------------------------------------------% -c - call dlanv2( h( i-1, i-1 ), h( i-1, i ), h( i, i-1 ), - & h( i, i ), wr( i-1 ), wi( i-1 ), wr( i ), wi( i ), - & cs, sn ) - - if( wantt ) then -c -c %-----------------------------------------------------% -c | Apply the transformation to the rest of H and to Z, | -c | as required. | -c %-----------------------------------------------------% -c - if( i2.gt.i ) - & call drot( i2-i, h( i-1, i+1 ), ldh, h( i, i+1 ), ldh, - & cs, sn ) - call drot( i-i1-1, h( i1, i-1 ), 1, h( i1, i ), 1, cs, sn ) - sum = cs*z( i-1 ) + sn*z( i ) - z( i ) = cs*z( i ) - sn*z( i-1 ) - z( i-1 ) = sum - end if - end if -c -c %---------------------------------------------------------% -c | Decrement number of remaining iterations, and return to | -c | start of the main loop with new value of I. | -c %---------------------------------------------------------% -c - itn = itn - its - i = l - 1 - go to 10 - - 150 continue - return -c -c %---------------% -c | End of igraphdlaqrb | -c %---------------% -c - end diff --git a/src/rigraph/vendor/arpack/dmout.f b/src/rigraph/vendor/arpack/dmout.f deleted file mode 100644 index 6204d64..0000000 --- a/src/rigraph/vendor/arpack/dmout.f +++ /dev/null @@ -1,167 +0,0 @@ -*----------------------------------------------------------------------- -* 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 IGRAPHDMOUT( LOUT, M, N, A, LDA, IDIGIT, IFMT ) -* ... -* ... SPECIFICATIONS FOR ARGUMENTS -* ... -* ... SPECIFICATIONS FOR LOCAL VARIABLES -* .. Scalar Arguments .. - CHARACTER*( * ) IFMT - INTEGER IDIGIT, LDA, LOUT, M, N -* .. -* .. Array Arguments .. - DOUBLE PRECISION A( LDA, * ) -* .. -* .. Local Scalars .. - CHARACTER*80 LINE - INTEGER I, J, K1, K2, LLL, NDIGIT -* .. -* .. Local Arrays .. - CHARACTER ICOL( 3 ) -* .. -* .. Intrinsic Functions .. - INTRINSIC LEN, MIN, MIN0 -* .. -* .. Data statements .. - DATA ICOL( 1 ), ICOL( 2 ), ICOL( 3 ) / 'C', 'o', - $ 'l' / -* .. -* .. Executable Statements .. -* ... -* ... FIRST EXECUTABLE STATEMENT -* -c$$$ LLL = MIN( LEN( IFMT ), 80 ) -c$$$ DO 10 I = 1, LLL -c$$$ LINE( I: I ) = '-' -c$$$ 10 CONTINUE -c$$$* -c$$$ DO 20 I = LLL + 1, 80 -c$$$ LINE( I: I ) = ' ' -c$$$ 20 CONTINUE -c$$$* -c$$$ WRITE( LOUT, FMT = 9999 )IFMT, LINE( 1: LLL ) -c$$$ 9999 FORMAT( / 1X, A, / 1X, A ) -c$$$* -c$$$ IF( M.LE.0 .OR. N.LE.0 .OR. LDA.LE.0 ) -c$$$ $ RETURN -c$$$ NDIGIT = IDIGIT -c$$$ IF( IDIGIT.EQ.0 ) -c$$$ $ NDIGIT = 4 -c$$$* -c$$$*======================================================================= -c$$$* CODE FOR OUTPUT USING 72 COLUMNS FORMAT -c$$$*======================================================================= -c$$$* -c$$$ IF( IDIGIT.LT.0 ) THEN -c$$$ NDIGIT = -IDIGIT -c$$$ IF( NDIGIT.LE.4 ) THEN -c$$$ DO 40 K1 = 1, N, 5 -c$$$ K2 = MIN0( N, K1+4 ) -c$$$ WRITE( LOUT, FMT = 9998 )( ICOL, I, I = K1, K2 ) -c$$$ DO 30 I = 1, M -c$$$ WRITE( LOUT, FMT = 9994 )I, ( A( I, J ), J = K1, K2 ) -c$$$ 30 CONTINUE -c$$$ 40 CONTINUE -c$$$* -c$$$ ELSE IF( NDIGIT.LE.6 ) THEN -c$$$ DO 60 K1 = 1, N, 4 -c$$$ K2 = MIN0( N, K1+3 ) -c$$$ WRITE( LOUT, FMT = 9997 )( ICOL, I, I = K1, K2 ) -c$$$ DO 50 I = 1, M -c$$$ WRITE( LOUT, FMT = 9993 )I, ( A( I, J ), J = K1, K2 ) -c$$$ 50 CONTINUE -c$$$ 60 CONTINUE -c$$$* -c$$$ ELSE IF( NDIGIT.LE.10 ) THEN -c$$$ DO 80 K1 = 1, N, 3 -c$$$ K2 = MIN0( N, K1+2 ) -c$$$ WRITE( LOUT, FMT = 9996 )( ICOL, I, I = K1, K2 ) -c$$$ DO 70 I = 1, M -c$$$ WRITE( LOUT, FMT = 9992 )I, ( A( I, J ), J = K1, K2 ) -c$$$ 70 CONTINUE -c$$$ 80 CONTINUE -c$$$* -c$$$ ELSE -c$$$ DO 100 K1 = 1, N, 2 -c$$$ K2 = MIN0( N, K1+1 ) -c$$$ WRITE( LOUT, FMT = 9995 )( ICOL, I, I = K1, K2 ) -c$$$ DO 90 I = 1, M -c$$$ WRITE( LOUT, FMT = 9991 )I, ( A( I, J ), J = K1, K2 ) -c$$$ 90 CONTINUE -c$$$ 100 CONTINUE -c$$$ END IF -c$$$* -c$$$*======================================================================= -c$$$* CODE FOR OUTPUT USING 132 COLUMNS FORMAT -c$$$*======================================================================= -c$$$* -c$$$ ELSE -c$$$ IF( NDIGIT.LE.4 ) THEN -c$$$ DO 120 K1 = 1, N, 10 -c$$$ K2 = MIN0( N, K1+9 ) -c$$$ WRITE( LOUT, FMT = 9998 )( ICOL, I, I = K1, K2 ) -c$$$ DO 110 I = 1, M -c$$$ WRITE( LOUT, FMT = 9994 )I, ( A( I, J ), J = K1, K2 ) -c$$$ 110 CONTINUE -c$$$ 120 CONTINUE -c$$$* -c$$$ ELSE IF( NDIGIT.LE.6 ) THEN -c$$$ DO 140 K1 = 1, N, 8 -c$$$ K2 = MIN0( N, K1+7 ) -c$$$ WRITE( LOUT, FMT = 9997 )( ICOL, I, I = K1, K2 ) -c$$$ DO 130 I = 1, M -c$$$ WRITE( LOUT, FMT = 9993 )I, ( A( I, J ), J = K1, K2 ) -c$$$ 130 CONTINUE -c$$$ 140 CONTINUE -c$$$* -c$$$ ELSE IF( NDIGIT.LE.10 ) THEN -c$$$ DO 160 K1 = 1, N, 6 -c$$$ K2 = MIN0( N, K1+5 ) -c$$$ WRITE( LOUT, FMT = 9996 )( ICOL, I, I = K1, K2 ) -c$$$ DO 150 I = 1, M -c$$$ WRITE( LOUT, FMT = 9992 )I, ( A( I, J ), J = K1, K2 ) -c$$$ 150 CONTINUE -c$$$ 160 CONTINUE -c$$$* -c$$$ ELSE -c$$$ DO 180 K1 = 1, N, 5 -c$$$ K2 = MIN0( N, K1+4 ) -c$$$ WRITE( LOUT, FMT = 9995 )( ICOL, I, I = K1, K2 ) -c$$$ DO 170 I = 1, M -c$$$ WRITE( LOUT, FMT = 9991 )I, ( A( I, J ), J = K1, K2 ) -c$$$ 170 CONTINUE -c$$$ 180 CONTINUE -c$$$ END IF -c$$$ END IF -c$$$ WRITE( LOUT, FMT = 9990 ) -c$$$* -c$$$ 9998 FORMAT( 10X, 10( 4X, 3A1, I4, 1X ) ) -c$$$ 9997 FORMAT( 10X, 8( 5X, 3A1, I4, 2X ) ) -c$$$ 9996 FORMAT( 10X, 6( 7X, 3A1, I4, 4X ) ) -c$$$ 9995 FORMAT( 10X, 5( 9X, 3A1, I4, 6X ) ) -c$$$ 9994 FORMAT( 1X, ' Row', I4, ':', 1X, 1P, 10D12.3 ) -c$$$ 9993 FORMAT( 1X, ' Row', I4, ':', 1X, 1P, 8D14.5 ) -c$$$ 9992 FORMAT( 1X, ' Row', I4, ':', 1X, 1P, 6D18.9 ) -c$$$ 9991 FORMAT( 1X, ' Row', I4, ':', 1X, 1P, 5D22.13 ) -c$$$ 9990 FORMAT( 1X, ' ' ) -* - RETURN - END diff --git a/src/rigraph/vendor/arpack/dnaitr.f b/src/rigraph/vendor/arpack/dnaitr.f deleted file mode 100644 index 8ec2569..0000000 --- a/src/rigraph/vendor/arpack/dnaitr.f +++ /dev/null @@ -1,840 +0,0 @@ -c----------------------------------------------------------------------- -c\BeginDoc -c -c\Name: igraphdnaitr -c -c\Description: -c Reverse communication interface for applying NP additional steps to -c a K step nonsymmetric Arnoldi factorization. -c -c Input: OP*V_{k} - V_{k}*H = r_{k}*e_{k}^T -c -c with (V_{k}^T)*B*V_{k} = I, (V_{k}^T)*B*r_{k} = 0. -c -c Output: OP*V_{k+p} - V_{k+p}*H = r_{k+p}*e_{k+p}^T -c -c with (V_{k+p}^T)*B*V_{k+p} = I, (V_{k+p}^T)*B*r_{k+p} = 0. -c -c where OP and B are as in igraphdnaupd. The B-norm of r_{k+p} is also -c computed and returned. -c -c\Usage: -c call igraphdnaitr -c ( IDO, BMAT, N, K, NP, NB, RESID, RNORM, V, LDV, H, LDH, -c IPNTR, WORKD, INFO ) -c -c\Arguments -c IDO Integer. (INPUT/OUTPUT) -c Reverse communication flag. -c ------------------------------------------------------------- -c IDO = 0: first call to the reverse communication interface -c IDO = -1: compute Y = OP * X where -c IPNTR(1) is the pointer into WORK for X, -c IPNTR(2) is the pointer into WORK for Y. -c This is for the restart phase to force the new -c starting vector into the range of OP. -c IDO = 1: compute Y = OP * X where -c IPNTR(1) is the pointer into WORK for X, -c IPNTR(2) is the pointer into WORK for Y, -c IPNTR(3) is the pointer into WORK for B * X. -c IDO = 2: compute Y = B * X where -c IPNTR(1) is the pointer into WORK for X, -c IPNTR(2) is the pointer into WORK for Y. -c IDO = 99: done -c ------------------------------------------------------------- -c When the routine is used in the "shift-and-invert" mode, the -c vector B * Q is already available and do not need to be -c recompute in forming OP * Q. -c -c BMAT Character*1. (INPUT) -c BMAT specifies the type of the matrix B that defines the -c semi-inner product for the operator OP. See igraphdnaupd. -c B = 'I' -> standard eigenvalue problem A*x = lambda*x -c B = 'G' -> generalized eigenvalue problem A*x = lambda*M**x -c -c N Integer. (INPUT) -c Dimension of the eigenproblem. -c -c K Integer. (INPUT) -c Current size of V and H. -c -c NP Integer. (INPUT) -c Number of additional Arnoldi steps to take. -c -c NB Integer. (INPUT) -c Blocksize to be used in the recurrence. -c Only work for NB = 1 right now. The goal is to have a -c program that implement both the block and non-block method. -c -c RESID Double precision array of length N. (INPUT/OUTPUT) -c On INPUT: RESID contains the residual vector r_{k}. -c On OUTPUT: RESID contains the residual vector r_{k+p}. -c -c RNORM Double precision scalar. (INPUT/OUTPUT) -c B-norm of the starting residual on input. -c B-norm of the updated residual r_{k+p} on output. -c -c V Double precision N by K+NP array. (INPUT/OUTPUT) -c On INPUT: V contains the Arnoldi vectors in the first K -c columns. -c On OUTPUT: V contains the new NP Arnoldi vectors in the next -c NP columns. The first K columns are unchanged. -c -c LDV Integer. (INPUT) -c Leading dimension of V exactly as declared in the calling -c program. -c -c H Double precision (K+NP) by (K+NP) array. (INPUT/OUTPUT) -c H is used to store the generated upper Hessenberg matrix. -c -c LDH Integer. (INPUT) -c Leading dimension of H exactly as declared in the calling -c program. -c -c IPNTR Integer array of length 3. (OUTPUT) -c Pointer to mark the starting locations in the WORK for -c vectors used by the Arnoldi iteration. -c ------------------------------------------------------------- -c IPNTR(1): pointer to the current operand vector X. -c IPNTR(2): pointer to the current result vector Y. -c IPNTR(3): pointer to the vector B * X when used in the -c shift-and-invert mode. X is the current operand. -c ------------------------------------------------------------- -c -c WORKD Double precision work array of length 3*N. (REVERSE COMMUNICATION) -c Distributed array to be used in the basic Arnoldi iteration -c for reverse communication. The calling program should not -c use WORKD as temporary workspace during the iteration !!!!!! -c On input, WORKD(1:N) = B*RESID and is used to save some -c computation at the first step. -c -c INFO Integer. (OUTPUT) -c = 0: Normal exit. -c > 0: Size of the spanning invariant subspace of OP found. -c -c\EndDoc -c -c----------------------------------------------------------------------- -c -c\BeginLib -c -c\Local variables: -c xxxxxx real -c -c\References: -c 1. D.C. Sorensen, "Implicit Application of Polynomial Filters in -c a k-Step Arnoldi Method", SIAM J. Matr. Anal. Apps., 13 (1992), -c pp 357-385. -c 2. R.B. Lehoucq, "Analysis and Implementation of an Implicitly -c Restarted Arnoldi Iteration", Rice University Technical Report -c TR95-13, Department of Computational and Applied Mathematics. -c -c\Routines called: -c igraphdgetv0 ARPACK routine to generate the initial vector. -c igraphivout ARPACK utility routine that prints integers. -c igraphsecond ARPACK utility routine for timing. -c igraphdmout ARPACK utility routine that prints matrices -c igraphdvout ARPACK utility routine that prints vectors. -c dlabad LAPACK routine that computes machine constants. -c dlamch LAPACK routine that determines machine constants. -c dlascl LAPACK routine for careful scaling of a matrix. -c dlanhs LAPACK routine that computes various norms of a matrix. -c dgemv Level 2 BLAS routine for matrix vector multiplication. -c daxpy Level 1 BLAS that computes a vector triad. -c dscal Level 1 BLAS that scales a vector. -c dcopy Level 1 BLAS that copies one vector to another . -c ddot Level 1 BLAS that computes the scalar product of two vectors. -c dnrm2 Level 1 BLAS that computes the norm of a vector. -c -c\Author -c Danny Sorensen Phuong Vu -c Richard Lehoucq CRPC / Rice University -c Dept. of Computational & Houston, Texas -c Applied Mathematics -c Rice University -c Houston, Texas -c -c\Revision history: -c xx/xx/92: Version ' 2.4' -c -c\SCCS Information: @(#) -c FILE: naitr.F SID: 2.4 DATE OF SID: 8/27/96 RELEASE: 2 -c -c\Remarks -c The algorithm implemented is: -c -c restart = .false. -c Given V_{k} = [v_{1}, ..., v_{k}], r_{k}; -c r_{k} contains the initial residual vector even for k = 0; -c Also assume that rnorm = || B*r_{k} || and B*r_{k} are already -c computed by the calling program. -c -c betaj = rnorm ; p_{k+1} = B*r_{k} ; -c For j = k+1, ..., k+np Do -c 1) if ( betaj < tol ) stop or restart depending on j. -c ( At present tol is zero ) -c if ( restart ) generate a new starting vector. -c 2) v_{j} = r(j-1)/betaj; V_{j} = [V_{j-1}, v_{j}]; -c p_{j} = p_{j}/betaj -c 3) r_{j} = OP*v_{j} where OP is defined as in igraphdnaupd -c For shift-invert mode p_{j} = B*v_{j} is already available. -c wnorm = || OP*v_{j} || -c 4) Compute the j-th step residual vector. -c w_{j} = V_{j}^T * B * OP * v_{j} -c r_{j} = OP*v_{j} - V_{j} * w_{j} -c H(:,j) = w_{j}; -c H(j,j-1) = rnorm -c rnorm = || r_(j) || -c If (rnorm > 0.717*wnorm) accept step and go back to 1) -c 5) Re-orthogonalization step: -c s = V_{j}'*B*r_{j} -c r_{j} = r_{j} - V_{j}*s; rnorm1 = || r_{j} || -c alphaj = alphaj + s_{j}; -c 6) Iterative refinement step: -c If (rnorm1 > 0.717*rnorm) then -c rnorm = rnorm1 -c accept step and go back to 1) -c Else -c rnorm = rnorm1 -c If this is the first time in step 6), go to 5) -c Else r_{j} lies in the span of V_{j} numerically. -c Set r_{j} = 0 and rnorm = 0; go to 1) -c EndIf -c End Do -c -c\EndLib -c -c----------------------------------------------------------------------- -c - subroutine igraphdnaitr - & (ido, bmat, n, k, np, nb, resid, rnorm, v, ldv, h, ldh, - & ipntr, workd, info) -c -c %----------------------------------------------------% -c | Include files for debugging and timing information | -c %----------------------------------------------------% -c - include 'debug.h' - include 'stat.h' -c -c %------------------% -c | Scalar Arguments | -c %------------------% -c - character bmat*1 - integer ido, info, k, ldh, ldv, n, nb, np - Double precision - & rnorm -c -c %-----------------% -c | Array Arguments | -c %-----------------% -c - integer ipntr(3) - Double precision - & h(ldh,k+np), resid(n), v(ldv,k+np), workd(3*n) -c -c %------------% -c | Parameters | -c %------------% -c - Double precision - & one, zero - parameter (one = 1.0D+0, zero = 0.0D+0) -c -c %---------------% -c | Local Scalars | -c %---------------% -c - logical first, orth1, orth2, rstart, step3, step4 - integer ierr, i, infol, ipj, irj, ivj, iter, itry, j, msglvl, - & jj - Double precision - & betaj, ovfl, temp1, rnorm1, smlnum, tst1, ulp, unfl, - & wnorm - save first, orth1, orth2, rstart, step3, step4, - & ierr, ipj, irj, ivj, iter, itry, j, msglvl, ovfl, - & betaj, rnorm1, smlnum, ulp, unfl, wnorm -c -c %-----------------------% -c | Local Array Arguments | -c %-----------------------% -c - Double precision - & xtemp(2) -c -c %----------------------% -c | External Subroutines | -c %----------------------% -c - external daxpy, dcopy, dscal, dgemv, igraphdgetv0, dlabad, - & igraphdvout, igraphdmout, igraphivout, igraphsecond -c -c %--------------------% -c | External Functions | -c %--------------------% -c - Double precision - & ddot, dnrm2, dlanhs, dlamch - external ddot, dnrm2, dlanhs, dlamch -c -c %---------------------% -c | Intrinsic Functions | -c %---------------------% -c - intrinsic abs, sqrt -c -c %-----------------% -c | Data statements | -c %-----------------% -c - data first / .true. / -c -c %-----------------------% -c | Executable Statements | -c %-----------------------% -c - if (first) then -c -c %-----------------------------------------% -c | Set machine-dependent constants for the | -c | the splitting and deflation criterion. | -c | If norm(H) <= sqrt(OVFL), | -c | overflow should not occur. | -c | REFERENCE: LAPACK subroutine dlahqr | -c %-----------------------------------------% -c - unfl = dlamch( 'safe minimum' ) - ovfl = one / unfl - call dlabad( unfl, ovfl ) - ulp = dlamch( 'precision' ) - smlnum = unfl*( n / ulp ) - first = .false. - end if -c - if (ido .eq. 0) then -c -c %-------------------------------% -c | Initialize timing statistics | -c | & message level for debugging | -c %-------------------------------% -c - call igraphsecond (t0) - msglvl = mnaitr -c -c %------------------------------% -c | Initial call to this routine | -c %------------------------------% -c - info = 0 - step3 = .false. - step4 = .false. - rstart = .false. - orth1 = .false. - orth2 = .false. - j = k + 1 - ipj = 1 - irj = ipj + n - ivj = irj + n - end if -c -c %-------------------------------------------------% -c | When in reverse communication mode one of: | -c | STEP3, STEP4, ORTH1, ORTH2, RSTART | -c | will be .true. when .... | -c | STEP3: return from computing OP*v_{j}. | -c | STEP4: return from computing B-norm of OP*v_{j} | -c | ORTH1: return from computing B-norm of r_{j+1} | -c | ORTH2: return from computing B-norm of | -c | correction to the residual vector. | -c | RSTART: return from OP computations needed by | -c | igraphdgetv0. | -c %-------------------------------------------------% -c - if (step3) go to 50 - if (step4) go to 60 - if (orth1) go to 70 - if (orth2) go to 90 - if (rstart) go to 30 -c -c %-----------------------------% -c | Else this is the first step | -c %-----------------------------% -c -c %--------------------------------------------------------------% -c | | -c | A R N O L D I I T E R A T I O N L O O P | -c | | -c | Note: B*r_{j-1} is already in WORKD(1:N)=WORKD(IPJ:IPJ+N-1) | -c %--------------------------------------------------------------% - - 1000 continue -c - if (msglvl .gt. 1) then - call igraphivout (logfil, 1, [j], ndigit, - & '_naitr: generating Arnoldi vector number') - call igraphdvout (logfil, 1, [rnorm], ndigit, - & '_naitr: B-norm of the current residual is') - end if -c -c %---------------------------------------------------% -c | STEP 1: Check if the B norm of j-th residual | -c | vector is zero. Equivalent to determing whether | -c | an exact j-step Arnoldi factorization is present. | -c %---------------------------------------------------% -c - betaj = rnorm - if (rnorm .gt. zero) go to 40 -c -c %---------------------------------------------------% -c | Invariant subspace found, generate a new starting | -c | vector which is orthogonal to the current Arnoldi | -c | basis and continue the iteration. | -c %---------------------------------------------------% -c - if (msglvl .gt. 0) then - call igraphivout (logfil, 1, [j], ndigit, - & '_naitr: ****** RESTART AT STEP ******') - end if -c -c %---------------------------------------------% -c | ITRY is the loop variable that controls the | -c | maximum amount of times that a restart is | -c | attempted. NRSTRT is used by stat.h | -c %---------------------------------------------% -c - betaj = zero - nrstrt = nrstrt + 1 - itry = 1 - 20 continue - rstart = .true. - ido = 0 - 30 continue -c -c %--------------------------------------% -c | If in reverse communication mode and | -c | RSTART = .true. flow returns here. | -c %--------------------------------------% -c - call igraphdgetv0 (ido, bmat, itry, .false., n, j, v, ldv, - & resid, rnorm, ipntr, workd, ierr) - if (ido .ne. 99) go to 9000 - if (ierr .lt. 0) then - itry = itry + 1 - if (itry .le. 3) go to 20 -c -c %------------------------------------------------% -c | Give up after several restart attempts. | -c | Set INFO to the size of the invariant subspace | -c | which spans OP and exit. | -c %------------------------------------------------% -c - info = j - 1 - call igraphsecond (t1) - tnaitr = tnaitr + (t1 - t0) - ido = 99 - go to 9000 - end if -c - 40 continue -c -c %---------------------------------------------------------% -c | STEP 2: v_{j} = r_{j-1}/rnorm and p_{j} = p_{j}/rnorm | -c | Note that p_{j} = B*r_{j-1}. In order to avoid overflow | -c | when reciprocating a small RNORM, test against lower | -c | machine bound. | -c %---------------------------------------------------------% -c - call dcopy (n, resid, 1, v(1,j), 1) - if (rnorm .ge. unfl) then - temp1 = one / rnorm - call dscal (n, temp1, v(1,j), 1) - call dscal (n, temp1, workd(ipj), 1) - else -c -c %-----------------------------------------% -c | To scale both v_{j} and p_{j} carefully | -c | use LAPACK routine SLASCL | -c %-----------------------------------------% -c - call dlascl ('General', i, i, rnorm, one, n, 1, - & v(1,j), n, infol) - call dlascl ('General', i, i, rnorm, one, n, 1, - & workd(ipj), n, infol) - end if -c -c %------------------------------------------------------% -c | STEP 3: r_{j} = OP*v_{j}; Note that p_{j} = B*v_{j} | -c | Note that this is not quite yet r_{j}. See STEP 4 | -c %------------------------------------------------------% -c - step3 = .true. - nopx = nopx + 1 - call igraphsecond (t2) - call dcopy (n, v(1,j), 1, workd(ivj), 1) - ipntr(1) = ivj - ipntr(2) = irj - ipntr(3) = ipj - ido = 1 -c -c %-----------------------------------% -c | Exit in order to compute OP*v_{j} | -c %-----------------------------------% -c - go to 9000 - 50 continue -c -c %----------------------------------% -c | Back from reverse communication; | -c | WORKD(IRJ:IRJ+N-1) := OP*v_{j} | -c | if step3 = .true. | -c %----------------------------------% -c - call igraphsecond (t3) - tmvopx = tmvopx + (t3 - t2) - - step3 = .false. -c -c %------------------------------------------% -c | Put another copy of OP*v_{j} into RESID. | -c %------------------------------------------% -c - call dcopy (n, workd(irj), 1, resid, 1) -c -c %---------------------------------------% -c | STEP 4: Finish extending the Arnoldi | -c | factorization to length j. | -c %---------------------------------------% -c - call igraphsecond (t2) - if (bmat .eq. 'G') then - nbx = nbx + 1 - step4 = .true. - ipntr(1) = irj - ipntr(2) = ipj - ido = 2 -c -c %-------------------------------------% -c | Exit in order to compute B*OP*v_{j} | -c %-------------------------------------% -c - go to 9000 - else if (bmat .eq. 'I') then - call dcopy (n, resid, 1, workd(ipj), 1) - end if - 60 continue -c -c %----------------------------------% -c | Back from reverse communication; | -c | WORKD(IPJ:IPJ+N-1) := B*OP*v_{j} | -c | if step4 = .true. | -c %----------------------------------% -c - if (bmat .eq. 'G') then - call igraphsecond (t3) - tmvbx = tmvbx + (t3 - t2) - end if -c - step4 = .false. -c -c %-------------------------------------% -c | The following is needed for STEP 5. | -c | Compute the B-norm of OP*v_{j}. | -c %-------------------------------------% -c - if (bmat .eq. 'G') then - wnorm = ddot (n, resid, 1, workd(ipj), 1) - wnorm = sqrt(abs(wnorm)) - else if (bmat .eq. 'I') then - wnorm = dnrm2(n, resid, 1) - end if -c -c %-----------------------------------------% -c | Compute the j-th residual corresponding | -c | to the j step factorization. | -c | Use Classical Gram Schmidt and compute: | -c | w_{j} <- V_{j}^T * B * OP * v_{j} | -c | r_{j} <- OP*v_{j} - V_{j} * w_{j} | -c %-----------------------------------------% -c -c -c %------------------------------------------% -c | Compute the j Fourier coefficients w_{j} | -c | WORKD(IPJ:IPJ+N-1) contains B*OP*v_{j}. | -c %------------------------------------------% -c - call dgemv ('T', n, j, one, v, ldv, workd(ipj), 1, - & zero, h(1,j), 1) -c -c %--------------------------------------% -c | Orthogonalize r_{j} against V_{j}. | -c | RESID contains OP*v_{j}. See STEP 3. | -c %--------------------------------------% -c - call dgemv ('N', n, j, -one, v, ldv, h(1,j), 1, - & one, resid, 1) -c - if (j .gt. 1) h(j,j-1) = betaj -c - call igraphsecond (t4) -c - orth1 = .true. -c - call igraphsecond (t2) - if (bmat .eq. 'G') then - nbx = nbx + 1 - call dcopy (n, resid, 1, workd(irj), 1) - ipntr(1) = irj - ipntr(2) = ipj - ido = 2 -c -c %----------------------------------% -c | Exit in order to compute B*r_{j} | -c %----------------------------------% -c - go to 9000 - else if (bmat .eq. 'I') then - call dcopy (n, resid, 1, workd(ipj), 1) - end if - 70 continue -c -c %---------------------------------------------------% -c | Back from reverse communication if ORTH1 = .true. | -c | WORKD(IPJ:IPJ+N-1) := B*r_{j}. | -c %---------------------------------------------------% -c - if (bmat .eq. 'G') then - call igraphsecond (t3) - tmvbx = tmvbx + (t3 - t2) - end if -c - orth1 = .false. -c -c %------------------------------% -c | Compute the B-norm of r_{j}. | -c %------------------------------% -c - if (bmat .eq. 'G') then - rnorm = ddot (n, resid, 1, workd(ipj), 1) - rnorm = sqrt(abs(rnorm)) - else if (bmat .eq. 'I') then - rnorm = dnrm2(n, resid, 1) - end if -c -c %-----------------------------------------------------------% -c | STEP 5: Re-orthogonalization / Iterative refinement phase | -c | Maximum NITER_ITREF tries. | -c | | -c | s = V_{j}^T * B * r_{j} | -c | r_{j} = r_{j} - V_{j}*s | -c | alphaj = alphaj + s_{j} | -c | | -c | The stopping criteria used for iterative refinement is | -c | discussed in Parlett's book SEP, page 107 and in Gragg & | -c | Reichel ACM TOMS paper; Algorithm 686, Dec. 1990. | -c | Determine if we need to correct the residual. The goal is | -c | to enforce ||v(:,1:j)^T * r_{j}|| .le. eps * || r_{j} || | -c | The following test determines whether the sine of the | -c | angle between OP*x and the computed residual is less | -c | than or equal to 0.717. | -c %-----------------------------------------------------------% -c - if (rnorm .gt. 0.717*wnorm) go to 100 - iter = 0 - nrorth = nrorth + 1 -c -c %---------------------------------------------------% -c | Enter the Iterative refinement phase. If further | -c | refinement is necessary, loop back here. The loop | -c | variable is ITER. Perform a step of Classical | -c | Gram-Schmidt using all the Arnoldi vectors V_{j} | -c %---------------------------------------------------% -c - 80 continue -c - if (msglvl .gt. 2) then - xtemp(1) = wnorm - xtemp(2) = rnorm - call igraphdvout (logfil, 2, xtemp, ndigit, - & '_naitr: re-orthonalization; wnorm and rnorm are') - call igraphdvout (logfil, j, h(1,j), ndigit, - & '_naitr: j-th column of H') - end if -c -c %----------------------------------------------------% -c | Compute V_{j}^T * B * r_{j}. | -c | WORKD(IRJ:IRJ+J-1) = v(:,1:J)'*WORKD(IPJ:IPJ+N-1). | -c %----------------------------------------------------% -c - call dgemv ('T', n, j, one, v, ldv, workd(ipj), 1, - & zero, workd(irj), 1) -c -c %---------------------------------------------% -c | Compute the correction to the residual: | -c | r_{j} = r_{j} - V_{j} * WORKD(IRJ:IRJ+J-1). | -c | The correction to H is v(:,1:J)*H(1:J,1:J) | -c | + v(:,1:J)*WORKD(IRJ:IRJ+J-1)*e'_j. | -c %---------------------------------------------% -c - call dgemv ('N', n, j, -one, v, ldv, workd(irj), 1, - & one, resid, 1) - call daxpy (j, one, workd(irj), 1, h(1,j), 1) -c - orth2 = .true. - call igraphsecond (t2) - if (bmat .eq. 'G') then - nbx = nbx + 1 - call dcopy (n, resid, 1, workd(irj), 1) - ipntr(1) = irj - ipntr(2) = ipj - ido = 2 -c -c %-----------------------------------% -c | Exit in order to compute B*r_{j}. | -c | r_{j} is the corrected residual. | -c %-----------------------------------% -c - go to 9000 - else if (bmat .eq. 'I') then - call dcopy (n, resid, 1, workd(ipj), 1) - end if - 90 continue -c -c %---------------------------------------------------% -c | Back from reverse communication if ORTH2 = .true. | -c %---------------------------------------------------% -c - if (bmat .eq. 'G') then - call igraphsecond (t3) - tmvbx = tmvbx + (t3 - t2) - end if -c -c %-----------------------------------------------------% -c | Compute the B-norm of the corrected residual r_{j}. | -c %-----------------------------------------------------% -c - if (bmat .eq. 'G') then - rnorm1 = ddot (n, resid, 1, workd(ipj), 1) - rnorm1 = sqrt(abs(rnorm1)) - else if (bmat .eq. 'I') then - rnorm1 = dnrm2(n, resid, 1) - end if -c - if (msglvl .gt. 0 .and. iter .gt. 0) then - call igraphivout (logfil, 1, [j], ndigit, - & '_naitr: Iterative refinement for Arnoldi residual') - if (msglvl .gt. 2) then - xtemp(1) = rnorm - xtemp(2) = rnorm1 - call igraphdvout (logfil, 2, xtemp, ndigit, - & '_naitr: iterative refinement ; rnorm and rnorm1 are') - end if - end if -c -c %-----------------------------------------% -c | Determine if we need to perform another | -c | step of re-orthogonalization. | -c %-----------------------------------------% -c - if (rnorm1 .gt. 0.717*rnorm) then -c -c %---------------------------------------% -c | No need for further refinement. | -c | The cosine of the angle between the | -c | corrected residual vector and the old | -c | residual vector is greater than 0.717 | -c | In other words the corrected residual | -c | and the old residual vector share an | -c | angle of less than arcCOS(0.717) | -c %---------------------------------------% -c - rnorm = rnorm1 -c - else -c -c %-------------------------------------------% -c | Another step of iterative refinement step | -c | is required. NITREF is used by stat.h | -c %-------------------------------------------% -c - nitref = nitref + 1 - rnorm = rnorm1 - iter = iter + 1 - if (iter .le. 1) go to 80 -c -c %-------------------------------------------------% -c | Otherwise RESID is numerically in the span of V | -c %-------------------------------------------------% -c - do 95 jj = 1, n - resid(jj) = zero - 95 continue - rnorm = zero - end if -c -c %----------------------------------------------% -c | Branch here directly if iterative refinement | -c | wasn't necessary or after at most NITER_REF | -c | steps of iterative refinement. | -c %----------------------------------------------% -c - 100 continue -c - rstart = .false. - orth2 = .false. -c - call igraphsecond (t5) - titref = titref + (t5 - t4) -c -c %------------------------------------% -c | STEP 6: Update j = j+1; Continue | -c %------------------------------------% -c - j = j + 1 - if (j .gt. k+np) then - call igraphsecond (t1) - tnaitr = tnaitr + (t1 - t0) - ido = 99 - do 110 i = max(1,k), k+np-1 -c -c %--------------------------------------------% -c | Check for splitting and deflation. | -c | Use a standard test as in the QR algorithm | -c | REFERENCE: LAPACK subroutine dlahqr | -c %--------------------------------------------% -c - tst1 = abs( h( i, i ) ) + abs( h( i+1, i+1 ) ) - if( tst1.eq.zero ) - & tst1 = dlanhs( '1', k+np, h, ldh, workd(n+1) ) - if( abs( h( i+1,i ) ).le.max( ulp*tst1, smlnum ) ) - & h(i+1,i) = zero - 110 continue -c - if (msglvl .gt. 2) then - call igraphdmout (logfil, k+np, k+np, h, ldh, ndigit, - & '_naitr: Final upper Hessenberg matrix H of order K+NP') - end if -c - go to 9000 - end if -c -c %--------------------------------------------------------% -c | Loop back to extend the factorization by another step. | -c %--------------------------------------------------------% -c - go to 1000 -c -c %---------------------------------------------------------------% -c | | -c | E N D O F M A I N I T E R A T I O N L O O P | -c | | -c %---------------------------------------------------------------% -c - 9000 continue - return -c -c %---------------% -c | End of igraphdnaitr | -c %---------------% -c - end diff --git a/src/rigraph/vendor/arpack/dnapps.f b/src/rigraph/vendor/arpack/dnapps.f deleted file mode 100644 index 247e66b..0000000 --- a/src/rigraph/vendor/arpack/dnapps.f +++ /dev/null @@ -1,647 +0,0 @@ -c----------------------------------------------------------------------- -c\BeginDoc -c -c\Name: igraphdnapps -c -c\Description: -c Given the Arnoldi factorization -c -c A*V_{k} - V_{k}*H_{k} = r_{k+p}*e_{k+p}^T, -c -c apply NP implicit shifts resulting in -c -c A*(V_{k}*Q) - (V_{k}*Q)*(Q^T* H_{k}*Q) = r_{k+p}*e_{k+p}^T * Q -c -c where Q is an orthogonal matrix which is the product of rotations -c and reflections resulting from the NP bulge chage sweeps. -c The updated Arnoldi factorization becomes: -c -c A*VNEW_{k} - VNEW_{k}*HNEW_{k} = rnew_{k}*e_{k}^T. -c -c\Usage: -c call igraphdnapps -c ( N, KEV, NP, SHIFTR, SHIFTI, V, LDV, H, LDH, RESID, Q, LDQ, -c WORKL, WORKD ) -c -c\Arguments -c N Integer. (INPUT) -c Problem size, i.e. size of matrix A. -c -c KEV Integer. (INPUT/OUTPUT) -c KEV+NP is the size of the input matrix H. -c KEV is the size of the updated matrix HNEW. KEV is only -c updated on ouput when fewer than NP shifts are applied in -c order to keep the conjugate pair together. -c -c NP Integer. (INPUT) -c Number of implicit shifts to be applied. -c -c SHIFTR, Double precision array of length NP. (INPUT) -c SHIFTI Real and imaginary part of the shifts to be applied. -c Upon, entry to igraphdnapps, the shifts must be sorted so that the -c conjugate pairs are in consecutive locations. -c -c V Double precision N by (KEV+NP) array. (INPUT/OUTPUT) -c On INPUT, V contains the current KEV+NP Arnoldi vectors. -c On OUTPUT, V contains the updated KEV Arnoldi vectors -c in the first KEV columns of V. -c -c LDV Integer. (INPUT) -c Leading dimension of V exactly as declared in the calling -c program. -c -c H Double precision (KEV+NP) by (KEV+NP) array. (INPUT/OUTPUT) -c On INPUT, H contains the current KEV+NP by KEV+NP upper -c Hessenber matrix of the Arnoldi factorization. -c On OUTPUT, H contains the updated KEV by KEV upper Hessenberg -c matrix in the KEV leading submatrix. -c -c LDH Integer. (INPUT) -c Leading dimension of H exactly as declared in the calling -c program. -c -c RESID Double precision array of length N. (INPUT/OUTPUT) -c On INPUT, RESID contains the the residual vector r_{k+p}. -c On OUTPUT, RESID is the update residual vector rnew_{k} -c in the first KEV locations. -c -c Q Double precision KEV+NP by KEV+NP work array. (WORKSPACE) -c Work array used to accumulate the rotations and reflections -c during the bulge chase sweep. -c -c LDQ Integer. (INPUT) -c Leading dimension of Q exactly as declared in the calling -c program. -c -c WORKL Double precision work array of length (KEV+NP). (WORKSPACE) -c Private (replicated) array on each PE or array allocated on -c the front end. -c -c WORKD Double precision work array of length 2*N. (WORKSPACE) -c Distributed array used in the application of the accumulated -c orthogonal matrix Q. -c -c\EndDoc -c -c----------------------------------------------------------------------- -c -c\BeginLib -c -c\Local variables: -c xxxxxx real -c -c\References: -c 1. D.C. Sorensen, "Implicit Application of Polynomial Filters in -c a k-Step Arnoldi Method", SIAM J. Matr. Anal. Apps., 13 (1992), -c pp 357-385. -c -c\Routines called: -c igraphivout ARPACK utility routine that prints integers. -c igraphsecond ARPACK utility routine for timing. -c igraphdmout ARPACK utility routine that prints matrices. -c igraphdvout ARPACK utility routine that prints vectors. -c dlabad LAPACK routine that computes machine constants. -c dlacpy LAPACK matrix copy routine. -c dlamch LAPACK routine that determines machine constants. -c dlanhs LAPACK routine that computes various norms of a matrix. -c dlapy2 LAPACK routine to compute sqrt(x**2+y**2) carefully. -c dlarf LAPACK routine that applies Householder reflection to -c a matrix. -c dlarfg LAPACK Householder reflection construction routine. -c dlartg LAPACK Givens rotation construction routine. -c dlaset LAPACK matrix initialization routine. -c dgemv Level 2 BLAS routine for matrix vector multiplication. -c daxpy Level 1 BLAS that computes a vector triad. -c dcopy Level 1 BLAS that copies one vector to another . -c dscal Level 1 BLAS that scales a vector. -c -c\Author -c Danny Sorensen Phuong Vu -c Richard Lehoucq CRPC / Rice University -c Dept. of Computational & Houston, Texas -c Applied Mathematics -c Rice University -c Houston, Texas -c -c\Revision history: -c xx/xx/92: Version ' 2.1' -c -c\SCCS Information: @(#) -c FILE: napps.F SID: 2.3 DATE OF SID: 4/20/96 RELEASE: 2 -c -c\Remarks -c 1. In this version, each shift is applied to all the sublocks of -c the Hessenberg matrix H and not just to the submatrix that it -c comes from. Deflation as in LAPACK routine dlahqr (QR algorithm -c for upper Hessenberg matrices ) is used. -c The subdiagonals of H are enforced to be non-negative. -c -c\EndLib -c -c----------------------------------------------------------------------- -c - subroutine igraphdnapps - & ( n, kev, np, shiftr, shifti, v, ldv, h, ldh, resid, q, ldq, - & workl, workd ) -c -c %----------------------------------------------------% -c | Include files for debugging and timing information | -c %----------------------------------------------------% -c - include 'debug.h' - include 'stat.h' -c -c %------------------% -c | Scalar Arguments | -c %------------------% -c - integer kev, ldh, ldq, ldv, n, np -c -c %-----------------% -c | Array Arguments | -c %-----------------% -c - Double precision - & h(ldh,kev+np), resid(n), shifti(np), shiftr(np), - & v(ldv,kev+np), q(ldq,kev+np), workd(2*n), workl(kev+np) -c -c %------------% -c | Parameters | -c %------------% -c - Double precision - & one, zero - parameter (one = 1.0D+0, zero = 0.0D+0) -c -c %------------------------% -c | Local Scalars & Arrays | -c %------------------------% -c - integer i, iend, ir, istart, j, jj, kplusp, msglvl, nr - logical cconj, first - Double precision - & c, f, g, h11, h12, h21, h22, h32, ovfl, r, s, sigmai, - & sigmar, smlnum, ulp, unfl, u(3), t, tau, tst1 - save first, ovfl, smlnum, ulp, unfl -c -c %----------------------% -c | External Subroutines | -c %----------------------% -c - external daxpy, dcopy, dscal, dlacpy, dlarfg, dlarf, - & dlaset, dlabad, igraphsecond, dlartg -c -c %--------------------% -c | External Functions | -c %--------------------% -c - Double precision - & dlamch, dlanhs, dlapy2 - external dlamch, dlanhs, dlapy2 -c -c %----------------------% -c | Intrinsics Functions | -c %----------------------% -c - intrinsic abs, max, min -c -c %----------------% -c | Data statments | -c %----------------% -c - data first / .true. / -c -c %-----------------------% -c | Executable Statements | -c %-----------------------% -c - if (first) then -c -c %-----------------------------------------------% -c | Set machine-dependent constants for the | -c | stopping criterion. If norm(H) <= sqrt(OVFL), | -c | overflow should not occur. | -c | REFERENCE: LAPACK subroutine dlahqr | -c %-----------------------------------------------% -c - unfl = dlamch( 'safe minimum' ) - ovfl = one / unfl - call dlabad( unfl, ovfl ) - ulp = dlamch( 'precision' ) - smlnum = unfl*( n / ulp ) - first = .false. - end if -c -c %-------------------------------% -c | Initialize timing statistics | -c | & message level for debugging | -c %-------------------------------% -c - call igraphsecond (t0) - msglvl = mnapps - kplusp = kev + np -c -c %--------------------------------------------% -c | Initialize Q to the identity to accumulate | -c | the rotations and reflections | -c %--------------------------------------------% -c - call dlaset ('All', kplusp, kplusp, zero, one, q, ldq) -c -c %----------------------------------------------% -c | Quick return if there are no shifts to apply | -c %----------------------------------------------% -c - if (np .eq. 0) go to 9000 -c -c %----------------------------------------------% -c | Chase the bulge with the application of each | -c | implicit shift. Each shift is applied to the | -c | whole matrix including each block. | -c %----------------------------------------------% -c - cconj = .false. - do 110 jj = 1, np - sigmar = shiftr(jj) - sigmai = shifti(jj) -c - if (msglvl .gt. 2 ) then - call igraphivout (logfil, 1, [jj], ndigit, - & '_napps: shift number.') - call igraphdvout (logfil, 1, [sigmar], ndigit, - & '_napps: The real part of the shift ') - call igraphdvout (logfil, 1, [sigmai], ndigit, - & '_napps: The imaginary part of the shift ') - end if -c -c %-------------------------------------------------% -c | The following set of conditionals is necessary | -c | in order that complex conjugate pairs of shifts | -c | are applied together or not at all. | -c %-------------------------------------------------% -c - if ( cconj ) then -c -c %-----------------------------------------% -c | cconj = .true. means the previous shift | -c | had non-zero imaginary part. | -c %-----------------------------------------% -c - cconj = .false. - go to 110 - else if ( jj .lt. np .and. abs( sigmai ) .gt. zero ) then -c -c %------------------------------------% -c | Start of a complex conjugate pair. | -c %------------------------------------% -c - cconj = .true. - else if ( jj .eq. np .and. abs( sigmai ) .gt. zero ) then -c -c %----------------------------------------------% -c | The last shift has a nonzero imaginary part. | -c | Don't apply it; thus the order of the | -c | compressed H is order KEV+1 since only np-1 | -c | were applied. | -c %----------------------------------------------% -c - kev = kev + 1 - go to 110 - end if - istart = 1 - 20 continue -c -c %--------------------------------------------------% -c | if sigmai = 0 then | -c | Apply the jj-th shift ... | -c | else | -c | Apply the jj-th and (jj+1)-th together ... | -c | (Note that jj < np at this point in the code) | -c | end | -c | to the current block of H. The next do loop | -c | determines the current block ; | -c %--------------------------------------------------% -c - do 30 i = istart, kplusp-1 -c -c %----------------------------------------% -c | Check for splitting and deflation. Use | -c | a standard test as in the QR algorithm | -c | REFERENCE: LAPACK subroutine dlahqr | -c %----------------------------------------% -c - tst1 = abs( h( i, i ) ) + abs( h( i+1, i+1 ) ) - if( tst1.eq.zero ) - & tst1 = dlanhs( '1', kplusp-jj+1, h, ldh, workl ) - if( abs( h( i+1,i ) ).le.max( ulp*tst1, smlnum ) ) then - if (msglvl .gt. 0) then - call igraphivout (logfil, 1, [i], ndigit, - & '_napps: matrix splitting at row/column no.') - call igraphivout (logfil, 1, [jj], ndigit, - & '_napps: matrix splitting with shift number.') - call igraphdvout (logfil, 1, h(i+1,i), ndigit, - & '_napps: off diagonal element.') - end if - iend = i - h(i+1,i) = zero - go to 40 - end if - 30 continue - iend = kplusp - 40 continue -c - if (msglvl .gt. 2) then - call igraphivout (logfil, 1, [istart], ndigit, - & '_napps: Start of current block ') - call igraphivout (logfil, 1, [iend], ndigit, - & '_napps: End of current block ') - end if -c -c %------------------------------------------------% -c | No reason to apply a shift to block of order 1 | -c %------------------------------------------------% -c - if ( istart .eq. iend ) go to 100 -c -c %------------------------------------------------------% -c | If istart + 1 = iend then no reason to apply a | -c | complex conjugate pair of shifts on a 2 by 2 matrix. | -c %------------------------------------------------------% -c - if ( istart + 1 .eq. iend .and. abs( sigmai ) .gt. zero ) - & go to 100 -c - h11 = h(istart,istart) - h21 = h(istart+1,istart) - if ( abs( sigmai ) .le. zero ) then -c -c %---------------------------------------------% -c | Real-valued shift ==> apply single shift QR | -c %---------------------------------------------% -c - f = h11 - sigmar - g = h21 -c - do 80 i = istart, iend-1 -c -c %-----------------------------------------------------% -c | Contruct the plane rotation G to zero out the bulge | -c %-----------------------------------------------------% -c - call dlartg (f, g, c, s, r) - if (i .gt. istart) then -c -c %-------------------------------------------% -c | The following ensures that h(1:iend-1,1), | -c | the first iend-2 off diagonal of elements | -c | H, remain non negative. | -c %-------------------------------------------% -c - if (r .lt. zero) then - r = -r - c = -c - s = -s - end if - h(i,i-1) = r - h(i+1,i-1) = zero - end if -c -c %---------------------------------------------% -c | Apply rotation to the left of H; H <- G'*H | -c %---------------------------------------------% -c - do 50 j = i, kplusp - t = c*h(i,j) + s*h(i+1,j) - h(i+1,j) = -s*h(i,j) + c*h(i+1,j) - h(i,j) = t - 50 continue -c -c %---------------------------------------------% -c | Apply rotation to the right of H; H <- H*G | -c %---------------------------------------------% -c - do 60 j = 1, min(i+2,iend) - t = c*h(j,i) + s*h(j,i+1) - h(j,i+1) = -s*h(j,i) + c*h(j,i+1) - h(j,i) = t - 60 continue -c -c %----------------------------------------------------% -c | Accumulate the rotation in the matrix Q; Q <- Q*G | -c %----------------------------------------------------% -c - do 70 j = 1, min( j+jj, kplusp ) - t = c*q(j,i) + s*q(j,i+1) - q(j,i+1) = - s*q(j,i) + c*q(j,i+1) - q(j,i) = t - 70 continue -c -c %---------------------------% -c | Prepare for next rotation | -c %---------------------------% -c - if (i .lt. iend-1) then - f = h(i+1,i) - g = h(i+2,i) - end if - 80 continue -c -c %-----------------------------------% -c | Finished applying the real shift. | -c %-----------------------------------% -c - else -c -c %----------------------------------------------------% -c | Complex conjugate shifts ==> apply double shift QR | -c %----------------------------------------------------% -c - h12 = h(istart,istart+1) - h22 = h(istart+1,istart+1) - h32 = h(istart+2,istart+1) -c -c %---------------------------------------------------------% -c | Compute 1st column of (H - shift*I)*(H - conj(shift)*I) | -c %---------------------------------------------------------% -c - s = 2.0*sigmar - t = dlapy2 ( sigmar, sigmai ) - u(1) = ( h11 * (h11 - s) + t * t ) / h21 + h12 - u(2) = h11 + h22 - s - u(3) = h32 -c - do 90 i = istart, iend-1 -c - nr = min ( 3, iend-i+1 ) -c -c %-----------------------------------------------------% -c | Construct Householder reflector G to zero out u(1). | -c | G is of the form I - tau*( 1 u )' * ( 1 u' ). | -c %-----------------------------------------------------% -c - call dlarfg ( nr, u(1), u(2), 1, tau ) -c - if (i .gt. istart) then - h(i,i-1) = u(1) - h(i+1,i-1) = zero - if (i .lt. iend-1) h(i+2,i-1) = zero - end if - u(1) = one -c -c %--------------------------------------% -c | Apply the reflector to the left of H | -c %--------------------------------------% -c - call dlarf ('Left', nr, kplusp-i+1, u, 1, tau, - & h(i,i), ldh, workl) -c -c %---------------------------------------% -c | Apply the reflector to the right of H | -c %---------------------------------------% -c - ir = min ( i+3, iend ) - call dlarf ('Right', ir, nr, u, 1, tau, - & h(1,i), ldh, workl) -c -c %-----------------------------------------------------% -c | Accumulate the reflector in the matrix Q; Q <- Q*G | -c %-----------------------------------------------------% -c - call dlarf ('Right', kplusp, nr, u, 1, tau, - & q(1,i), ldq, workl) -c -c %----------------------------% -c | Prepare for next reflector | -c %----------------------------% -c - if (i .lt. iend-1) then - u(1) = h(i+1,i) - u(2) = h(i+2,i) - if (i .lt. iend-2) u(3) = h(i+3,i) - end if -c - 90 continue -c -c %--------------------------------------------% -c | Finished applying a complex pair of shifts | -c | to the current block | -c %--------------------------------------------% -c - end if -c - 100 continue -c -c %---------------------------------------------------------% -c | Apply the same shift to the next block if there is any. | -c %---------------------------------------------------------% -c - istart = iend + 1 - if (iend .lt. kplusp) go to 20 -c -c %---------------------------------------------% -c | Loop back to the top to get the next shift. | -c %---------------------------------------------% -c - 110 continue -c -c %--------------------------------------------------% -c | Perform a similarity transformation that makes | -c | sure that H will have non negative sub diagonals | -c %--------------------------------------------------% -c - do 120 j=1,kev - if ( h(j+1,j) .lt. zero ) then - call dscal( kplusp-j+1, -one, h(j+1,j), ldh ) - call dscal( min(j+2, kplusp), -one, h(1,j+1), 1 ) - call dscal( min(j+np+1,kplusp), -one, q(1,j+1), 1 ) - end if - 120 continue -c - do 130 i = 1, kev -c -c %--------------------------------------------% -c | Final check for splitting and deflation. | -c | Use a standard test as in the QR algorithm | -c | REFERENCE: LAPACK subroutine dlahqr | -c %--------------------------------------------% -c - tst1 = abs( h( i, i ) ) + abs( h( i+1, i+1 ) ) - if( tst1.eq.zero ) - & tst1 = dlanhs( '1', kev, h, ldh, workl ) - if( h( i+1,i ) .le. max( ulp*tst1, smlnum ) ) - & h(i+1,i) = zero - 130 continue -c -c %-------------------------------------------------% -c | Compute the (kev+1)-st column of (V*Q) and | -c | temporarily store the result in WORKD(N+1:2*N). | -c | This is needed in the residual update since we | -c | cannot GUARANTEE that the corresponding entry | -c | of H would be zero as in exact arithmetic. | -c %-------------------------------------------------% -c - if (h(kev+1,kev) .gt. zero) - & call dgemv ('N', n, kplusp, one, v, ldv, q(1,kev+1), 1, zero, - & workd(n+1), 1) -c -c %----------------------------------------------------------% -c | Compute column 1 to kev of (V*Q) in backward order | -c | taking advantage of the upper Hessenberg structure of Q. | -c %----------------------------------------------------------% -c - do 140 i = 1, kev - call dgemv ('N', n, kplusp-i+1, one, v, ldv, - & q(1,kev-i+1), 1, zero, workd, 1) - call dcopy (n, workd, 1, v(1,kplusp-i+1), 1) - 140 continue -c -c %-------------------------------------------------% -c | Move v(:,kplusp-kev+1:kplusp) into v(:,1:kev). | -c %-------------------------------------------------% -c - call dlacpy ('A', n, kev, v(1,kplusp-kev+1), ldv, v, ldv) -c -c %--------------------------------------------------------------% -c | Copy the (kev+1)-st column of (V*Q) in the appropriate place | -c %--------------------------------------------------------------% -c - if (h(kev+1,kev) .gt. zero) - & call dcopy (n, workd(n+1), 1, v(1,kev+1), 1) -c -c %-------------------------------------% -c | Update the residual vector: | -c | r <- sigmak*r + betak*v(:,kev+1) | -c | where | -c | sigmak = (e_{kplusp}'*Q)*e_{kev} | -c | betak = e_{kev+1}'*H*e_{kev} | -c %-------------------------------------% -c - call dscal (n, q(kplusp,kev), resid, 1) - if (h(kev+1,kev) .gt. zero) - & call daxpy (n, h(kev+1,kev), v(1,kev+1), 1, resid, 1) -c - if (msglvl .gt. 1) then - call igraphdvout (logfil, 1, q(kplusp,kev), ndigit, - & '_napps: sigmak = (e_{kev+p}^T*Q)*e_{kev}') - call igraphdvout (logfil, 1, h(kev+1,kev), ndigit, - & '_napps: betak = e_{kev+1}^T*H*e_{kev}') - call igraphivout (logfil, 1, [kev], ndigit, - & '_napps: Order of the final Hessenberg matrix ') - if (msglvl .gt. 2) then - call igraphdmout (logfil, kev, kev, h, ldh, ndigit, - & '_napps: updated Hessenberg matrix H for next iteration') - end if -c - end if -c - 9000 continue - call igraphsecond (t1) - tnapps = tnapps + (t1 - t0) -c - return -c -c %---------------% -c | End of igraphdnapps | -c %---------------% -c - end diff --git a/src/rigraph/vendor/arpack/dnaup2.f b/src/rigraph/vendor/arpack/dnaup2.f deleted file mode 100644 index e060689..0000000 --- a/src/rigraph/vendor/arpack/dnaup2.f +++ /dev/null @@ -1,838 +0,0 @@ -c\BeginDoc -c -c\Name: igraphdnaup2 -c -c\Description: -c Intermediate level interface called by igraphdnaupd. -c -c\Usage: -c call igraphdnaup2 -c ( IDO, BMAT, N, WHICH, NEV, NP, TOL, RESID, MODE, IUPD, -c ISHIFT, MXITER, V, LDV, H, LDH, RITZR, RITZI, BOUNDS, -c Q, LDQ, WORKL, IPNTR, WORKD, INFO ) -c -c\Arguments -c -c IDO, BMAT, N, WHICH, NEV, TOL, RESID: same as defined in igraphdnaupd. -c MODE, ISHIFT, MXITER: see the definition of IPARAM in igraphdnaupd. -c -c NP Integer. (INPUT/OUTPUT) -c Contains the number of implicit shifts to apply during -c each Arnoldi iteration. -c If ISHIFT=1, NP is adjusted dynamically at each iteration -c to accelerate convergence and prevent stagnation. -c This is also roughly equal to the number of matrix-vector -c products (involving the operator OP) per Arnoldi iteration. -c The logic for adjusting is contained within the current -c subroutine. -c If ISHIFT=0, NP is the number of shifts the user needs -c to provide via reverse comunication. 0 < NP < NCV-NEV. -c NP may be less than NCV-NEV for two reasons. The first, is -c to keep complex conjugate pairs of "wanted" Ritz values -c together. The igraphsecond, is that a leading block of the current -c upper Hessenberg matrix has split off and contains "unwanted" -c Ritz values. -c Upon termination of the IRA iteration, NP contains the number -c of "converged" wanted Ritz values. -c -c IUPD Integer. (INPUT) -c IUPD .EQ. 0: use explicit restart instead implicit update. -c IUPD .NE. 0: use implicit update. -c -c V Double precision N by (NEV+NP) array. (INPUT/OUTPUT) -c The Arnoldi basis vectors are returned in the first NEV -c columns of V. -c -c LDV Integer. (INPUT) -c Leading dimension of V exactly as declared in the calling -c program. -c -c H Double precision (NEV+NP) by (NEV+NP) array. (OUTPUT) -c H is used to store the generated upper Hessenberg matrix -c -c LDH Integer. (INPUT) -c Leading dimension of H exactly as declared in the calling -c program. -c -c RITZR, Double precision arrays of length NEV+NP. (OUTPUT) -c RITZI RITZR(1:NEV) (resp. RITZI(1:NEV)) contains the real (resp. -c imaginary) part of the computed Ritz values of OP. -c -c BOUNDS Double precision array of length NEV+NP. (OUTPUT) -c BOUNDS(1:NEV) contain the error bounds corresponding to -c the computed Ritz values. -c -c Q Double precision (NEV+NP) by (NEV+NP) array. (WORKSPACE) -c Private (replicated) work array used to accumulate the -c rotation in the shift application step. -c -c LDQ Integer. (INPUT) -c Leading dimension of Q exactly as declared in the calling -c program. -c -c WORKL Double precision work array of length at least -c (NEV+NP)**2 + 3*(NEV+NP). (INPUT/WORKSPACE) -c Private (replicated) array on each PE or array allocated on -c the front end. It is used in shifts calculation, shifts -c application and convergence checking. -c -c On exit, the last 3*(NEV+NP) locations of WORKL contain -c the Ritz values (real,imaginary) and associated Ritz -c estimates of the current Hessenberg matrix. They are -c listed in the same order as returned from igraphdneigh. -c -c If ISHIFT .EQ. O and IDO .EQ. 3, the first 2*NP locations -c of WORKL are used in reverse communication to hold the user -c supplied shifts. -c -c IPNTR Integer array of length 3. (OUTPUT) -c Pointer to mark the starting locations in the WORKD for -c vectors used by the Arnoldi iteration. -c ------------------------------------------------------------- -c IPNTR(1): pointer to the current operand vector X. -c IPNTR(2): pointer to the current result vector Y. -c IPNTR(3): pointer to the vector B * X when used in the -c shift-and-invert mode. X is the current operand. -c ------------------------------------------------------------- -c -c WORKD Double precision work array of length 3*N. (WORKSPACE) -c Distributed array to be used in the basic Arnoldi iteration -c for reverse communication. The user should not use WORKD -c as temporary workspace during the iteration !!!!!!!!!! -c See Data Distribution Note in DNAUPD. -c -c INFO Integer. (INPUT/OUTPUT) -c If INFO .EQ. 0, a randomly initial residual vector is used. -c If INFO .NE. 0, RESID contains the initial residual vector, -c possibly from a previous run. -c Error flag on output. -c = 0: Normal return. -c = 1: Maximum number of iterations taken. -c All possible eigenvalues of OP has been found. -c NP returns the number of converged Ritz values. -c = 2: No shifts could be applied. -c = -8: Error return from LAPACK eigenvalue calculation; -c This should never happen. -c = -9: Starting vector is zero. -c = -9999: Could not build an Arnoldi factorization. -c Size that was built in returned in NP. -c -c\EndDoc -c -c----------------------------------------------------------------------- -c -c\BeginLib -c -c\Local variables: -c xxxxxx real -c -c\References: -c 1. D.C. Sorensen, "Implicit Application of Polynomial Filters in -c a k-Step Arnoldi Method", SIAM J. Matr. Anal. Apps., 13 (1992), -c pp 357-385. -c 2. R.B. Lehoucq, "Analysis and Implementation of an Implicitly -c Restarted Arnoldi Iteration", Rice University Technical Report -c TR95-13, Department of Computational and Applied Mathematics. -c -c\Routines called: -c igraphdgetv0 ARPACK initial vector generation routine. -c igraphdnaitr ARPACK Arnoldi factorization routine. -c igraphdnapps ARPACK application of implicit shifts routine. -c igraphdnconv ARPACK convergence of Ritz values routine. -c igraphdneigh ARPACK compute Ritz values and error bounds routine. -c igraphdngets ARPACK reorder Ritz values and error bounds routine. -c igraphdsortc ARPACK sorting routine. -c igraphivout ARPACK utility routine that prints integers. -c igraphsecond ARPACK utility routine for timing. -c igraphdmout ARPACK utility routine that prints matrices -c igraphdvout ARPACK utility routine that prints vectors. -c dlamch LAPACK routine that determines machine constants. -c dlapy2 LAPACK routine to compute sqrt(x**2+y**2) carefully. -c dcopy Level 1 BLAS that copies one vector to another . -c ddot Level 1 BLAS that computes the scalar product of two vectors. -c dnrm2 Level 1 BLAS that computes the norm of a vector. -c dswap Level 1 BLAS that swaps two vectors. -c -c\Author -c Danny Sorensen Phuong Vu -c Richard Lehoucq CRPC / Rice University -c Dept. of Computational & Houston, Texas -c Applied Mathematics -c Rice University -c Houston, Texas -c -c\SCCS Information: @(#) -c FILE: naup2.F SID: 2.4 DATE OF SID: 7/30/96 RELEASE: 2 -c -c\Remarks -c 1. None -c -c\EndLib -c -c----------------------------------------------------------------------- -c - subroutine igraphdnaup2 - & ( 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 ) -c -c %----------------------------------------------------% -c | Include files for debugging and timing information | -c %----------------------------------------------------% -c - include 'debug.h' - include 'stat.h' -c -c %------------------% -c | Scalar Arguments | -c %------------------% -c - character bmat*1, which*2 - integer ido, info, ishift, iupd, mode, ldh, ldq, ldv, mxiter, - & n, nev, np - Double precision - & tol -c -c %-----------------% -c | Array Arguments | -c %-----------------% -c - integer ipntr(13) - Double precision - & bounds(nev+np), h(ldh,nev+np), q(ldq,nev+np), resid(n), - & ritzi(nev+np), ritzr(nev+np), v(ldv,nev+np), - & workd(3*n), workl( (nev+np)*(nev+np+3) ) -c -c %------------% -c | Parameters | -c %------------% -c - Double precision - & one, zero - parameter (one = 1.0D+0, zero = 0.0D+0) -c -c %---------------% -c | Local Scalars | -c %---------------% -c - character wprime*2 - logical cnorm, getv0, initv, update, ushift - integer ierr, iter, j, kplusp, msglvl, nconv, nevbef, nev0, - & np0, nptemp, numcnv - Double precision - & rnorm, temp, eps23 -c -c %-----------------------% -c | Local array arguments | -c %-----------------------% -c - integer kp(4) - save -c -c %----------------------% -c | External Subroutines | -c %----------------------% -c - external dcopy, igraphdgetv0, igraphdnaitr, igraphdnconv, - & igraphdneigh, igraphdngets, igraphdnapps, - & igraphdvout, igraphivout, igraphsecond -c -c %--------------------% -c | External Functions | -c %--------------------% -c - Double precision - & ddot, dnrm2, dlapy2, dlamch - external ddot, dnrm2, dlapy2, dlamch -c -c %---------------------% -c | Intrinsic Functions | -c %---------------------% -c - intrinsic min, max, abs, sqrt -c -c %-----------------------% -c | Executable Statements | -c %-----------------------% -c - if (ido .eq. 0) then -c - call igraphsecond (t0) -c - msglvl = mnaup2 -c -c %-------------------------------------% -c | Get the machine dependent constant. | -c %-------------------------------------% -c - eps23 = dlamch('Epsilon-Machine') - eps23 = eps23**(2.0D+0 / 3.0D+0) -c - nev0 = nev - np0 = np -c -c %-------------------------------------% -c | kplusp is the bound on the largest | -c | Lanczos factorization built. | -c | nconv is the current number of | -c | "converged" eigenvlues. | -c | iter is the counter on the current | -c | iteration step. | -c %-------------------------------------% -c - kplusp = nev + np - nconv = 0 - iter = 0 -c -c %---------------------------------------% -c | Set flags for computing the first NEV | -c | steps of the Arnoldi factorization. | -c %---------------------------------------% -c - getv0 = .true. - update = .false. - ushift = .false. - cnorm = .false. -c - if (info .ne. 0) then -c -c %--------------------------------------------% -c | User provides the initial residual vector. | -c %--------------------------------------------% -c - initv = .true. - info = 0 - else - initv = .false. - end if - end if -c -c %---------------------------------------------% -c | Get a possibly random starting vector and | -c | force it into the range of the operator OP. | -c %---------------------------------------------% -c - 10 continue -c - if (getv0) then - call igraphdgetv0 (ido, bmat, 1, initv, n, 1, v, ldv, resid, - & rnorm, ipntr, workd, info) -c - if (ido .ne. 99) go to 9000 -c - if (rnorm .eq. zero) then -c -c %-----------------------------------------% -c | The initial vector is zero. Error exit. | -c %-----------------------------------------% -c - info = -9 - go to 1100 - end if - getv0 = .false. - ido = 0 - end if -c -c %-----------------------------------% -c | Back from reverse communication : | -c | continue with update step | -c %-----------------------------------% -c - if (update) go to 20 -c -c %-------------------------------------------% -c | Back from computing user specified shifts | -c %-------------------------------------------% -c - if (ushift) go to 50 -c -c %-------------------------------------% -c | Back from computing residual norm | -c | at the end of the current iteration | -c %-------------------------------------% -c - if (cnorm) go to 100 -c -c %----------------------------------------------------------% -c | Compute the first NEV steps of the Arnoldi factorization | -c %----------------------------------------------------------% -c - call igraphdnaitr (ido, bmat, n, 0, nev, mode, resid, rnorm, v, - & ldv, h, ldh, ipntr, workd, info) -c -c %---------------------------------------------------% -c | ido .ne. 99 implies use of reverse communication | -c | to compute operations involving OP and possibly B | -c %---------------------------------------------------% -c - if (ido .ne. 99) go to 9000 -c - if (info .gt. 0) then - np = info - mxiter = iter - info = -9999 - go to 1200 - end if -c -c %--------------------------------------------------------------% -c | | -c | M A I N ARNOLDI I T E R A T I O N L O O P | -c | Each iteration implicitly restarts the Arnoldi | -c | factorization in place. | -c | | -c %--------------------------------------------------------------% -c - 1000 continue -c - iter = iter + 1 -c - if (msglvl .gt. 0) then - call igraphivout (logfil, 1, [iter], ndigit, - & '_naup2: **** Start of major iteration number ****') - end if -c -c %-----------------------------------------------------------% -c | Compute NP additional steps of the Arnoldi factorization. | -c | Adjust NP since NEV might have been updated by last call | -c | to the shift application routine igraphdnapps. | -c %-----------------------------------------------------------% -c - np = kplusp - nev -c - if (msglvl .gt. 1) then - call igraphivout (logfil, 1, [nev], ndigit, - & '_naup2: The length of the current Arnoldi factorization') - call igraphivout (logfil, 1, [np], ndigit, - & '_naup2: Extend the Arnoldi factorization by') - end if -c -c %-----------------------------------------------------------% -c | Compute NP additional steps of the Arnoldi factorization. | -c %-----------------------------------------------------------% -c - ido = 0 - 20 continue - update = .true. -c - call igraphdnaitr (ido, bmat, n, nev, np, mode, resid, rnorm, - & v, ldv, h, ldh, ipntr, workd, info) -c -c %---------------------------------------------------% -c | ido .ne. 99 implies use of reverse communication | -c | to compute operations involving OP and possibly B | -c %---------------------------------------------------% -c - if (ido .ne. 99) go to 9000 -c - if (info .gt. 0) then - np = info - mxiter = iter - info = -9999 - go to 1200 - end if - update = .false. -c - if (msglvl .gt. 1) then - call igraphdvout (logfil, 1, [rnorm], ndigit, - & '_naup2: Corresponding B-norm of the residual') - end if -c -c %--------------------------------------------------------% -c | Compute the eigenvalues and corresponding error bounds | -c | of the current upper Hessenberg matrix. | -c %--------------------------------------------------------% -c - call igraphdneigh (rnorm, kplusp, h, ldh, ritzr, ritzi, bounds, - & q, ldq, workl, ierr) -c - if (ierr .ne. 0) then - info = -8 - go to 1200 - end if -c -c %----------------------------------------------------% -c | Make a copy of eigenvalues and corresponding error | -c | bounds obtained from igraphdneigh. | -c %----------------------------------------------------% -c - call dcopy(kplusp, ritzr, 1, workl(kplusp**2+1), 1) - call dcopy(kplusp, ritzi, 1, workl(kplusp**2+kplusp+1), 1) - call dcopy(kplusp, bounds, 1, workl(kplusp**2+2*kplusp+1), 1) -c -c %---------------------------------------------------% -c | Select the wanted Ritz values and their bounds | -c | to be used in the convergence test. | -c | The wanted part of the spectrum and corresponding | -c | error bounds are in the last NEV loc. of RITZR, | -c | RITZI and BOUNDS respectively. The variables NEV | -c | and NP may be updated if the NEV-th wanted Ritz | -c | value has a non zero imaginary part. In this case | -c | NEV is increased by one and NP decreased by one. | -c | NOTE: The last two arguments of igraphdngets are no | -c | longer used as of version 2.1. | -c %---------------------------------------------------% -c - nev = nev0 - np = np0 - numcnv = nev - call igraphdngets (ishift, which, nev, np, ritzr, ritzi, - & bounds, workl, workl(np+1)) - if (nev .eq. nev0+1) numcnv = nev0+1 -c -c %-------------------% -c | Convergence test. | -c %-------------------% -c - call dcopy (nev, bounds(np+1), 1, workl(2*np+1), 1) - call igraphdnconv (nev, ritzr(np+1), ritzi(np+1), - & workl(2*np+1), tol, nconv) -c - if (msglvl .gt. 2) then - kp(1) = nev - kp(2) = np - kp(3) = numcnv - kp(4) = nconv - call igraphivout (logfil, 4, kp, ndigit, - & '_naup2: NEV, NP, NUMCNV, NCONV are') - call igraphdvout (logfil, kplusp, ritzr, ndigit, - & '_naup2: Real part of the eigenvalues of H') - call igraphdvout (logfil, kplusp, ritzi, ndigit, - & '_naup2: Imaginary part of the eigenvalues of H') - call igraphdvout (logfil, kplusp, bounds, ndigit, - & '_naup2: Ritz estimates of the current NCV Ritz values') - end if -c -c %---------------------------------------------------------% -c | Count the number of unwanted Ritz values that have zero | -c | Ritz estimates. If any Ritz estimates are equal to zero | -c | then a leading block of H of order equal to at least | -c | the number of Ritz values with zero Ritz estimates has | -c | split off. None of these Ritz values may be removed by | -c | shifting. Decrease NP the number of shifts to apply. If | -c | no shifts may be applied, then prepare to exit | -c %---------------------------------------------------------% -c - nptemp = np - do 30 j=1, nptemp - if (bounds(j) .eq. zero) then - np = np - 1 - nev = nev + 1 - end if - 30 continue -c - if ( (nconv .ge. numcnv) .or. - & (iter .gt. mxiter) .or. - & (np .eq. 0) ) then -c - if (msglvl .gt. 4) then - call igraphdvout(logfil, kplusp, workl(kplusp**2+1), - & ndigit, - & '_naup2: Real part of the eig computed by _neigh:') - call igraphdvout(logfil, kplusp, - & workl(kplusp**2+kplusp+1), ndigit, - & '_naup2: Imag part of the eig computed by _neigh:') - call igraphdvout(logfil, kplusp, - & workl(kplusp**2+kplusp*2+1), ndigit, - & '_naup2: Ritz eistmates computed by _neigh:') - end if -c -c %------------------------------------------------% -c | Prepare to exit. Put the converged Ritz values | -c | and corresponding bounds in RITZ(1:NCONV) and | -c | BOUNDS(1:NCONV) respectively. Then sort. Be | -c | careful when NCONV > NP | -c %------------------------------------------------% -c -c %------------------------------------------% -c | Use h( 3,1 ) as storage to communicate | -c | rnorm to _neupd if needed | -c %------------------------------------------% - - h(3,1) = rnorm -c -c %----------------------------------------------% -c | To be consistent with igraphdngets, we first do a | -c | pre-processing sort in order to keep complex | -c | conjugate pairs together. This is similar | -c | to the pre-processing sort used in igraphdngets | -c | except that the sort is done in the opposite | -c | order. | -c %----------------------------------------------% -c - if (which .eq. 'LM') wprime = 'SR' - if (which .eq. 'SM') wprime = 'LR' - if (which .eq. 'LR') wprime = 'SM' - if (which .eq. 'SR') wprime = 'LM' - if (which .eq. 'LI') wprime = 'SM' - if (which .eq. 'SI') wprime = 'LM' -c - call igraphdsortc (wprime, .true., kplusp, ritzr, ritzi, - & bounds) -c -c %----------------------------------------------% -c | Now sort Ritz values so that converged Ritz | -c | values appear within the first NEV locations | -c | of ritzr, ritzi and bounds, and the most | -c | desired one appears at the front. | -c %----------------------------------------------% -c - if (which .eq. 'LM') wprime = 'SM' - if (which .eq. 'SM') wprime = 'LM' - if (which .eq. 'LR') wprime = 'SR' - if (which .eq. 'SR') wprime = 'LR' - if (which .eq. 'LI') wprime = 'SI' - if (which .eq. 'SI') wprime = 'LI' -c - call igraphdsortc(wprime, .true., kplusp, ritzr, ritzi, - & bounds) -c -c %--------------------------------------------------% -c | Scale the Ritz estimate of each Ritz value | -c | by 1 / max(eps23,magnitude of the Ritz value). | -c %--------------------------------------------------% -c - do 35 j = 1, nev0 - temp = max(eps23,dlapy2(ritzr(j), - & ritzi(j))) - bounds(j) = bounds(j)/temp - 35 continue -c -c %----------------------------------------------------% -c | Sort the Ritz values according to the scaled Ritz | -c | esitmates. This will push all the converged ones | -c | towards the front of ritzr, ritzi, bounds | -c | (in the case when NCONV < NEV.) | -c %----------------------------------------------------% -c - wprime = 'LR' - call igraphdsortc(wprime, .true., nev0, bounds, ritzr, - & ritzi) -c -c %----------------------------------------------% -c | Scale the Ritz estimate back to its original | -c | value. | -c %----------------------------------------------% -c - do 40 j = 1, nev0 - temp = max(eps23, dlapy2(ritzr(j), - & ritzi(j))) - bounds(j) = bounds(j)*temp - 40 continue -c -c %------------------------------------------------% -c | Sort the converged Ritz values again so that | -c | the "threshold" value appears at the front of | -c | ritzr, ritzi and bound. | -c %------------------------------------------------% -c - call igraphdsortc(which, .true., nconv, ritzr, ritzi, - & bounds) -c - if (msglvl .gt. 1) then - call igraphdvout (logfil, kplusp, ritzr, ndigit, - & '_naup2: Sorted real part of the eigenvalues') - call igraphdvout (logfil, kplusp, ritzi, ndigit, - & '_naup2: Sorted imaginary part of the eigenvalues') - call igraphdvout (logfil, kplusp, bounds, ndigit, - & '_naup2: Sorted ritz estimates.') - end if -c -c %------------------------------------% -c | Max iterations have been exceeded. | -c %------------------------------------% -c - if (iter .gt. mxiter .and. nconv .lt. numcnv) info = 1 -c -c %---------------------% -c | No shifts to apply. | -c %---------------------% -c - if (np .eq. 0 .and. nconv .lt. numcnv) info = 2 -c - np = nconv - go to 1100 -c - else if ( (nconv .lt. numcnv) .and. (ishift .eq. 1) ) then -c -c %-------------------------------------------------% -c | Do not have all the requested eigenvalues yet. | -c | To prevent possible stagnation, adjust the size | -c | of NEV. | -c %-------------------------------------------------% -c - nevbef = nev - nev = nev + min(nconv, np/2) - if (nev .eq. 1 .and. kplusp .ge. 6) then - nev = kplusp / 2 - else if (nev .eq. 1 .and. kplusp .gt. 3) then - nev = 2 - end if - np = kplusp - nev -c -c %---------------------------------------% -c | If the size of NEV was just increased | -c | resort the eigenvalues. | -c %---------------------------------------% -c - if (nevbef .lt. nev) - & call igraphdngets (ishift, which, nev, np, ritzr, ritzi, - & bounds, workl, workl(np+1)) -c - end if -c - if (msglvl .gt. 0) then - call igraphivout (logfil, 1, [nconv], ndigit, - & '_naup2: no. of "converged" Ritz values at this iter.') - if (msglvl .gt. 1) then - kp(1) = nev - kp(2) = np - call igraphivout (logfil, 2, kp, ndigit, - & '_naup2: NEV and NP are') - call igraphdvout (logfil, nev, ritzr(np+1), ndigit, - & '_naup2: "wanted" Ritz values -- real part') - call igraphdvout (logfil, nev, ritzi(np+1), ndigit, - & '_naup2: "wanted" Ritz values -- imag part') - call igraphdvout (logfil, nev, bounds(np+1), ndigit, - & '_naup2: Ritz estimates of the "wanted" values ') - end if - end if -c - if (ishift .eq. 0) then -c -c %-------------------------------------------------------% -c | User specified shifts: reverse comminucation to | -c | compute the shifts. They are returned in the first | -c | 2*NP locations of WORKL. | -c %-------------------------------------------------------% -c - ushift = .true. - ido = 3 - go to 9000 - end if -c - 50 continue -c -c %------------------------------------% -c | Back from reverse communication; | -c | User specified shifts are returned | -c | in WORKL(1:2*NP) | -c %------------------------------------% -c - ushift = .false. -c - if ( ishift .eq. 0 ) then -c -c %----------------------------------% -c | Move the NP shifts from WORKL to | -c | RITZR, RITZI to free up WORKL | -c | for non-exact shift case. | -c %----------------------------------% -c - call dcopy (np, workl, 1, ritzr, 1) - call dcopy (np, workl(np+1), 1, ritzi, 1) - end if -c - if (msglvl .gt. 2) then - call igraphivout (logfil, 1, [np], ndigit, - & '_naup2: The number of shifts to apply ') - call igraphdvout (logfil, np, ritzr, ndigit, - & '_naup2: Real part of the shifts') - call igraphdvout (logfil, np, ritzi, ndigit, - & '_naup2: Imaginary part of the shifts') - if ( ishift .eq. 1 ) - & call igraphdvout (logfil, np, bounds, ndigit, - & '_naup2: Ritz estimates of the shifts') - end if -c -c %---------------------------------------------------------% -c | Apply the NP implicit shifts by QR bulge chasing. | -c | Each shift is applied to the whole upper Hessenberg | -c | matrix H. | -c | The first 2*N locations of WORKD are used as workspace. | -c %---------------------------------------------------------% -c - call igraphdnapps (n, nev, np, ritzr, ritzi, v, ldv, - & h, ldh, resid, q, ldq, workl, workd) -c -c %---------------------------------------------% -c | Compute the B-norm of the updated residual. | -c | Keep B*RESID in WORKD(1:N) to be used in | -c | the first step of the next call to igraphdnaitr. | -c %---------------------------------------------% -c - cnorm = .true. - call igraphsecond (t2) - if (bmat .eq. 'G') then - nbx = nbx + 1 - call dcopy (n, resid, 1, workd(n+1), 1) - ipntr(1) = n + 1 - ipntr(2) = 1 - ido = 2 -c -c %----------------------------------% -c | Exit in order to compute B*RESID | -c %----------------------------------% -c - go to 9000 - else if (bmat .eq. 'I') then - call dcopy (n, resid, 1, workd, 1) - end if -c - 100 continue -c -c %----------------------------------% -c | Back from reverse communication; | -c | WORKD(1:N) := B*RESID | -c %----------------------------------% -c - if (bmat .eq. 'G') then - call igraphsecond (t3) - tmvbx = tmvbx + (t3 - t2) - end if -c - if (bmat .eq. 'G') then - rnorm = ddot (n, resid, 1, workd, 1) - rnorm = sqrt(abs(rnorm)) - else if (bmat .eq. 'I') then - rnorm = dnrm2(n, resid, 1) - end if - cnorm = .false. -c - if (msglvl .gt. 2) then - call igraphdvout (logfil, 1, [rnorm], ndigit, - & '_naup2: B-norm of residual for compressed factorization') - call igraphdmout (logfil, nev, nev, h, ldh, ndigit, - & '_naup2: Compressed upper Hessenberg matrix H') - end if -c - go to 1000 -c -c %---------------------------------------------------------------% -c | | -c | E N D O F M A I N I T E R A T I O N L O O P | -c | | -c %---------------------------------------------------------------% -c - 1100 continue -c - mxiter = iter - nev = numcnv -c - 1200 continue - ido = 99 -c -c %------------% -c | Error Exit | -c %------------% -c - call igraphsecond (t1) - tnaup2 = t1 - t0 -c - 9000 continue -c -c %---------------% -c | End of igraphdnaup2 | -c %---------------% -c - return - end diff --git a/src/rigraph/vendor/arpack/dnaupd.f b/src/rigraph/vendor/arpack/dnaupd.f deleted file mode 100644 index 4133022..0000000 --- a/src/rigraph/vendor/arpack/dnaupd.f +++ /dev/null @@ -1,655 +0,0 @@ -c\BeginDoc -c -c\Name: igraphdnaupd -c -c\Description: -c Reverse communication interface for the Implicitly Restarted Arnoldi -c iteration. This subroutine computes approximations to a few eigenpairs -c of a linear operator "OP" with respect to a semi-inner product defined by -c a symmetric positive semi-definite real matrix B. B may be the identity -c matrix. NOTE: If the linear operator "OP" is real and symmetric -c with respect to the real positive semi-definite symmetric matrix B, -c i.e. B*OP = (OP')*B, then subroutine ssaupd should be used instead. -c -c The computed approximate eigenvalues are called Ritz values and -c the corresponding approximate eigenvectors are called Ritz vectors. -c -c igraphdnaupd is usually called iteratively to solve one of the -c following problems: -c -c Mode 1: A*x = lambda*x. -c ===> OP = A and B = I. -c -c Mode 2: A*x = lambda*M*x, M symmetric positive definite -c ===> OP = inv[M]*A and B = M. -c ===> (If M can be factored see remark 3 below) -c -c Mode 3: A*x = lambda*M*x, M symmetric semi-definite -c ===> OP = Real_Part{ inv[A - sigma*M]*M } and B = M. -c ===> shift-and-invert mode (in real arithmetic) -c If OP*x = amu*x, then -c amu = 1/2 * [ 1/(lambda-sigma) + 1/(lambda-conjg(sigma)) ]. -c Note: If sigma is real, i.e. imaginary part of sigma is zero; -c Real_Part{ inv[A - sigma*M]*M } == inv[A - sigma*M]*M -c amu == 1/(lambda-sigma). -c -c Mode 4: A*x = lambda*M*x, M symmetric semi-definite -c ===> OP = Imaginary_Part{ inv[A - sigma*M]*M } and B = M. -c ===> shift-and-invert mode (in real arithmetic) -c If OP*x = amu*x, then -c amu = 1/2i * [ 1/(lambda-sigma) - 1/(lambda-conjg(sigma)) ]. -c -c Both mode 3 and 4 give the same enhancement to eigenvalues close to -c the (complex) shift sigma. However, as lambda goes to infinity, -c the operator OP in mode 4 dampens the eigenvalues more strongly than -c does OP defined in mode 3. -c -c NOTE: The action of w <- inv[A - sigma*M]*v or w <- inv[M]*v -c should be accomplished either by a direct method -c using a sparse matrix factorization and solving -c -c [A - sigma*M]*w = v or M*w = v, -c -c or through an iterative method for solving these -c systems. If an iterative method is used, the -c convergence test must be more stringent than -c the accuracy requirements for the eigenvalue -c approximations. -c -c\Usage: -c call igraphdnaupd -c ( IDO, BMAT, N, WHICH, NEV, TOL, RESID, NCV, V, LDV, IPARAM, -c IPNTR, WORKD, WORKL, LWORKL, INFO ) -c -c\Arguments -c IDO Integer. (INPUT/OUTPUT) -c Reverse communication flag. IDO must be zero on the first -c call to igraphdnaupd. IDO will be set internally to -c indicate the type of operation to be performed. Control is -c then given back to the calling routine which has the -c responsibility to carry out the requested operation and call -c igraphdnaupd with the result. The operand is given in -c WORKD(IPNTR(1)), the result must be put in WORKD(IPNTR(2)). -c ------------------------------------------------------------- -c IDO = 0: first call to the reverse communication interface -c IDO = -1: compute Y = OP * X where -c IPNTR(1) is the pointer into WORKD for X, -c IPNTR(2) is the pointer into WORKD for Y. -c This is for the initialization phase to force the -c starting vector into the range of OP. -c IDO = 1: compute Y = OP * X where -c IPNTR(1) is the pointer into WORKD for X, -c IPNTR(2) is the pointer into WORKD for Y. -c In mode 3 and 4, the vector B * X is already -c available in WORKD(ipntr(3)). It does not -c need to be recomputed in forming OP * X. -c IDO = 2: compute Y = B * X where -c IPNTR(1) is the pointer into WORKD for X, -c IPNTR(2) is the pointer into WORKD for Y. -c IDO = 3: compute the IPARAM(8) real and imaginary parts -c of the shifts where INPTR(14) is the pointer -c into WORKL for placing the shifts. See Remark -c 5 below. -c IDO = 99: done -c ------------------------------------------------------------- -c -c BMAT Character*1. (INPUT) -c BMAT specifies the type of the matrix B that defines the -c semi-inner product for the operator OP. -c BMAT = 'I' -> standard eigenvalue problem A*x = lambda*x -c BMAT = 'G' -> generalized eigenvalue problem A*x = lambda*B*x -c -c N Integer. (INPUT) -c Dimension of the eigenproblem. -c -c WHICH Character*2. (INPUT) -c 'LM' -> want the NEV eigenvalues of largest magnitude. -c 'SM' -> want the NEV eigenvalues of smallest magnitude. -c 'LR' -> want the NEV eigenvalues of largest real part. -c 'SR' -> want the NEV eigenvalues of smallest real part. -c 'LI' -> want the NEV eigenvalues of largest imaginary part. -c 'SI' -> want the NEV eigenvalues of smallest imaginary part. -c -c NEV Integer. (INPUT) -c Number of eigenvalues of OP to be computed. 0 < NEV < N-1. -c -c TOL Double precision scalar. (INPUT) -c Stopping criterion: the relative accuracy of the Ritz value -c is considered acceptable if BOUNDS(I) .LE. TOL*ABS(RITZ(I)) -c where ABS(RITZ(I)) is the magnitude when RITZ(I) is complex. -c DEFAULT = DLAMCH('EPS') (machine precision as computed -c by the LAPACK auxiliary subroutine DLAMCH). -c -c RESID Double precision array of length N. (INPUT/OUTPUT) -c On INPUT: -c If INFO .EQ. 0, a random initial residual vector is used. -c If INFO .NE. 0, RESID contains the initial residual vector, -c possibly from a previous run. -c On OUTPUT: -c RESID contains the final residual vector. -c -c NCV Integer. (INPUT) -c Number of columns of the matrix V. NCV must satisfy the two -c inequalities 2 <= NCV-NEV and NCV <= N. -c This will indicate how many Arnoldi vectors are generated -c at each iteration. After the startup phase in which NEV -c Arnoldi vectors are generated, the algorithm generates -c approximately NCV-NEV Arnoldi vectors at each subsequent update -c iteration. Most of the cost in generating each Arnoldi vector is -c in the matrix-vector operation OP*x. -c NOTE: 2 <= NCV-NEV in order that complex conjugate pairs of Ritz -c values are kept together. (See remark 4 below) -c -c V Double precision array N by NCV. (OUTPUT) -c Contains the final set of Arnoldi basis vectors. -c -c LDV Integer. (INPUT) -c Leading dimension of V exactly as declared in the calling program. -c -c IPARAM Integer array of length 11. (INPUT/OUTPUT) -c IPARAM(1) = ISHIFT: method for selecting the implicit shifts. -c The shifts selected at each iteration are used to restart -c the Arnoldi iteration in an implicit fashion. -c ------------------------------------------------------------- -c ISHIFT = 0: the shifts are provided by the user via -c reverse communication. The real and imaginary -c parts of the NCV eigenvalues of the Hessenberg -c matrix H are returned in the part of the WORKL -c array corresponding to RITZR and RITZI. See remark -c 5 below. -c ISHIFT = 1: exact shifts with respect to the current -c Hessenberg matrix H. This is equivalent to -c restarting the iteration with a starting vector -c that is a linear combination of approximate Schur -c vectors associated with the "wanted" Ritz values. -c ------------------------------------------------------------- -c -c IPARAM(2) = No longer referenced. -c -c IPARAM(3) = MXITER -c On INPUT: maximum number of Arnoldi update iterations allowed. -c On OUTPUT: actual number of Arnoldi update iterations taken. -c -c IPARAM(4) = NB: blocksize to be used in the recurrence. -c The code currently works only for NB = 1. -c -c IPARAM(5) = NCONV: number of "converged" Ritz values. -c This represents the number of Ritz values that satisfy -c the convergence criterion. -c -c IPARAM(6) = IUPD -c No longer referenced. Implicit restarting is ALWAYS used. -c -c IPARAM(7) = MODE -c On INPUT determines what type of eigenproblem is being solved. -c Must be 1,2,3,4; See under \Description of igraphdnaupd for the -c four modes available. -c -c IPARAM(8) = NP -c When ido = 3 and the user provides shifts through reverse -c communication (IPARAM(1)=0), igraphdnaupd returns NP, the number -c of shifts the user is to provide. 0 < NP <=NCV-NEV. See Remark -c 5 below. -c -c IPARAM(9) = NUMOP, IPARAM(10) = NUMOPB, IPARAM(11) = NUMREO, -c OUTPUT: NUMOP = total number of OP*x operations, -c NUMOPB = total number of B*x operations if BMAT='G', -c NUMREO = total number of steps of re-orthogonalization. -c -c IPNTR Integer array of length 14. (OUTPUT) -c Pointer to mark the starting locations in the WORKD and WORKL -c arrays for matrices/vectors used by the Arnoldi iteration. -c ------------------------------------------------------------- -c IPNTR(1): pointer to the current operand vector X in WORKD. -c IPNTR(2): pointer to the current result vector Y in WORKD. -c IPNTR(3): pointer to the vector B * X in WORKD when used in -c the shift-and-invert mode. -c IPNTR(4): pointer to the next available location in WORKL -c that is untouched by the program. -c IPNTR(5): pointer to the NCV by NCV upper Hessenberg matrix -c H in WORKL. -c IPNTR(6): pointer to the real part of the ritz value array -c RITZR in WORKL. -c IPNTR(7): pointer to the imaginary part of the ritz value array -c RITZI in WORKL. -c IPNTR(8): pointer to the Ritz estimates in array WORKL associated -c with the Ritz values located in RITZR and RITZI in WORKL. -c -c IPNTR(14): pointer to the NP shifts in WORKL. See Remark 5 below. -c -c Note: IPNTR(9:13) is only referenced by igraphdneupd. See Remark 2 below. -c -c IPNTR(9): pointer to the real part of the NCV RITZ values of the -c original system. -c IPNTR(10): pointer to the imaginary part of the NCV RITZ values of -c the original system. -c IPNTR(11): pointer to the NCV corresponding error bounds. -c IPNTR(12): pointer to the NCV by NCV upper quasi-triangular -c Schur matrix for H. -c IPNTR(13): pointer to the NCV by NCV matrix of eigenvectors -c of the upper Hessenberg matrix H. Only referenced by -c igraphdneupd if RVEC = .TRUE. See Remark 2 below. -c ------------------------------------------------------------- -c -c WORKD Double precision work array of length 3*N. (REVERSE COMMUNICATION) -c Distributed array to be used in the basic Arnoldi iteration -c for reverse communication. The user should not use WORKD -c as temporary workspace during the iteration. Upon termination -c WORKD(1:N) contains B*RESID(1:N). If an invariant subspace -c associated with the converged Ritz values is desired, see remark -c 2 below, subroutine igraphdneupd uses this output. -c See Data Distribution Note below. -c -c WORKL Double precision work array of length LWORKL. (OUTPUT/WORKSPACE) -c Private (replicated) array on each PE or array allocated on -c the front end. See Data Distribution Note below. -c -c LWORKL Integer. (INPUT) -c LWORKL must be at least 3*NCV**2 + 6*NCV. -c -c INFO Integer. (INPUT/OUTPUT) -c If INFO .EQ. 0, a randomly initial residual vector is used. -c If INFO .NE. 0, RESID contains the initial residual vector, -c possibly from a previous run. -c Error flag on output. -c = 0: Normal exit. -c = 1: Maximum number of iterations taken. -c All possible eigenvalues of OP has been found. IPARAM(5) -c returns the number of wanted converged Ritz values. -c = 2: No longer an informational error. Deprecated starting -c with release 2 of ARPACK. -c = 3: No shifts could be applied during a cycle of the -c Implicitly restarted Arnoldi iteration. One possibility -c is to increase the size of NCV relative to NEV. -c See remark 4 below. -c = -1: N must be positive. -c = -2: NEV must be positive. -c = -3: NCV-NEV >= 2 and less than or equal to N. -c = -4: The maximum number of Arnoldi update iteration -c must be greater than zero. -c = -5: WHICH must be one of 'LM', 'SM', 'LR', 'SR', 'LI', 'SI' -c = -6: BMAT must be one of 'I' or 'G'. -c = -7: Length of private work array is not sufficient. -c = -8: Error return from LAPACK eigenvalue calculation; -c = -9: Starting vector is zero. -c = -10: IPARAM(7) must be 1,2,3,4. -c = -11: IPARAM(7) = 1 and BMAT = 'G' are incompatable. -c = -12: IPARAM(1) must be equal to 0 or 1. -c = -9999: Could not build an Arnoldi factorization. -c IPARAM(5) returns the size of the current Arnoldi -c factorization. -c -c\Remarks -c 1. The computed Ritz values are approximate eigenvalues of OP. The -c selection of WHICH should be made with this in mind when -c Mode = 3 and 4. After convergence, approximate eigenvalues of the -c original problem may be obtained with the ARPACK subroutine igraphdneupd. -c -c 2. If a basis for the invariant subspace corresponding to the converged Ritz -c values is needed, the user must call igraphdneupd immediately following -c completion of igraphdnaupd. This is new starting with release 2 of ARPACK. -c -c 3. If M can be factored into a Cholesky factorization M = LL' -c then Mode = 2 should not be selected. Instead one should use -c Mode = 1 with OP = inv(L)*A*inv(L'). Appropriate triangular -c linear systems should be solved with L and L' rather -c than computing inverses. After convergence, an approximate -c eigenvector z of the original problem is recovered by solving -c L'z = x where x is a Ritz vector of OP. -c -c 4. At present there is no a-priori analysis to guide the selection -c of NCV relative to NEV. The only formal requrement is that NCV > NEV + 2. -c However, it is recommended that NCV .ge. 2*NEV+1. If many problems of -c the same type are to be solved, one should experiment with increasing -c NCV while keeping NEV fixed for a given test problem. This will -c usually decrease the required number of OP*x operations but it -c also increases the work and storage required to maintain the orthogonal -c basis vectors. The optimal "cross-over" with respect to CPU time -c is problem dependent and must be determined empirically. -c See Chapter 8 of Reference 2 for further information. -c -c 5. When IPARAM(1) = 0, and IDO = 3, the user needs to provide the -c NP = IPARAM(8) real and imaginary parts of the shifts in locations -c real part imaginary part -c ----------------------- -------------- -c 1 WORKL(IPNTR(14)) WORKL(IPNTR(14)+NP) -c 2 WORKL(IPNTR(14)+1) WORKL(IPNTR(14)+NP+1) -c . . -c . . -c . . -c NP WORKL(IPNTR(14)+NP-1) WORKL(IPNTR(14)+2*NP-1). -c -c Only complex conjugate pairs of shifts may be applied and the pairs -c must be placed in consecutive locations. The real part of the -c eigenvalues of the current upper Hessenberg matrix are located in -c WORKL(IPNTR(6)) through WORKL(IPNTR(6)+NCV-1) and the imaginary part -c in WORKL(IPNTR(7)) through WORKL(IPNTR(7)+NCV-1). They are ordered -c according to the order defined by WHICH. The complex conjugate -c pairs are kept together and the associated Ritz estimates are located in -c WORKL(IPNTR(8)), WORKL(IPNTR(8)+1), ... , WORKL(IPNTR(8)+NCV-1). -c -c----------------------------------------------------------------------- -c -c\Data Distribution Note: -c -c Fortran-D syntax: -c ================ -c Double precision resid(n), v(ldv,ncv), workd(3*n), workl(lworkl) -c decompose d1(n), d2(n,ncv) -c align resid(i) with d1(i) -c align v(i,j) with d2(i,j) -c align workd(i) with d1(i) range (1:n) -c align workd(i) with d1(i-n) range (n+1:2*n) -c align workd(i) with d1(i-2*n) range (2*n+1:3*n) -c distribute d1(block), d2(block,:) -c replicated workl(lworkl) -c -c Cray MPP syntax: -c =============== -c Double precision resid(n), v(ldv,ncv), workd(n,3), workl(lworkl) -c shared resid(block), v(block,:), workd(block,:) -c replicated workl(lworkl) -c -c CM2/CM5 syntax: -c ============== -c -c----------------------------------------------------------------------- -c -c include 'ex-nonsym.doc' -c -c----------------------------------------------------------------------- -c -c\BeginLib -c -c\Local variables: -c xxxxxx real -c -c\References: -c 1. D.C. Sorensen, "Implicit Application of Polynomial Filters in -c a k-Step Arnoldi Method", SIAM J. Matr. Anal. Apps., 13 (1992), -c pp 357-385. -c 2. R.B. Lehoucq, "Analysis and Implementation of an Implicitly -c Restarted Arnoldi Iteration", Rice University Technical Report -c TR95-13, Department of Computational and Applied Mathematics. -c 3. B.N. Parlett & Y. Saad, "Complex Shift and Invert Strategies for -c Real Matrices", Linear Algebra and its Applications, vol 88/89, -c pp 575-595, (1987). -c -c\Routines called: -c igraphdnaup2 ARPACK routine that implements the Implicitly Restarted -c Arnoldi Iteration. -c igraphivout ARPACK utility routine that prints integers. -c igraphsecond ARPACK utility routine for timing. -c igraphdvout ARPACK utility routine that prints vectors. -c dlamch LAPACK routine that determines machine constants. -c -c\Author -c Danny Sorensen Phuong Vu -c Richard Lehoucq CRPC / Rice University -c Dept. of Computational & Houston, Texas -c Applied Mathematics -c Rice University -c Houston, Texas -c -c\Revision history: -c 12/16/93: Version '1.1' -c -c\SCCS Information: @(#) -c FILE: naupd.F SID: 2.5 DATE OF SID: 8/27/96 RELEASE: 2 -c -c\Remarks -c -c\EndLib -c -c----------------------------------------------------------------------- -c - subroutine igraphdnaupd - & ( ido, bmat, n, which, nev, tol, resid, ncv, v, ldv, iparam, - & ipntr, workd, workl, lworkl, info ) -c -c %----------------------------------------------------% -c | Include files for debugging and timing information | -c %----------------------------------------------------% -c - include 'debug.h' - include 'stat.h' -c -c %------------------% -c | Scalar Arguments | -c %------------------% -c - character bmat*1, which*2 - integer ido, info, ldv, lworkl, n, ncv, nev - Double precision - & tol -c -c %-----------------% -c | Array Arguments | -c %-----------------% -c - integer iparam(11), ipntr(14) - Double precision - & resid(n), v(ldv,ncv), workd(3*n), workl(lworkl) -c -c %------------% -c | Parameters | -c %------------% -c - Double precision - & one, zero - parameter (one = 1.0D+0, zero = 0.0D+0) -c -c %---------------% -c | Local Scalars | -c %---------------% -c - integer bounds, ierr, ih, iq, ishift, iupd, iw, - & ldh, ldq, levec, mode, msglvl, mxiter, nb, - & nev0, next, np, ritzi, ritzr, j - save bounds, ih, iq, ishift, iupd, iw, ldh, ldq, - & levec, mode, msglvl, mxiter, nb, nev0, next, - & np, ritzi, ritzr -c -c %----------------------% -c | External Subroutines | -c %----------------------% -c - external igraphdnaup2, igraphdvout, igraphivout, - & igraphsecond, igraphdstatn -c -c %--------------------% -c | External Functions | -c %--------------------% -c - Double precision - & dlamch - external dlamch -c -c %-----------------------% -c | Executable Statements | -c %-----------------------% -c - if (ido .eq. 0) then -c -c %-------------------------------% -c | Initialize timing statistics | -c | & message level for debugging | -c %-------------------------------% -c - call igraphdstatn - call igraphsecond (t0) - msglvl = mnaupd -c -c %----------------% -c | Error checking | -c %----------------% -c - ierr = 0 - ishift = iparam(1) - levec = iparam(2) - mxiter = iparam(3) - nb = iparam(4) -c -c %--------------------------------------------% -c | Revision 2 performs only implicit restart. | -c %--------------------------------------------% -c - iupd = 1 - mode = iparam(7) -c - if (n .le. 0) then - ierr = -1 - else if (nev .le. 0) then - ierr = -2 - else if (ncv .le. nev+1 .or. ncv .gt. n) then - ierr = -3 - else if (mxiter .le. 0) then - ierr = -4 - else if (which .ne. 'LM' .and. - & which .ne. 'SM' .and. - & which .ne. 'LR' .and. - & which .ne. 'SR' .and. - & which .ne. 'LI' .and. - & which .ne. 'SI') then - ierr = -5 - else if (bmat .ne. 'I' .and. bmat .ne. 'G') then - ierr = -6 - else if (lworkl .lt. 3*ncv**2 + 6*ncv) then - ierr = -7 - else if (mode .lt. 1 .or. mode .gt. 5) then - ierr = -10 - else if (mode .eq. 1 .and. bmat .eq. 'G') then - ierr = -11 - else if (ishift .lt. 0 .or. ishift .gt. 1) then - ierr = -12 - end if -c -c %------------% -c | Error Exit | -c %------------% -c - if (ierr .ne. 0) then - info = ierr - ido = 99 - go to 9000 - end if -c -c %------------------------% -c | Set default parameters | -c %------------------------% -c - if (nb .le. 0) nb = 1 - if (tol .le. zero) tol = dlamch('EpsMach') -c -c %----------------------------------------------% -c | NP is the number of additional steps to | -c | extend the length NEV Lanczos factorization. | -c | NEV0 is the local variable designating the | -c | size of the invariant subspace desired. | -c %----------------------------------------------% -c - np = ncv - nev - nev0 = nev -c -c %-----------------------------% -c | Zero out internal workspace | -c %-----------------------------% -c - do 10 j = 1, 3*ncv**2 + 6*ncv - workl(j) = zero - 10 continue -c -c %-------------------------------------------------------------% -c | Pointer into WORKL for address of H, RITZ, BOUNDS, Q | -c | etc... and the remaining workspace. | -c | Also update pointer to be used on output. | -c | Memory is laid out as follows: | -c | workl(1:ncv*ncv) := generated Hessenberg matrix | -c | workl(ncv*ncv+1:ncv*ncv+2*ncv) := real and imaginary | -c | parts of ritz values | -c | workl(ncv*ncv+2*ncv+1:ncv*ncv+3*ncv) := error bounds | -c | workl(ncv*ncv+3*ncv+1:2*ncv*ncv+3*ncv) := rotation matrix Q | -c | workl(2*ncv*ncv+3*ncv+1:3*ncv*ncv+6*ncv) := workspace | -c | The final workspace is needed by subroutine igraphdneigh called | -c | by igraphdnaup2. Subroutine igraphdneigh calls LAPACK routines for | -c | calculating eigenvalues and the last row of the eigenvector | -c | matrix. | -c %-------------------------------------------------------------% -c - ldh = ncv - ldq = ncv - ih = 1 - ritzr = ih + ldh*ncv - ritzi = ritzr + ncv - bounds = ritzi + ncv - iq = bounds + ncv - iw = iq + ldq*ncv - next = iw + ncv**2 + 3*ncv -c - ipntr(4) = next - ipntr(5) = ih - ipntr(6) = ritzr - ipntr(7) = ritzi - ipntr(8) = bounds - ipntr(14) = iw -c - end if -c -c %-------------------------------------------------------% -c | Carry out the Implicitly restarted Arnoldi Iteration. | -c %-------------------------------------------------------% -c - call igraphdnaup2 - & ( ido, bmat, n, which, nev0, np, tol, resid, mode, iupd, - & ishift, mxiter, v, ldv, workl(ih), ldh, workl(ritzr), - & workl(ritzi), workl(bounds), workl(iq), ldq, workl(iw), - & ipntr, workd, info ) -c -c %--------------------------------------------------% -c | ido .ne. 99 implies use of reverse communication | -c | to compute operations involving OP or shifts. | -c %--------------------------------------------------% -c - if (ido .eq. 3) iparam(8) = np - if (ido .ne. 99) go to 9000 -c - iparam(3) = mxiter - iparam(5) = np - iparam(9) = nopx - iparam(10) = nbx - iparam(11) = nrorth -c -c %------------------------------------% -c | Exit if there was an informational | -c | error within igraphdnaup2. | -c %------------------------------------% -c - if (info .lt. 0) go to 9000 - if (info .eq. 2) info = 3 -c - if (msglvl .gt. 0) then - call igraphivout (logfil, 1, [mxiter], ndigit, - & '_naupd: Number of update iterations taken') - call igraphivout (logfil, 1, [np], ndigit, - & '_naupd: Number of wanted "converged" Ritz values') - call igraphdvout (logfil, np, workl(ritzr), ndigit, - & '_naupd: Real part of the final Ritz values') - call igraphdvout (logfil, np, workl(ritzi), ndigit, - & '_naupd: Imaginary part of the final Ritz values') - call igraphdvout (logfil, np, workl(bounds), ndigit, - & '_naupd: Associated Ritz estimates') - end if -c - call igraphsecond (t1) - tnaupd = t1 - t0 -c -c - 9000 continue -c - return -c -c %---------------% -c | End of igraphdnaupd | -c %---------------% -c - end diff --git a/src/rigraph/vendor/arpack/dnconv.f b/src/rigraph/vendor/arpack/dnconv.f deleted file mode 100644 index 4735159..0000000 --- a/src/rigraph/vendor/arpack/dnconv.f +++ /dev/null @@ -1,146 +0,0 @@ -c----------------------------------------------------------------------- -c\BeginDoc -c -c\Name: igraphdnconv -c -c\Description: -c Convergence testing for the nonsymmetric Arnoldi eigenvalue routine. -c -c\Usage: -c call igraphdnconv -c ( N, RITZR, RITZI, BOUNDS, TOL, NCONV ) -c -c\Arguments -c N Integer. (INPUT) -c Number of Ritz values to check for convergence. -c -c RITZR, Double precision arrays of length N. (INPUT) -c RITZI Real and imaginary parts of the Ritz values to be checked -c for convergence. - -c BOUNDS Double precision array of length N. (INPUT) -c Ritz estimates for the Ritz values in RITZR and RITZI. -c -c TOL Double precision scalar. (INPUT) -c Desired backward error for a Ritz value to be considered -c "converged". -c -c NCONV Integer scalar. (OUTPUT) -c Number of "converged" Ritz values. -c -c\EndDoc -c -c----------------------------------------------------------------------- -c -c\BeginLib -c -c\Local variables: -c xxxxxx real -c -c\Routines called: -c igraphsecond ARPACK utility routine for timing. -c dlamch LAPACK routine that determines machine constants. -c dlapy2 LAPACK routine to compute sqrt(x**2+y**2) carefully. -c -c\Author -c Danny Sorensen Phuong Vu -c Richard Lehoucq CRPC / Rice University -c Dept. of Computational & Houston, Texas -c Applied Mathematics -c Rice University -c Houston, Texas -c -c\Revision history: -c xx/xx/92: Version ' 2.1' -c -c\SCCS Information: @(#) -c FILE: nconv.F SID: 2.3 DATE OF SID: 4/20/96 RELEASE: 2 -c -c\Remarks -c 1. xxxx -c -c\EndLib -c -c----------------------------------------------------------------------- -c - subroutine igraphdnconv (n, ritzr, ritzi, bounds, tol, nconv) -c -c %----------------------------------------------------% -c | Include files for debugging and timing information | -c %----------------------------------------------------% -c - include 'debug.h' - include 'stat.h' -c -c %------------------% -c | Scalar Arguments | -c %------------------% -c - integer n, nconv - Double precision - & tol -c -c %-----------------% -c | Array Arguments | -c %-----------------% - - Double precision - & ritzr(n), ritzi(n), bounds(n) -c -c %---------------% -c | Local Scalars | -c %---------------% -c - integer i - Double precision - & temp, eps23 -c -c %--------------------% -c | External Functions | -c %--------------------% -c - Double precision - & dlapy2, dlamch - external dlapy2, dlamch - -c %-----------------------% -c | Executable Statements | -c %-----------------------% -c -c %-------------------------------------------------------------% -c | Convergence test: unlike in the symmetric code, I am not | -c | using things like refined error bounds and gap condition | -c | because I don't know the exact equivalent concept. | -c | | -c | Instead the i-th Ritz value is considered "converged" when: | -c | | -c | bounds(i) .le. ( TOL * | ritz | ) | -c | | -c | for some appropriate choice of norm. | -c %-------------------------------------------------------------% -c - call igraphsecond (t0) -c -c %---------------------------------% -c | Get machine dependent constant. | -c %---------------------------------% -c - eps23 = dlamch('Epsilon-Machine') - eps23 = eps23**(2.0D+0 / 3.0D+0) -c - nconv = 0 - do 20 i = 1, n - temp = max( eps23, dlapy2( ritzr(i), ritzi(i) ) ) - if (bounds(i) .le. tol*temp) nconv = nconv + 1 - 20 continue -c - call igraphsecond (t1) - tnconv = tnconv + (t1 - t0) -c - return -c -c %---------------% -c | End of igraphdnconv | -c %---------------% -c - end diff --git a/src/rigraph/vendor/arpack/dneigh.f b/src/rigraph/vendor/arpack/dneigh.f deleted file mode 100644 index 53c7c89..0000000 --- a/src/rigraph/vendor/arpack/dneigh.f +++ /dev/null @@ -1,315 +0,0 @@ -c----------------------------------------------------------------------- -c\BeginDoc -c -c\Name: igraphdneigh -c -c\Description: -c Compute the eigenvalues of the current upper Hessenberg matrix -c and the corresponding Ritz estimates given the current residual norm. -c -c\Usage: -c call igraphdneigh -c ( RNORM, N, H, LDH, RITZR, RITZI, BOUNDS, Q, LDQ, WORKL, IERR ) -c -c\Arguments -c RNORM Double precision scalar. (INPUT) -c Residual norm corresponding to the current upper Hessenberg -c matrix H. -c -c N Integer. (INPUT) -c Size of the matrix H. -c -c H Double precision N by N array. (INPUT) -c H contains the current upper Hessenberg matrix. -c -c LDH Integer. (INPUT) -c Leading dimension of H exactly as declared in the calling -c program. -c -c RITZR, Double precision arrays of length N. (OUTPUT) -c RITZI On output, RITZR(1:N) (resp. RITZI(1:N)) contains the real -c (respectively imaginary) parts of the eigenvalues of H. -c -c BOUNDS Double precision array of length N. (OUTPUT) -c On output, BOUNDS contains the Ritz estimates associated with -c the eigenvalues RITZR and RITZI. This is equal to RNORM -c times the last components of the eigenvectors corresponding -c to the eigenvalues in RITZR and RITZI. -c -c Q Double precision N by N array. (WORKSPACE) -c Workspace needed to store the eigenvectors of H. -c -c LDQ Integer. (INPUT) -c Leading dimension of Q exactly as declared in the calling -c program. -c -c WORKL Double precision work array of length N**2 + 3*N. (WORKSPACE) -c Private (replicated) array on each PE or array allocated on -c the front end. This is needed to keep the full Schur form -c of H and also in the calculation of the eigenvectors of H. -c -c IERR Integer. (OUTPUT) -c Error exit flag from igraphdlaqrb or dtrevc. -c -c\EndDoc -c -c----------------------------------------------------------------------- -c -c\BeginLib -c -c\Local variables: -c xxxxxx real -c -c\Routines called: -c igraphdlaqrb ARPACK routine to compute the real Schur form of an -c upper Hessenberg matrix and last row of the Schur vectors. -c igraphsecond ARPACK utility routine for timing. -c igraphdmout ARPACK utility routine that prints matrices -c igraphdvout ARPACK utility routine that prints vectors. -c dlacpy LAPACK matrix copy routine. -c dlapy2 LAPACK routine to compute sqrt(x**2+y**2) carefully. -c dtrevc LAPACK routine to compute the eigenvectors of a matrix -c in upper quasi-triangular form -c dgemv Level 2 BLAS routine for matrix vector multiplication. -c dcopy Level 1 BLAS that copies one vector to another . -c dnrm2 Level 1 BLAS that computes the norm of a vector. -c dscal Level 1 BLAS that scales a vector. -c -c -c\Author -c Danny Sorensen Phuong Vu -c Richard Lehoucq CRPC / Rice University -c Dept. of Computational & Houston, Texas -c Applied Mathematics -c Rice University -c Houston, Texas -c -c\Revision history: -c xx/xx/92: Version ' 2.1' -c -c\SCCS Information: @(#) -c FILE: neigh.F SID: 2.3 DATE OF SID: 4/20/96 RELEASE: 2 -c -c\Remarks -c None -c -c\EndLib -c -c----------------------------------------------------------------------- -c - subroutine igraphdneigh (rnorm, n, h, ldh, ritzr, ritzi, bounds, - & q, ldq, workl, ierr) -c -c %----------------------------------------------------% -c | Include files for debugging and timing information | -c %----------------------------------------------------% -c - include 'debug.h' - include 'stat.h' -c -c %------------------% -c | Scalar Arguments | -c %------------------% -c - integer ierr, n, ldh, ldq - Double precision - & rnorm -c -c %-----------------% -c | Array Arguments | -c %-----------------% -c - Double precision - & bounds(n), h(ldh,n), q(ldq,n), ritzi(n), ritzr(n), - & workl(n*(n+3)) -c -c %------------% -c | Parameters | -c %------------% -c - Double precision - & one, zero - parameter (one = 1.0D+0, zero = 0.0D+0) -c -c %------------------------% -c | Local Scalars & Arrays | -c %------------------------% -c - logical select(1) - integer i, iconj, msglvl - Double precision - & temp, vl(1) -c -c %----------------------% -c | External Subroutines | -c %----------------------% -c - external dcopy, dlacpy, igraphdlaqrb, dtrevc, igraphdvout, - & igraphsecond -c -c %--------------------% -c | External Functions | -c %--------------------% -c - Double precision - & dlapy2, dnrm2 - external dlapy2, dnrm2 -c -c %---------------------% -c | Intrinsic Functions | -c %---------------------% -c - intrinsic abs -c -c %-----------------------% -c | Executable Statements | -c %-----------------------% -c -c -c %-------------------------------% -c | Initialize timing statistics | -c | & message level for debugging | -c %-------------------------------% -c - call igraphsecond (t0) - msglvl = mneigh -c - if (msglvl .gt. 2) then - call igraphdmout (logfil, n, n, h, ldh, ndigit, - & '_neigh: Entering upper Hessenberg matrix H ') - end if -c -c %-----------------------------------------------------------% -c | 1. Compute the eigenvalues, the last components of the | -c | corresponding Schur vectors and the full Schur form T | -c | of the current upper Hessenberg matrix H. | -c | igraphdlaqrb returns the full Schur form of H in WORKL(1:N**2) | -c | and the last components of the Schur vectors in BOUNDS. | -c %-----------------------------------------------------------% -c - call dlacpy ('All', n, n, h, ldh, workl, n) - call igraphdlaqrb (.true., n, 1, n, workl, n, ritzr, ritzi, - & bounds, ierr) - if (ierr .ne. 0) go to 9000 -c - if (msglvl .gt. 1) then - call igraphdvout (logfil, n, bounds, ndigit, - & '_neigh: last row of the Schur matrix for H') - end if -c -c %-----------------------------------------------------------% -c | 2. Compute the eigenvectors of the full Schur form T and | -c | apply the last components of the Schur vectors to get | -c | the last components of the corresponding eigenvectors. | -c | Remember that if the i-th and (i+1)-st eigenvalues are | -c | complex conjugate pairs, then the real & imaginary part | -c | of the eigenvector components are split across adjacent | -c | columns of Q. | -c %-----------------------------------------------------------% -c - call dtrevc ('R', 'A', select, n, workl, n, vl, n, q, ldq, - & n, n, workl(n*n+1), ierr) -c - if (ierr .ne. 0) go to 9000 -c -c %------------------------------------------------% -c | Scale the returning eigenvectors so that their | -c | euclidean norms are all one. LAPACK subroutine | -c | dtrevc returns each eigenvector normalized so | -c | that the element of largest magnitude has | -c | magnitude 1; here the magnitude of a complex | -c | number (x,y) is taken to be |x| + |y|. | -c %------------------------------------------------% -c - iconj = 0 - do 10 i=1, n - if ( abs( ritzi(i) ) .le. zero ) then -c -c %----------------------% -c | Real eigenvalue case | -c %----------------------% -c - temp = dnrm2( n, q(1,i), 1 ) - call dscal ( n, one / temp, q(1,i), 1 ) - else -c -c %-------------------------------------------% -c | Complex conjugate pair case. Note that | -c | since the real and imaginary part of | -c | the eigenvector are stored in consecutive | -c | columns, we further normalize by the | -c | square root of two. | -c %-------------------------------------------% -c - if (iconj .eq. 0) then - temp = dlapy2( dnrm2( n, q(1,i), 1 ), - & dnrm2( n, q(1,i+1), 1 ) ) - call dscal ( n, one / temp, q(1,i), 1 ) - call dscal ( n, one / temp, q(1,i+1), 1 ) - iconj = 1 - else - iconj = 0 - end if - end if - 10 continue -c - call dgemv ('T', n, n, one, q, ldq, bounds, 1, zero, workl, 1) -c - if (msglvl .gt. 1) then - call igraphdvout (logfil, n, workl, ndigit, - & '_neigh: Last row of the eigenvector matrix for H') - end if -c -c %----------------------------% -c | Compute the Ritz estimates | -c %----------------------------% -c - iconj = 0 - do 20 i = 1, n - if ( abs( ritzi(i) ) .le. zero ) then -c -c %----------------------% -c | Real eigenvalue case | -c %----------------------% -c - bounds(i) = rnorm * abs( workl(i) ) - else -c -c %-------------------------------------------% -c | Complex conjugate pair case. Note that | -c | since the real and imaginary part of | -c | the eigenvector are stored in consecutive | -c | columns, we need to take the magnitude | -c | of the last components of the two vectors | -c %-------------------------------------------% -c - if (iconj .eq. 0) then - bounds(i) = rnorm * dlapy2( workl(i), workl(i+1) ) - bounds(i+1) = bounds(i) - iconj = 1 - else - iconj = 0 - end if - end if - 20 continue -c - if (msglvl .gt. 2) then - call igraphdvout (logfil, n, ritzr, ndigit, - & '_neigh: Real part of the eigenvalues of H') - call igraphdvout (logfil, n, ritzi, ndigit, - & '_neigh: Imaginary part of the eigenvalues of H') - call igraphdvout (logfil, n, bounds, ndigit, - & '_neigh: Ritz estimates for the eigenvalues of H') - end if -c - call igraphsecond (t1) - tneigh = tneigh + (t1 - t0) -c - 9000 continue - return -c -c %---------------% -c | End of igraphdneigh | -c %---------------% -c - end diff --git a/src/rigraph/vendor/arpack/dneupd.f b/src/rigraph/vendor/arpack/dneupd.f deleted file mode 100644 index 92b4bc3..0000000 --- a/src/rigraph/vendor/arpack/dneupd.f +++ /dev/null @@ -1,1044 +0,0 @@ -c\BeginDoc -c -c\Name: igraphdneupd -c -c\Description: -c -c This subroutine returns the converged approximations to eigenvalues -c of A*z = lambda*B*z and (optionally): -c -c (1) The corresponding approximate eigenvectors; -c -c (2) An orthonormal basis for the associated approximate -c invariant subspace; -c -c (3) Both. -c -c There is negligible additional cost to obtain eigenvectors. An orthonormal -c basis is always computed. There is an additional storage cost of n*nev -c if both are requested (in this case a separate array Z must be supplied). -c -c The approximate eigenvalues and eigenvectors of A*z = lambda*B*z -c are derived from approximate eigenvalues and eigenvectors of -c of the linear operator OP prescribed by the MODE selection in the -c call to DNAUPD. DNAUPD must be called before this routine is called. -c These approximate eigenvalues and vectors are commonly called Ritz -c values and Ritz vectors respectively. They are referred to as such -c in the comments that follow. The computed orthonormal basis for the -c invariant subspace corresponding to these Ritz values is referred to as a -c Schur basis. -c -c See documentation in the header of the subroutine DNAUPD for -c definition of OP as well as other terms and the relation of computed -c Ritz values and Ritz vectors of OP with respect to the given problem -c A*z = lambda*B*z. For a brief description, see definitions of -c IPARAM(7), MODE and WHICH in the documentation of DNAUPD. -c -c\Usage: -c call igraphdneupd -c ( RVEC, HOWMNY, SELECT, DR, DI, Z, LDZ, SIGMAR, SIGMAI, WORKEV, BMAT, -c N, WHICH, NEV, TOL, RESID, NCV, V, LDV, IPARAM, IPNTR, WORKD, WORKL, -c LWORKL, INFO ) -c -c\Arguments: -c RVEC LOGICAL (INPUT) -c Specifies whether a basis for the invariant subspace corresponding -c to the converged Ritz value approximations for the eigenproblem -c A*z = lambda*B*z is computed. -c -c RVEC = .FALSE. Compute Ritz values only. -c -c RVEC = .TRUE. Compute the Ritz vectors or Schur vectors. -c See Remarks below. -c -c HOWMNY Character*1 (INPUT) -c Specifies the form of the basis for the invariant subspace -c corresponding to the converged Ritz values that is to be computed. -c -c = 'A': Compute NEV Ritz vectors; -c = 'P': Compute NEV Schur vectors; -c = 'S': compute some of the Ritz vectors, specified -c by the logical array SELECT. -c -c SELECT Logical array of dimension NCV. (INPUT) -c If HOWMNY = 'S', SELECT specifies the Ritz vectors to be -c computed. To select the Ritz vector corresponding to a -c Ritz value (DR(j), DI(j)), SELECT(j) must be set to .TRUE.. -c If HOWMNY = 'A' or 'P', SELECT is used as internal workspace. -c -c DR Double precision array of dimension NEV+1. (OUTPUT) -c If IPARAM(7) = 1,2 or 3 and SIGMAI=0.0 then on exit: DR contains -c the real part of the Ritz approximations to the eigenvalues of -c A*z = lambda*B*z. -c If IPARAM(7) = 3, 4 and SIGMAI is not equal to zero, then on exit: -c DR contains the real part of the Ritz values of OP computed by -c DNAUPD. A further computation must be performed by the user -c to transform the Ritz values computed for OP by DNAUPD to those -c of the original system A*z = lambda*B*z. See remark 3 below. -c -c DI Double precision array of dimension NEV+1. (OUTPUT) -c On exit, DI contains the imaginary part of the Ritz value -c approximations to the eigenvalues of A*z = lambda*B*z associated -c with DR. -c -c NOTE: When Ritz values are complex, they will come in complex -c conjugate pairs. If eigenvectors are requested, the -c corresponding Ritz vectors will also come in conjugate -c pairs and the real and imaginary parts of these are -c represented in two consecutive columns of the array Z -c (see below). -c -c Z Double precision N by NEV+1 array if RVEC = .TRUE. and HOWMNY = 'A'. (OUTPUT) -c On exit, if RVEC = .TRUE. and HOWMNY = 'A', then the columns of -c Z represent approximate eigenvectors (Ritz vectors) corresponding -c to the NCONV=IPARAM(5) Ritz values for eigensystem -c A*z = lambda*B*z. -c -c The complex Ritz vector associated with the Ritz value -c with positive imaginary part is stored in two consecutive -c columns. The first column holds the real part of the Ritz -c vector and the igraphsecond column holds the imaginary part. The -c Ritz vector associated with the Ritz value with negative -c imaginary part is simply the complex conjugate of the Ritz vector -c associated with the positive imaginary part. -c -c If RVEC = .FALSE. or HOWMNY = 'P', then Z is not referenced. -c -c NOTE: If if RVEC = .TRUE. and a Schur basis is not required, -c the array Z may be set equal to first NEV+1 columns of the Arnoldi -c basis array V computed by DNAUPD. In this case the Arnoldi basis -c will be destroyed and overwritten with the eigenvector basis. -c -c LDZ Integer. (INPUT) -c The leading dimension of the array Z. If Ritz vectors are -c desired, then LDZ >= max( 1, N ). In any case, LDZ >= 1. -c -c SIGMAR Double precision (INPUT) -c If IPARAM(7) = 3 or 4, represents the real part of the shift. -c Not referenced if IPARAM(7) = 1 or 2. -c -c SIGMAI Double precision (INPUT) -c If IPARAM(7) = 3 or 4, represents the imaginary part of the shift. -c Not referenced if IPARAM(7) = 1 or 2. See remark 3 below. -c -c WORKEV Double precision work array of dimension 3*NCV. (WORKSPACE) -c -c **** The remaining arguments MUST be the same as for the **** -c **** call to DNAUPD that was just completed. **** -c -c NOTE: The remaining arguments -c -c BMAT, N, WHICH, NEV, TOL, RESID, NCV, V, LDV, IPARAM, IPNTR, -c WORKD, WORKL, LWORKL, INFO -c -c must be passed directly to DNEUPD following the last call -c to DNAUPD. These arguments MUST NOT BE MODIFIED between -c the the last call to DNAUPD and the call to DNEUPD. -c -c Three of these parameters (V, WORKL, INFO) are also output parameters: -c -c V Double precision N by NCV array. (INPUT/OUTPUT) -c -c Upon INPUT: the NCV columns of V contain the Arnoldi basis -c vectors for OP as constructed by DNAUPD . -c -c Upon OUTPUT: If RVEC = .TRUE. the first NCONV=IPARAM(5) columns -c contain approximate Schur vectors that span the -c desired invariant subspace. See Remark 2 below. -c -c NOTE: If the array Z has been set equal to first NEV+1 columns -c of the array V and RVEC=.TRUE. and HOWMNY= 'A', then the -c Arnoldi basis held by V has been overwritten by the desired -c Ritz vectors. If a separate array Z has been passed then -c the first NCONV=IPARAM(5) columns of V will contain approximate -c Schur vectors that span the desired invariant subspace. -c -c WORKL Double precision work array of length LWORKL. (OUTPUT/WORKSPACE) -c WORKL(1:ncv*ncv+3*ncv) contains information obtained in -c igraphdnaupd. They are not changed by igraphdneupd. -c WORKL(ncv*ncv+3*ncv+1:3*ncv*ncv+6*ncv) holds the -c real and imaginary part of the untransformed Ritz values, -c the upper quasi-triangular matrix for H, and the -c associated matrix representation of the invariant subspace for H. -c -c Note: IPNTR(9:13) contains the pointer into WORKL for addresses -c of the above information computed by igraphdneupd. -c ------------------------------------------------------------- -c IPNTR(9): pointer to the real part of the NCV RITZ values of the -c original system. -c IPNTR(10): pointer to the imaginary part of the NCV RITZ values of -c the original system. -c IPNTR(11): pointer to the NCV corresponding error bounds. -c IPNTR(12): pointer to the NCV by NCV upper quasi-triangular -c Schur matrix for H. -c IPNTR(13): pointer to the NCV by NCV matrix of eigenvectors -c of the upper Hessenberg matrix H. Only referenced by -c igraphdneupd if RVEC = .TRUE. See Remark 2 below. -c ------------------------------------------------------------- -c -c INFO Integer. (OUTPUT) -c Error flag on output. -c -c = 0: Normal exit. -c -c = 1: The Schur form computed by LAPACK routine dlahqr -c could not be reordered by LAPACK routine dtrsen. -c Re-enter subroutine igraphdneupd with IPARAM(5)=NCV and -c increase the size of the arrays DR and DI to have -c dimension at least dimension NCV and allocate at least NCV -c columns for Z. NOTE: Not necessary if Z and V share -c the same space. Please notify the authors if this error -c occurs. -c -c = -1: N must be positive. -c = -2: NEV must be positive. -c = -3: NCV-NEV >= 2 and less than or equal to N. -c = -5: WHICH must be one of 'LM', 'SM', 'LR', 'SR', 'LI', 'SI' -c = -6: BMAT must be one of 'I' or 'G'. -c = -7: Length of private work WORKL array is not sufficient. -c = -8: Error return from calculation of a real Schur form. -c Informational error from LAPACK routine dlahqr. -c = -9: Error return from calculation of eigenvectors. -c Informational error from LAPACK routine dtrevc. -c = -10: IPARAM(7) must be 1,2,3,4. -c = -11: IPARAM(7) = 1 and BMAT = 'G' are incompatible. -c = -12: HOWMNY = 'S' not yet implemented -c = -13: HOWMNY must be one of 'A' or 'P' if RVEC = .true. -c = -14: DNAUPD did not find any eigenvalues to sufficient -c accuracy. -c -c\BeginLib -c -c\References: -c 1. D.C. Sorensen, "Implicit Application of Polynomial Filters in -c a k-Step Arnoldi Method", SIAM J. Matr. Anal. Apps., 13 (1992), -c pp 357-385. -c 2. R.B. Lehoucq, "Analysis and Implementation of an Implicitly -c Restarted Arnoldi Iteration", Rice University Technical Report -c TR95-13, Department of Computational and Applied Mathematics. -c 3. B.N. Parlett & Y. Saad, "Complex Shift and Invert Strategies for -c Real Matrices", Linear Algebra and its Applications, vol 88/89, -c pp 575-595, (1987). -c -c\Routines called: -c igraphivout ARPACK utility routine that prints integers. -c igraphdmout ARPACK utility routine that prints matrices -c igraphdvout ARPACK utility routine that prints vectors. -c dgeqr2 LAPACK routine that computes the QR factorization of -c a matrix. -c dlacpy LAPACK matrix copy routine. -c dlahqr LAPACK routine to compute the real Schur form of an -c upper Hessenberg matrix. -c dlamch LAPACK routine that determines machine constants. -c dlapy2 LAPACK routine to compute sqrt(x**2+y**2) carefully. -c dlaset LAPACK matrix initialization routine. -c dorm2r LAPACK routine that applies an orthogonal matrix in -c factored form. -c dtrevc LAPACK routine to compute the eigenvectors of a matrix -c in upper quasi-triangular form. -c dtrsen LAPACK routine that re-orders the Schur form. -c dtrmm Level 3 BLAS matrix times an upper triangular matrix. -c dger Level 2 BLAS rank one update to a matrix. -c dcopy Level 1 BLAS that copies one vector to another . -c ddot Level 1 BLAS that computes the scalar product of two vectors. -c dnrm2 Level 1 BLAS that computes the norm of a vector. -c dscal Level 1 BLAS that scales a vector. -c -c\Remarks -c -c 1. Currently only HOWMNY = 'A' and 'P' are implemented. -c -c Let X' denote the transpose of X. -c -c 2. Schur vectors are an orthogonal representation for the basis of -c Ritz vectors. Thus, their numerical properties are often superior. -c If RVEC = .TRUE. then the relationship -c A * V(:,1:IPARAM(5)) = V(:,1:IPARAM(5)) * T, and -c V(:,1:IPARAM(5))' * V(:,1:IPARAM(5)) = I are approximately satisfied. -c Here T is the leading submatrix of order IPARAM(5) of the real -c upper quasi-triangular matrix stored workl(ipntr(12)). That is, -c T is block upper triangular with 1-by-1 and 2-by-2 diagonal blocks; -c each 2-by-2 diagonal block has its diagonal elements equal and its -c off-diagonal elements of opposite sign. Corresponding to each 2-by-2 -c diagonal block is a complex conjugate pair of Ritz values. The real -c Ritz values are stored on the diagonal of T. -c -c 3. If IPARAM(7) = 3 or 4 and SIGMAI is not equal zero, then the user must -c form the IPARAM(5) Rayleigh quotients in order to transform the Ritz -c values computed by DNAUPD for OP to those of A*z = lambda*B*z. -c Set RVEC = .true. and HOWMNY = 'A', and -c compute -c Z(:,I)' * A * Z(:,I) if DI(I) = 0. -c If DI(I) is not equal to zero and DI(I+1) = - D(I), -c then the desired real and imaginary parts of the Ritz value are -c Z(:,I)' * A * Z(:,I) + Z(:,I+1)' * A * Z(:,I+1), -c Z(:,I)' * A * Z(:,I+1) - Z(:,I+1)' * A * Z(:,I), respectively. -c Another possibility is to set RVEC = .true. and HOWMNY = 'P' and -c compute V(:,1:IPARAM(5))' * A * V(:,1:IPARAM(5)) and then an upper -c quasi-triangular matrix of order IPARAM(5) is computed. See remark -c 2 above. -c -c\Authors -c Danny Sorensen Phuong Vu -c Richard Lehoucq CRPC / Rice University -c Chao Yang Houston, Texas -c Dept. of Computational & -c Applied Mathematics -c Rice University -c Houston, Texas -c -c\SCCS Information: @(#) -c FILE: neupd.F SID: 2.5 DATE OF SID: 7/31/96 RELEASE: 2 -c -c\EndLib -c -c----------------------------------------------------------------------- - subroutine igraphdneupd (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) -c -c %----------------------------------------------------% -c | Include files for debugging and timing information | -c %----------------------------------------------------% -c - include 'debug.h' - include 'stat.h' -c -c %------------------% -c | Scalar Arguments | -c %------------------% -c - character bmat, howmny, which*2 - logical rvec - integer info, ldz, ldv, lworkl, n, ncv, nev - Double precision - & sigmar, sigmai, tol -c -c %-----------------% -c | Array Arguments | -c %-----------------% -c - integer iparam(11), ipntr(14) - logical select(ncv) - Double precision - & dr(nev+1), di(nev+1), resid(n), v(ldv,ncv), z(ldz,*), - & workd(3*n), workl(lworkl), workev(3*ncv) -c -c %------------% -c | Parameters | -c %------------% -c - Double precision - & one, zero - parameter (one = 1.0D+0, zero = 0.0D+0) -c -c %---------------% -c | Local Scalars | -c %---------------% -c - character type*6 - integer bounds, ierr, ih, ihbds, iheigr, iheigi, iconj, nconv, - & invsub, iuptri, iwev, iwork(1), j, k, ktrord, - & ldh, ldq, mode, msglvl, outncv, ritzr, ritzi, wri, wrr, - & irr, iri, ibd - logical reord - Double precision - & conds, rnorm, sep, temp, thres, vl(1,1), temp1, eps23 -c -c %----------------------% -c | External Subroutines | -c %----------------------% -c - external dcopy, dger, dgeqr2, dlacpy, dlahqr, dlaset, - & igraphdmout, dorm2r, dtrevc, dtrmm, dtrsen, dscal, - & igraphdvout, igraphivout -c -c %--------------------% -c | External Functions | -c %--------------------% -c - Double precision - & dlapy2, dnrm2, dlamch, ddot - external dlapy2, dnrm2, dlamch, ddot -c -c %---------------------% -c | Intrinsic Functions | -c %---------------------% -c - intrinsic abs, min, sqrt -c -c %-----------------------% -c | Executable Statements | -c %-----------------------% -c -c %------------------------% -c | Set default parameters | -c %------------------------% -c - msglvl = mneupd - mode = iparam(7) - nconv = iparam(5) - info = 0 -c -c %---------------------------------% -c | Get machine dependent constant. | -c %---------------------------------% -c - eps23 = dlamch('Epsilon-Machine') - eps23 = eps23**(2.0D+0 / 3.0D+0) -c -c %--------------% -c | Quick return | -c %--------------% -c - ierr = 0 -c - if (nconv .le. 0) then - ierr = -14 - else if (n .le. 0) then - ierr = -1 - else if (nev .le. 0) then - ierr = -2 - else if (ncv .le. nev+1 .or. ncv .gt. n) then - ierr = -3 - else if (which .ne. 'LM' .and. - & which .ne. 'SM' .and. - & which .ne. 'LR' .and. - & which .ne. 'SR' .and. - & which .ne. 'LI' .and. - & which .ne. 'SI') then - ierr = -5 - else if (bmat .ne. 'I' .and. bmat .ne. 'G') then - ierr = -6 - else if (lworkl .lt. 3*ncv**2 + 6*ncv) then - ierr = -7 - else if ( (howmny .ne. 'A' .and. - & howmny .ne. 'P' .and. - & howmny .ne. 'S') .and. rvec ) then - ierr = -13 - else if (howmny .eq. 'S' ) then - ierr = -12 - end if -c - if (mode .eq. 1 .or. mode .eq. 2) then - type = 'REGULR' - else if (mode .eq. 3 .and. sigmai .eq. zero) then - type = 'SHIFTI' - else if (mode .eq. 3 ) then - type = 'REALPT' - else if (mode .eq. 4 ) then - type = 'IMAGPT' - else - ierr = -10 - end if - if (mode .eq. 1 .and. bmat .eq. 'G') ierr = -11 -c -c %------------% -c | Error Exit | -c %------------% -c - if (ierr .ne. 0) then - info = ierr - go to 9000 - end if -c -c %--------------------------------------------------------% -c | Pointer into WORKL for address of H, RITZ, BOUNDS, Q | -c | etc... and the remaining workspace. | -c | Also update pointer to be used on output. | -c | Memory is laid out as follows: | -c | workl(1:ncv*ncv) := generated Hessenberg matrix | -c | workl(ncv*ncv+1:ncv*ncv+2*ncv) := real and imaginary | -c | parts of ritz values | -c | workl(ncv*ncv+2*ncv+1:ncv*ncv+3*ncv) := error bounds | -c %--------------------------------------------------------% -c -c %-----------------------------------------------------------% -c | The following is used and set by DNEUPD. | -c | workl(ncv*ncv+3*ncv+1:ncv*ncv+4*ncv) := The untransformed | -c | real part of the Ritz values. | -c | workl(ncv*ncv+4*ncv+1:ncv*ncv+5*ncv) := The untransformed | -c | imaginary part of the Ritz values. | -c | workl(ncv*ncv+5*ncv+1:ncv*ncv+6*ncv) := The untransformed | -c | error bounds of the Ritz values | -c | workl(ncv*ncv+6*ncv+1:2*ncv*ncv+6*ncv) := Holds the upper | -c | quasi-triangular matrix for H | -c | workl(2*ncv*ncv+6*ncv+1: 3*ncv*ncv+6*ncv) := Holds the | -c | associated matrix representation of the invariant | -c | subspace for H. | -c | GRAND total of NCV * ( 3 * NCV + 6 ) locations. | -c %-----------------------------------------------------------% -c - 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 -c -c %-----------------------------------------% -c | irr points to the REAL part of the Ritz | -c | values computed by _neigh before | -c | exiting _naup2. | -c | iri points to the IMAGINARY part of the | -c | Ritz values computed by _neigh | -c | before exiting _naup2. | -c | ibd points to the Ritz estimates | -c | computed by _neigh before exiting | -c | _naup2. | -c %-----------------------------------------% -c - irr = ipntr(14)+ncv*ncv - iri = irr+ncv - ibd = iri+ncv -c -c %------------------------------------% -c | RNORM is B-norm of the RESID(1:N). | -c %------------------------------------% -c - rnorm = workl(ih+2) - workl(ih+2) = zero -c - if (rvec) then -c -c %-------------------------------------------% -c | Get converged Ritz value on the boundary. | -c | Note: converged Ritz values have been | -c | placed in the first NCONV locations in | -c | workl(ritzr) and workl(ritzi). They have | -c | been sorted (in _naup2) according to the | -c | WHICH selection criterion. | -c %-------------------------------------------% -c - if (which .eq. 'LM' .or. which .eq. 'SM') then - thres = dlapy2( workl(ritzr), workl(ritzi) ) - else if (which .eq. 'LR' .or. which .eq. 'SR') then - thres = workl(ritzr) - else if (which .eq. 'LI' .or. which .eq. 'SI') then - thres = abs( workl(ritzi) ) - end if -c - if (msglvl .gt. 2) then - call igraphdvout(logfil, 1, [thres], ndigit, - & '_neupd: Threshold eigenvalue used for re-ordering') - end if -c -c %----------------------------------------------------------% -c | Check to see if all converged Ritz values appear at the | -c | top of the upper quasi-triangular matrix computed by | -c | _neigh in _naup2. This is done in the following way: | -c | | -c | 1) For each Ritz value obtained from _neigh, compare it | -c | with the threshold Ritz value computed above to | -c | determine whether it is a wanted one. | -c | | -c | 2) If it is wanted, then check the corresponding Ritz | -c | estimate to see if it has converged. If it has, set | -c | correponding entry in the logical array SELECT to | -c | .TRUE.. | -c | | -c | If SELECT(j) = .TRUE. and j > NCONV, then there is a | -c | converged Ritz value that does not appear at the top of | -c | the upper quasi-triangular matrix computed by _neigh in | -c | _naup2. Reordering is needed. | -c %----------------------------------------------------------% -c - reord = .false. - ktrord = 0 - do 10 j = 0, ncv-1 - select(j+1) = .false. - if (which .eq. 'LM') then - if (dlapy2(workl(irr+j), workl(iri+j)) - & .ge. thres) then - temp1 = max( eps23, - & dlapy2( workl(irr+j), workl(iri+j) ) ) - if (workl(ibd+j) .le. tol*temp1) - & select(j+1) = .true. - end if - else if (which .eq. 'SM') then - if (dlapy2(workl(irr+j), workl(iri+j)) - & .le. thres) then - temp1 = max( eps23, - & dlapy2( workl(irr+j), workl(iri+j) ) ) - if (workl(ibd+j) .le. tol*temp1) - & select(j+1) = .true. - end if - else if (which .eq. 'LR') then - if (workl(irr+j) .ge. thres) then - temp1 = max( eps23, - & dlapy2( workl(irr+j), workl(iri+j) ) ) - if (workl(ibd+j) .le. tol*temp1) - & select(j+1) = .true. - end if - else if (which .eq. 'SR') then - if (workl(irr+j) .le. thres) then - temp1 = max( eps23, - & dlapy2( workl(irr+j), workl(iri+j) ) ) - if (workl(ibd+j) .le. tol*temp1) - & select(j+1) = .true. - end if - else if (which .eq. 'LI') then - if (abs(workl(iri+j)) .ge. thres) then - temp1 = max( eps23, - & dlapy2( workl(irr+j), workl(iri+j) ) ) - if (workl(ibd+j) .le. tol*temp1) - & select(j+1) = .true. - end if - else if (which .eq. 'SI') then - if (abs(workl(iri+j)) .le. thres) then - temp1 = max( eps23, - & dlapy2( workl(irr+j), workl(iri+j) ) ) - if (workl(ibd+j) .le. tol*temp1) - & select(j+1) = .true. - end if - end if - if (j+1 .gt. nconv ) reord = ( select(j+1) .or. reord ) - if (select(j+1)) ktrord = ktrord + 1 - 10 continue -c - if (msglvl .gt. 2) then - call igraphivout(logfil, 1, [ktrord], ndigit, - & '_neupd: Number of specified eigenvalues') - call igraphivout(logfil, 1, [nconv], ndigit, - & '_neupd: Number of "converged" eigenvalues') - end if -c -c %-----------------------------------------------------------% -c | Call LAPACK routine dlahqr to compute the real Schur form | -c | of the upper Hessenberg matrix returned by DNAUPD. | -c | Make a copy of the upper Hessenberg matrix. | -c | Initialize the Schur vector matrix Q to the identity. | -c %-----------------------------------------------------------% -c - call dcopy (ldh*ncv, workl(ih), 1, workl(iuptri), 1) - call dlaset ('All', ncv, ncv, zero, one, workl(invsub), ldq) - call dlahqr (.true., .true., ncv, 1, ncv, workl(iuptri), ldh, - & workl(iheigr), workl(iheigi), 1, ncv, - & workl(invsub), ldq, ierr) - call dcopy (ncv, workl(invsub+ncv-1), ldq, workl(ihbds), 1) -c - if (ierr .ne. 0) then - info = -8 - go to 9000 - end if -c - if (msglvl .gt. 1) then - call igraphdvout (logfil, ncv, workl(iheigr), ndigit, - & '_neupd: Real part of the eigenvalues of H') - call igraphdvout (logfil, ncv, workl(iheigi), ndigit, - & '_neupd: Imaginary part of the Eigenvalues of H') - call igraphdvout (logfil, ncv, workl(ihbds), ndigit, - & '_neupd: Last row of the Schur vector matrix') - if (msglvl .gt. 3) then - call igraphdmout (logfil, ncv, ncv, workl(iuptri), ldh, - & ndigit, - & '_neupd: The upper quasi-triangular matrix ') - end if - end if -c - if (reord) then -c -c %-----------------------------------------------------% -c | Reorder the computed upper quasi-triangular matrix. | -c %-----------------------------------------------------% -c - call dtrsen ('None', 'V', select, ncv, workl(iuptri), ldh, - & workl(invsub), ldq, workl(iheigr), workl(iheigi), - & nconv, conds, sep, workl(ihbds), ncv, iwork, 1, ierr) -c - if (ierr .eq. 1) then - info = 1 - go to 9000 - end if -c - if (msglvl .gt. 2) then - call igraphdvout (logfil, ncv, workl(iheigr), ndigit, - & '_neupd: Real part of the eigenvalues of H--reordered') - call igraphdvout (logfil, ncv, workl(iheigi), ndigit, - & '_neupd: Imag part of the eigenvalues of H--reordered') - if (msglvl .gt. 3) then - call igraphdmout (logfil, ncv, ncv, workl(iuptri), - & ldq, ndigit, - & '_neupd: Quasi-triangular matrix after re-ordering') - end if - end if -c - end if -c -c %---------------------------------------% -c | Copy the last row of the Schur vector | -c | into workl(ihbds). This will be used | -c | to compute the Ritz estimates of | -c | converged Ritz values. | -c %---------------------------------------% -c - call dcopy(ncv, workl(invsub+ncv-1), ldq, workl(ihbds), 1) -c -c %----------------------------------------------------% -c | Place the computed eigenvalues of H into DR and DI | -c | if a spectral transformation was not used. | -c %----------------------------------------------------% -c - if (type .eq. 'REGULR') then - call dcopy (nconv, workl(iheigr), 1, dr, 1) - call dcopy (nconv, workl(iheigi), 1, di, 1) - end if -c -c %----------------------------------------------------------% -c | Compute the QR factorization of the matrix representing | -c | the wanted invariant subspace located in the first NCONV | -c | columns of workl(invsub,ldq). | -c %----------------------------------------------------------% -c - call dgeqr2 (ncv, nconv, workl(invsub), ldq, workev, - & workev(ncv+1), ierr) -c -c %---------------------------------------------------------% -c | * Postmultiply V by Q using dorm2r. | -c | * Copy the first NCONV columns of VQ into Z. | -c | * Postmultiply Z by R. | -c | The N by NCONV matrix Z is now a matrix representation | -c | of the approximate invariant subspace associated with | -c | the Ritz values in workl(iheigr) and workl(iheigi) | -c | The first NCONV columns of V are now approximate Schur | -c | vectors associated with the real upper quasi-triangular | -c | matrix of order NCONV in workl(iuptri) | -c %---------------------------------------------------------% -c - call dorm2r ('Right', 'Notranspose', n, ncv, nconv, - & workl(invsub), ldq, workev, v, ldv, workd(n+1), ierr) - call dlacpy ('All', n, nconv, v, ldv, z, ldz) -c - do 20 j=1, nconv -c -c %---------------------------------------------------% -c | Perform both a column and row scaling if the | -c | diagonal element of workl(invsub,ldq) is negative | -c | I'm lazy and don't take advantage of the upper | -c | quasi-triangular form of workl(iuptri,ldq) | -c | Note that since Q is orthogonal, R is a diagonal | -c | matrix consisting of plus or minus ones | -c %---------------------------------------------------% -c - if (workl(invsub+(j-1)*ldq+j-1) .lt. zero) then - call dscal (nconv, -one, workl(iuptri+j-1), ldq) - call dscal (nconv, -one, workl(iuptri+(j-1)*ldq), 1) - end if -c - 20 continue -c - if (howmny .eq. 'A') then -c -c %--------------------------------------------% -c | Compute the NCONV wanted eigenvectors of T | -c | located in workl(iuptri,ldq). | -c %--------------------------------------------% -c - do 30 j=1, ncv - if (j .le. nconv) then - select(j) = .true. - else - select(j) = .false. - end if - 30 continue -c - call dtrevc ('Right', 'Select', select, ncv, workl(iuptri), - & ldq, vl, 1, workl(invsub), ldq, ncv, outncv, workev, - & ierr) -c - if (ierr .ne. 0) then - info = -9 - go to 9000 - end if -c -c %------------------------------------------------% -c | Scale the returning eigenvectors so that their | -c | Euclidean norms are all one. LAPACK subroutine | -c | dtrevc returns each eigenvector normalized so | -c | that the element of largest magnitude has | -c | magnitude 1; | -c %------------------------------------------------% -c - iconj = 0 - do 40 j=1, nconv -c - if ( workl(iheigi+j-1) .eq. zero ) then -c -c %----------------------% -c | real eigenvalue case | -c %----------------------% -c - temp = dnrm2( ncv, workl(invsub+(j-1)*ldq), 1 ) - call dscal ( ncv, one / temp, - & workl(invsub+(j-1)*ldq), 1 ) -c - else -c -c %-------------------------------------------% -c | Complex conjugate pair case. Note that | -c | since the real and imaginary part of | -c | the eigenvector are stored in consecutive | -c | columns, we further normalize by the | -c | square root of two. | -c %-------------------------------------------% -c - if (iconj .eq. 0) then - temp = dlapy2( dnrm2( ncv, workl(invsub+(j-1)*ldq), - & 1 ), dnrm2( ncv, workl(invsub+j*ldq), 1) ) - call dscal ( ncv, one / temp, - & workl(invsub+(j-1)*ldq), 1 ) - call dscal ( ncv, one / temp, - & workl(invsub+j*ldq), 1 ) - iconj = 1 - else - iconj = 0 - end if -c - end if -c - 40 continue -c - call dgemv('T', ncv, nconv, one, workl(invsub), - & ldq, workl(ihbds), 1, zero, workev, 1) -c - iconj = 0 - do 45 j=1, nconv - if (workl(iheigi+j-1) .ne. zero) then -c -c %-------------------------------------------% -c | Complex conjugate pair case. Note that | -c | since the real and imaginary part of | -c | the eigenvector are stored in consecutive | -c %-------------------------------------------% -c - if (iconj .eq. 0) then - workev(j) = dlapy2(workev(j), workev(j+1)) - workev(j+1) = workev(j) - iconj = 1 - else - iconj = 0 - end if - end if - 45 continue -c - if (msglvl .gt. 2) then - call dcopy(ncv, workl(invsub+ncv-1), ldq, - & workl(ihbds), 1) - call igraphdvout (logfil, ncv, workl(ihbds), ndigit, - & '_neupd: Last row of the eigenvector matrix for T') - if (msglvl .gt. 3) then - call igraphdmout (logfil, ncv, ncv, workl(invsub), - & ldq, ndigit, - & '_neupd: The eigenvector matrix for T') - end if - end if -c -c %---------------------------------------% -c | Copy Ritz estimates into workl(ihbds) | -c %---------------------------------------% -c - call dcopy(nconv, workev, 1, workl(ihbds), 1) -c -c %---------------------------------------------------------% -c | Compute the QR factorization of the eigenvector matrix | -c | associated with leading portion of T in the first NCONV | -c | columns of workl(invsub,ldq). | -c %---------------------------------------------------------% -c - call dgeqr2 (ncv, nconv, workl(invsub), ldq, workev, - & workev(ncv+1), ierr) -c -c %----------------------------------------------% -c | * Postmultiply Z by Q. | -c | * Postmultiply Z by R. | -c | The N by NCONV matrix Z is now contains the | -c | Ritz vectors associated with the Ritz values | -c | in workl(iheigr) and workl(iheigi). | -c %----------------------------------------------% -c - call dorm2r ('Right', 'Notranspose', n, ncv, nconv, - & workl(invsub), ldq, workev, z, ldz, workd(n+1), ierr) -c - call dtrmm ('Right', 'Upper', 'No transpose', 'Non-unit', - & n, nconv, one, workl(invsub), ldq, z, ldz) -c - end if -c - else -c -c %------------------------------------------------------% -c | An approximate invariant subspace is not needed. | -c | Place the Ritz values computed DNAUPD into DR and DI | -c %------------------------------------------------------% -c - call dcopy (nconv, workl(ritzr), 1, dr, 1) - call dcopy (nconv, workl(ritzi), 1, di, 1) - call dcopy (nconv, workl(ritzr), 1, workl(iheigr), 1) - call dcopy (nconv, workl(ritzi), 1, workl(iheigi), 1) - call dcopy (nconv, workl(bounds), 1, workl(ihbds), 1) - end if -c -c %------------------------------------------------% -c | Transform the Ritz values and possibly vectors | -c | and corresponding error bounds of OP to those | -c | of A*x = lambda*B*x. | -c %------------------------------------------------% -c - if (type .eq. 'REGULR') then -c - if (rvec) - & call dscal (ncv, rnorm, workl(ihbds), 1) -c - else -c -c %---------------------------------------% -c | A spectral transformation was used. | -c | * Determine the Ritz estimates of the | -c | Ritz values in the original system. | -c %---------------------------------------% -c - if (type .eq. 'SHIFTI') then -c - if (rvec) - & call dscal (ncv, rnorm, workl(ihbds), 1) -c - do 50 k=1, ncv - temp = dlapy2( workl(iheigr+k-1), - & workl(iheigi+k-1) ) - workl(ihbds+k-1) = abs( workl(ihbds+k-1) ) - & / temp / temp - 50 continue -c - else if (type .eq. 'REALPT') then -c - do 60 k=1, ncv - 60 continue -c - else if (type .eq. 'IMAGPT') then -c - do 70 k=1, ncv - 70 continue -c - end if -c -c %-----------------------------------------------------------% -c | * Transform the Ritz values back to the original system. | -c | For TYPE = 'SHIFTI' the transformation is | -c | lambda = 1/theta + sigma | -c | For TYPE = 'REALPT' or 'IMAGPT' the user must from | -c | Rayleigh quotients or a projection. See remark 3 above.| -c | NOTES: | -c | *The Ritz vectors are not affected by the transformation. | -c %-----------------------------------------------------------% -c - if (type .eq. 'SHIFTI') then -c - do 80 k=1, ncv - temp = dlapy2( 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 - 80 continue -c - call dcopy (nconv, workl(iheigr), 1, dr, 1) - call dcopy (nconv, workl(iheigi), 1, di, 1) -c - else if (type .eq. 'REALPT' .or. type .eq. 'IMAGPT') then -c - call dcopy (nconv, workl(iheigr), 1, dr, 1) - call dcopy (nconv, workl(iheigi), 1, di, 1) -c - end if -c - end if -c - if (type .eq. 'SHIFTI' .and. msglvl .gt. 1) then - call igraphdvout (logfil, nconv, dr, ndigit, - & '_neupd: Untransformed real part of the Ritz valuess.') - call igraphdvout (logfil, nconv, di, ndigit, - & '_neupd: Untransformed imag part of the Ritz valuess.') - call igraphdvout (logfil, nconv, workl(ihbds), ndigit, - & '_neupd: Ritz estimates of untransformed Ritz values.') - else if (type .eq. 'REGULR' .and. msglvl .gt. 1) then - call igraphdvout (logfil, nconv, dr, ndigit, - & '_neupd: Real parts of converged Ritz values.') - call igraphdvout (logfil, nconv, di, ndigit, - & '_neupd: Imag parts of converged Ritz values.') - call igraphdvout (logfil, nconv, workl(ihbds), ndigit, - & '_neupd: Associated Ritz estimates.') - end if -c -c %-------------------------------------------------% -c | Eigenvector Purification step. Formally perform | -c | one of inverse subspace iteration. Only used | -c | for MODE = 2. | -c %-------------------------------------------------% -c - if (rvec .and. howmny .eq. 'A' .and. type .eq. 'SHIFTI') then -c -c %------------------------------------------------% -c | Purify the computed Ritz vectors by adding a | -c | little bit of the residual vector: | -c | T | -c | resid(:)*( e s ) / theta | -c | NCV | -c | where H s = s theta. Remember that when theta | -c | has nonzero imaginary part, the corresponding | -c | Ritz vector is stored across two columns of Z. | -c %------------------------------------------------% -c - iconj = 0 - do 110 j=1, nconv - if (workl(iheigi+j-1) .eq. zero) then - workev(j) = workl(invsub+(j-1)*ldq+ncv-1) / - & workl(iheigr+j-1) - else if (iconj .eq. 0) then - temp = dlapy2( 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 - end if - 110 continue -c -c %---------------------------------------% -c | Perform a rank one update to Z and | -c | purify all the Ritz vectors together. | -c %---------------------------------------% -c - call dger (n, nconv, one, resid, 1, workev, 1, z, ldz) -c - end if -c - 9000 continue -c - return -c -c %---------------% -c | End of DNEUPD | -c %---------------% -c - end diff --git a/src/rigraph/vendor/arpack/dngets.f b/src/rigraph/vendor/arpack/dngets.f deleted file mode 100644 index 0202090..0000000 --- a/src/rigraph/vendor/arpack/dngets.f +++ /dev/null @@ -1,231 +0,0 @@ -c----------------------------------------------------------------------- -c\BeginDoc -c -c\Name: igraphdngets -c -c\Description: -c Given the eigenvalues of the upper Hessenberg matrix H, -c computes the NP shifts AMU that are zeros of the polynomial of -c degree NP which filters out components of the unwanted eigenvectors -c corresponding to the AMU's based on some given criteria. -c -c NOTE: call this even in the case of user specified shifts in order -c to sort the eigenvalues, and error bounds of H for later use. -c -c\Usage: -c call igraphdngets -c ( ISHIFT, WHICH, KEV, NP, RITZR, RITZI, BOUNDS, SHIFTR, SHIFTI ) -c -c\Arguments -c ISHIFT Integer. (INPUT) -c Method for selecting the implicit shifts at each iteration. -c ISHIFT = 0: user specified shifts -c ISHIFT = 1: exact shift with respect to the matrix H. -c -c WHICH Character*2. (INPUT) -c Shift selection criteria. -c 'LM' -> want the KEV eigenvalues of largest magnitude. -c 'SM' -> want the KEV eigenvalues of smallest magnitude. -c 'LR' -> want the KEV eigenvalues of largest real part. -c 'SR' -> want the KEV eigenvalues of smallest real part. -c 'LI' -> want the KEV eigenvalues of largest imaginary part. -c 'SI' -> want the KEV eigenvalues of smallest imaginary part. -c -c KEV Integer. (INPUT/OUTPUT) -c INPUT: KEV+NP is the size of the matrix H. -c OUTPUT: Possibly increases KEV by one to keep complex conjugate -c pairs together. -c -c NP Integer. (INPUT/OUTPUT) -c Number of implicit shifts to be computed. -c OUTPUT: Possibly decreases NP by one to keep complex conjugate -c pairs together. -c -c RITZR, Double precision array of length KEV+NP. (INPUT/OUTPUT) -c RITZI On INPUT, RITZR and RITZI contain the real and imaginary -c parts of the eigenvalues of H. -c On OUTPUT, RITZR and RITZI are sorted so that the unwanted -c eigenvalues are in the first NP locations and the wanted -c portion is in the last KEV locations. When exact shifts are -c selected, the unwanted part corresponds to the shifts to -c be applied. Also, if ISHIFT .eq. 1, the unwanted eigenvalues -c are further sorted so that the ones with largest Ritz values -c are first. -c -c BOUNDS Double precision array of length KEV+NP. (INPUT/OUTPUT) -c Error bounds corresponding to the ordering in RITZ. -c -c SHIFTR, SHIFTI *** USE deprecated as of version 2.1. *** -c -c -c\EndDoc -c -c----------------------------------------------------------------------- -c -c\BeginLib -c -c\Local variables: -c xxxxxx real -c -c\Routines called: -c igraphdsortc ARPACK sorting routine. -c dcopy Level 1 BLAS that copies one vector to another . -c -c\Author -c Danny Sorensen Phuong Vu -c Richard Lehoucq CRPC / Rice University -c Dept. of Computational & Houston, Texas -c Applied Mathematics -c Rice University -c Houston, Texas -c -c\Revision history: -c xx/xx/92: Version ' 2.1' -c -c\SCCS Information: @(#) -c FILE: ngets.F SID: 2.3 DATE OF SID: 4/20/96 RELEASE: 2 -c -c\Remarks -c 1. xxxx -c -c\EndLib -c -c----------------------------------------------------------------------- -c - subroutine igraphdngets ( ishift, which, kev, np, ritzr, ritzi, - & bounds, shiftr, shifti ) -c -c %----------------------------------------------------% -c | Include files for debugging and timing information | -c %----------------------------------------------------% -c - include 'debug.h' - include 'stat.h' -c -c %------------------% -c | Scalar Arguments | -c %------------------% -c - character*2 which - integer ishift, kev, np -c -c %-----------------% -c | Array Arguments | -c %-----------------% -c - Double precision - & bounds(kev+np), ritzr(kev+np), ritzi(kev+np), - & shiftr(1), shifti(1) -c -c %------------% -c | Parameters | -c %------------% -c - Double precision - & one, zero - parameter (one = 1.0, zero = 0.0) -c -c %---------------% -c | Local Scalars | -c %---------------% -c - integer msglvl -c -c %----------------------% -c | External Subroutines | -c %----------------------% -c - external dcopy, igraphdsortc, igraphsecond -c -c %----------------------% -c | Intrinsics Functions | -c %----------------------% -c - intrinsic abs -c -c %-----------------------% -c | Executable Statements | -c %-----------------------% -c -c %-------------------------------% -c | Initialize timing statistics | -c | & message level for debugging | -c %-------------------------------% -c - call igraphsecond (t0) - msglvl = mngets -c -c %----------------------------------------------------% -c | LM, SM, LR, SR, LI, SI case. | -c | Sort the eigenvalues of H into the desired order | -c | and apply the resulting order to BOUNDS. | -c | The eigenvalues are sorted so that the wanted part | -c | are always in the last KEV locations. | -c | We first do a pre-processing sort in order to keep | -c | complex conjugate pairs together | -c %----------------------------------------------------% -c - if (which .eq. 'LM') then - call igraphdsortc ('LR', .true., kev+np, ritzr, ritzi, bounds) - else if (which .eq. 'SM') then - call igraphdsortc ('SR', .true., kev+np, ritzr, ritzi, bounds) - else if (which .eq. 'LR') then - call igraphdsortc ('LM', .true., kev+np, ritzr, ritzi, bounds) - else if (which .eq. 'SR') then - call igraphdsortc ('SM', .true., kev+np, ritzr, ritzi, bounds) - else if (which .eq. 'LI') then - call igraphdsortc ('LM', .true., kev+np, ritzr, ritzi, bounds) - else if (which .eq. 'SI') then - call igraphdsortc ('SM', .true., kev+np, ritzr, ritzi, bounds) - end if -c - call igraphdsortc (which, .true., kev+np, ritzr, ritzi, bounds) -c -c %-------------------------------------------------------% -c | Increase KEV by one if the ( ritzr(np),ritzi(np) ) | -c | = ( ritzr(np+1),-ritzi(np+1) ) and ritz(np) .ne. zero | -c | Accordingly decrease NP by one. In other words keep | -c | complex conjugate pairs together. | -c %-------------------------------------------------------% -c - if ( ( ritzr(np+1) - ritzr(np) ) .eq. zero - & .and. ( ritzi(np+1) + ritzi(np) ) .eq. zero ) then - np = np - 1 - kev = kev + 1 - end if -c - if ( ishift .eq. 1 ) then -c -c %-------------------------------------------------------% -c | Sort the unwanted Ritz values used as shifts so that | -c | the ones with largest Ritz estimates are first | -c | This will tend to minimize the effects of the | -c | forward instability of the iteration when they shifts | -c | are applied in subroutine igraphdnapps. | -c | Be careful and use 'SR' since we want to sort BOUNDS! | -c %-------------------------------------------------------% -c - call igraphdsortc ( 'SR', .true., np, bounds, ritzr, ritzi ) - end if -c - call igraphsecond (t1) - tngets = tngets + (t1 - t0) -c - if (msglvl .gt. 0) then - call igraphivout (logfil, 1, [kev], ndigit, '_ngets: KEV is') - call igraphivout (logfil, 1, [np], ndigit, '_ngets: NP is') - call igraphdvout (logfil, kev+np, ritzr, ndigit, - & '_ngets: Eigenvalues of current H matrix -- real part') - call igraphdvout (logfil, kev+np, ritzi, ndigit, - & '_ngets: Eigenvalues of current H matrix -- imag part') - call igraphdvout (logfil, kev+np, bounds, ndigit, - & '_ngets: Ritz estimates of the current KEV+NP Ritz values') - end if -c - return -c -c %---------------% -c | End of igraphdngets | -c %---------------% -c - end diff --git a/src/rigraph/vendor/arpack/dsaitr.f b/src/rigraph/vendor/arpack/dsaitr.f deleted file mode 100644 index 4a4698c..0000000 --- a/src/rigraph/vendor/arpack/dsaitr.f +++ /dev/null @@ -1,854 +0,0 @@ -c----------------------------------------------------------------------- -c\BeginDoc -c -c\Name: igraphdsaitr -c -c\Description: -c Reverse communication interface for applying NP additional steps to -c a K step symmetric Arnoldi factorization. -c -c Input: OP*V_{k} - V_{k}*H = r_{k}*e_{k}^T -c -c with (V_{k}^T)*B*V_{k} = I, (V_{k}^T)*B*r_{k} = 0. -c -c Output: OP*V_{k+p} - V_{k+p}*H = r_{k+p}*e_{k+p}^T -c -c with (V_{k+p}^T)*B*V_{k+p} = I, (V_{k+p}^T)*B*r_{k+p} = 0. -c -c where OP and B are as in igraphdsaupd. The B-norm of r_{k+p} is also -c computed and returned. -c -c\Usage: -c call igraphdsaitr -c ( IDO, BMAT, N, K, NP, MODE, RESID, RNORM, V, LDV, H, LDH, -c IPNTR, WORKD, INFO ) -c -c\Arguments -c IDO Integer. (INPUT/OUTPUT) -c Reverse communication flag. -c ------------------------------------------------------------- -c IDO = 0: first call to the reverse communication interface -c IDO = -1: compute Y = OP * X where -c IPNTR(1) is the pointer into WORK for X, -c IPNTR(2) is the pointer into WORK for Y. -c This is for the restart phase to force the new -c starting vector into the range of OP. -c IDO = 1: compute Y = OP * X where -c IPNTR(1) is the pointer into WORK for X, -c IPNTR(2) is the pointer into WORK for Y, -c IPNTR(3) is the pointer into WORK for B * X. -c IDO = 2: compute Y = B * X where -c IPNTR(1) is the pointer into WORK for X, -c IPNTR(2) is the pointer into WORK for Y. -c IDO = 99: done -c ------------------------------------------------------------- -c When the routine is used in the "shift-and-invert" mode, the -c vector B * Q is already available and does not need to be -c recomputed in forming OP * Q. -c -c BMAT Character*1. (INPUT) -c BMAT specifies the type of matrix B that defines the -c semi-inner product for the operator OP. See igraphdsaupd. -c B = 'I' -> standard eigenvalue problem A*x = lambda*x -c B = 'G' -> generalized eigenvalue problem A*x = lambda*M*x -c -c N Integer. (INPUT) -c Dimension of the eigenproblem. -c -c K Integer. (INPUT) -c Current order of H and the number of columns of V. -c -c NP Integer. (INPUT) -c Number of additional Arnoldi steps to take. -c -c MODE Integer. (INPUT) -c Signifies which form for "OP". If MODE=2 then -c a reduction in the number of B matrix vector multiplies -c is possible since the B-norm of OP*x is equivalent to -c the inv(B)-norm of A*x. -c -c RESID Double precision array of length N. (INPUT/OUTPUT) -c On INPUT: RESID contains the residual vector r_{k}. -c On OUTPUT: RESID contains the residual vector r_{k+p}. -c -c RNORM Double precision scalar. (INPUT/OUTPUT) -c On INPUT the B-norm of r_{k}. -c On OUTPUT the B-norm of the updated residual r_{k+p}. -c -c V Double precision N by K+NP array. (INPUT/OUTPUT) -c On INPUT: V contains the Arnoldi vectors in the first K -c columns. -c On OUTPUT: V contains the new NP Arnoldi vectors in the next -c NP columns. The first K columns are unchanged. -c -c LDV Integer. (INPUT) -c Leading dimension of V exactly as declared in the calling -c program. -c -c H Double precision (K+NP) by 2 array. (INPUT/OUTPUT) -c H is used to store the generated symmetric tridiagonal matrix -c with the subdiagonal in the first column starting at H(2,1) -c and the main diagonal in the igraphsecond column. -c -c LDH Integer. (INPUT) -c Leading dimension of H exactly as declared in the calling -c program. -c -c IPNTR Integer array of length 3. (OUTPUT) -c Pointer to mark the starting locations in the WORK for -c vectors used by the Arnoldi iteration. -c ------------------------------------------------------------- -c IPNTR(1): pointer to the current operand vector X. -c IPNTR(2): pointer to the current result vector Y. -c IPNTR(3): pointer to the vector B * X when used in the -c shift-and-invert mode. X is the current operand. -c ------------------------------------------------------------- -c -c WORKD Double precision work array of length 3*N. (REVERSE COMMUNICATION) -c Distributed array to be used in the basic Arnoldi iteration -c for reverse communication. The calling program should not -c use WORKD as temporary workspace during the iteration !!!!!! -c On INPUT, WORKD(1:N) = B*RESID where RESID is associated -c with the K step Arnoldi factorization. Used to save some -c computation at the first step. -c On OUTPUT, WORKD(1:N) = B*RESID where RESID is associated -c with the K+NP step Arnoldi factorization. -c -c INFO Integer. (OUTPUT) -c = 0: Normal exit. -c > 0: Size of an invariant subspace of OP is found that is -c less than K + NP. -c -c\EndDoc -c -c----------------------------------------------------------------------- -c -c\BeginLib -c -c\Local variables: -c xxxxxx real -c -c\Routines called: -c igraphdgetv0 ARPACK routine to generate the initial vector. -c igraphivout ARPACK utility routine that prints integers. -c igraphdmout ARPACK utility routine that prints matrices. -c igraphdvout ARPACK utility routine that prints vectors. -c dlamch LAPACK routine that determines machine constants. -c dlascl LAPACK routine for careful scaling of a matrix. -c dgemv Level 2 BLAS routine for matrix vector multiplication. -c daxpy Level 1 BLAS that computes a vector triad. -c dscal Level 1 BLAS that scales a vector. -c dcopy Level 1 BLAS that copies one vector to another . -c ddot Level 1 BLAS that computes the scalar product of two vectors. -c dnrm2 Level 1 BLAS that computes the norm of a vector. -c -c\Author -c Danny Sorensen Phuong Vu -c Richard Lehoucq CRPC / Rice University -c Dept. of Computational & Houston, Texas -c Applied Mathematics -c Rice University -c Houston, Texas -c -c\Revision history: -c xx/xx/93: Version ' 2.4' -c -c\SCCS Information: @(#) -c FILE: saitr.F SID: 2.6 DATE OF SID: 8/28/96 RELEASE: 2 -c -c\Remarks -c The algorithm implemented is: -c -c restart = .false. -c Given V_{k} = [v_{1}, ..., v_{k}], r_{k}; -c r_{k} contains the initial residual vector even for k = 0; -c Also assume that rnorm = || B*r_{k} || and B*r_{k} are already -c computed by the calling program. -c -c betaj = rnorm ; p_{k+1} = B*r_{k} ; -c For j = k+1, ..., k+np Do -c 1) if ( betaj < tol ) stop or restart depending on j. -c if ( restart ) generate a new starting vector. -c 2) v_{j} = r(j-1)/betaj; V_{j} = [V_{j-1}, v_{j}]; -c p_{j} = p_{j}/betaj -c 3) r_{j} = OP*v_{j} where OP is defined as in igraphdsaupd -c For shift-invert mode p_{j} = B*v_{j} is already available. -c wnorm = || OP*v_{j} || -c 4) Compute the j-th step residual vector. -c w_{j} = V_{j}^T * B * OP * v_{j} -c r_{j} = OP*v_{j} - V_{j} * w_{j} -c alphaj <- j-th component of w_{j} -c rnorm = || r_{j} || -c betaj+1 = rnorm -c If (rnorm > 0.717*wnorm) accept step and go back to 1) -c 5) Re-orthogonalization step: -c s = V_{j}'*B*r_{j} -c r_{j} = r_{j} - V_{j}*s; rnorm1 = || r_{j} || -c alphaj = alphaj + s_{j}; -c 6) Iterative refinement step: -c If (rnorm1 > 0.717*rnorm) then -c rnorm = rnorm1 -c accept step and go back to 1) -c Else -c rnorm = rnorm1 -c If this is the first time in step 6), go to 5) -c Else r_{j} lies in the span of V_{j} numerically. -c Set r_{j} = 0 and rnorm = 0; go to 1) -c EndIf -c End Do -c -c\EndLib -c -c----------------------------------------------------------------------- -c - subroutine igraphdsaitr - & (ido, bmat, n, k, np, mode, resid, rnorm, v, ldv, h, ldh, - & ipntr, workd, info) -c -c %----------------------------------------------------% -c | Include files for debugging and timing information | -c %----------------------------------------------------% -c - include 'debug.h' - include 'stat.h' -c -c %------------------% -c | Scalar Arguments | -c %------------------% -c - character bmat*1 - integer ido, info, k, ldh, ldv, n, mode, np - Double precision - & rnorm -c -c %-----------------% -c | Array Arguments | -c %-----------------% -c - integer ipntr(3) - Double precision - & h(ldh,2), resid(n), v(ldv,k+np), workd(3*n) -c -c %------------% -c | Parameters | -c %------------% -c - Double precision - & one, zero - parameter (one = 1.0D+0, zero = 0.0D+0) -c -c %---------------% -c | Local Scalars | -c %---------------% -c - logical first, orth1, orth2, rstart, step3, step4 - integer i, ierr, ipj, irj, ivj, iter, itry, j, msglvl, - & infol, jj - Double precision - & rnorm1, wnorm, safmin, temp1 - save orth1, orth2, rstart, step3, step4, - & ierr, ipj, irj, ivj, iter, itry, j, msglvl, - & rnorm1, safmin, wnorm -c -c %-----------------------% -c | Local Array Arguments | -c %-----------------------% -c - Double precision - & xtemp(2) -c -c %----------------------% -c | External Subroutines | -c %----------------------% -c - external daxpy, dcopy, dscal, dgemv, igraphdgetv0, - & igraphdvout, igraphdmout, - & dlascl, igraphivout, igraphsecond -c -c %--------------------% -c | External Functions | -c %--------------------% -c - Double precision - & ddot, dnrm2, dlamch - external ddot, dnrm2, dlamch -c -c %-----------------% -c | Data statements | -c %-----------------% -c - data first / .true. / -c -c %-----------------------% -c | Executable Statements | -c %-----------------------% -c - if (first) then - first = .false. -c -c %--------------------------------% -c | safmin = safe minimum is such | -c | that 1/sfmin does not overflow | -c %--------------------------------% -c - safmin = dlamch('safmin') - end if -c - if (ido .eq. 0) then -c -c %-------------------------------% -c | Initialize timing statistics | -c | & message level for debugging | -c %-------------------------------% -c - call igraphsecond (t0) - msglvl = msaitr -c -c %------------------------------% -c | Initial call to this routine | -c %------------------------------% -c - info = 0 - step3 = .false. - step4 = .false. - rstart = .false. - orth1 = .false. - orth2 = .false. -c -c %--------------------------------% -c | Pointer to the current step of | -c | the factorization to build | -c %--------------------------------% -c - j = k + 1 -c -c %------------------------------------------% -c | Pointers used for reverse communication | -c | when using WORKD. | -c %------------------------------------------% -c - ipj = 1 - irj = ipj + n - ivj = irj + n - end if -c -c %-------------------------------------------------% -c | When in reverse communication mode one of: | -c | STEP3, STEP4, ORTH1, ORTH2, RSTART | -c | will be .true. | -c | STEP3: return from computing OP*v_{j}. | -c | STEP4: return from computing B-norm of OP*v_{j} | -c | ORTH1: return from computing B-norm of r_{j+1} | -c | ORTH2: return from computing B-norm of | -c | correction to the residual vector. | -c | RSTART: return from OP computations needed by | -c | igraphdgetv0. | -c %-------------------------------------------------% -c - if (step3) go to 50 - if (step4) go to 60 - if (orth1) go to 70 - if (orth2) go to 90 - if (rstart) go to 30 -c -c %------------------------------% -c | Else this is the first step. | -c %------------------------------% -c -c %--------------------------------------------------------------% -c | | -c | A R N O L D I I T E R A T I O N L O O P | -c | | -c | Note: B*r_{j-1} is already in WORKD(1:N)=WORKD(IPJ:IPJ+N-1) | -c %--------------------------------------------------------------% -c - 1000 continue -c - if (msglvl .gt. 2) then - call igraphivout (logfil, 1, [j], ndigit, - & '_saitr: generating Arnoldi vector no.') - call igraphdvout (logfil, 1, [rnorm], ndigit, - & '_saitr: B-norm of the current residual =') - end if -c -c %---------------------------------------------------------% -c | Check for exact zero. Equivalent to determing whether a | -c | j-step Arnoldi factorization is present. | -c %---------------------------------------------------------% -c - if (rnorm .gt. zero) go to 40 -c -c %---------------------------------------------------% -c | Invariant subspace found, generate a new starting | -c | vector which is orthogonal to the current Arnoldi | -c | basis and continue the iteration. | -c %---------------------------------------------------% -c - if (msglvl .gt. 0) then - call igraphivout (logfil, 1, [j], ndigit, - & '_saitr: ****** restart at step ******') - end if -c -c %---------------------------------------------% -c | ITRY is the loop variable that controls the | -c | maximum amount of times that a restart is | -c | attempted. NRSTRT is used by stat.h | -c %---------------------------------------------% -c - nrstrt = nrstrt + 1 - itry = 1 - 20 continue - rstart = .true. - ido = 0 - 30 continue -c -c %--------------------------------------% -c | If in reverse communication mode and | -c | RSTART = .true. flow returns here. | -c %--------------------------------------% -c - call igraphdgetv0 (ido, bmat, itry, .false., n, j, v, ldv, - & resid, rnorm, ipntr, workd, ierr) - if (ido .ne. 99) go to 9000 - if (ierr .lt. 0) then - itry = itry + 1 - if (itry .le. 3) go to 20 -c -c %------------------------------------------------% -c | Give up after several restart attempts. | -c | Set INFO to the size of the invariant subspace | -c | which spans OP and exit. | -c %------------------------------------------------% -c - info = j - 1 - call igraphsecond (t1) - tsaitr = tsaitr + (t1 - t0) - ido = 99 - go to 9000 - end if -c - 40 continue -c -c %---------------------------------------------------------% -c | STEP 2: v_{j} = r_{j-1}/rnorm and p_{j} = p_{j}/rnorm | -c | Note that p_{j} = B*r_{j-1}. In order to avoid overflow | -c | when reciprocating a small RNORM, test against lower | -c | machine bound. | -c %---------------------------------------------------------% -c - call dcopy (n, resid, 1, v(1,j), 1) - if (rnorm .ge. safmin) then - temp1 = one / rnorm - call dscal (n, temp1, v(1,j), 1) - call dscal (n, temp1, workd(ipj), 1) - else -c -c %-----------------------------------------% -c | To scale both v_{j} and p_{j} carefully | -c | use LAPACK routine SLASCL | -c %-----------------------------------------% -c - call dlascl ('General', i, i, rnorm, one, n, 1, - & v(1,j), n, infol) - call dlascl ('General', i, i, rnorm, one, n, 1, - & workd(ipj), n, infol) - end if -c -c %------------------------------------------------------% -c | STEP 3: r_{j} = OP*v_{j}; Note that p_{j} = B*v_{j} | -c | Note that this is not quite yet r_{j}. See STEP 4 | -c %------------------------------------------------------% -c - step3 = .true. - nopx = nopx + 1 - call igraphsecond (t2) - call dcopy (n, v(1,j), 1, workd(ivj), 1) - ipntr(1) = ivj - ipntr(2) = irj - ipntr(3) = ipj - ido = 1 -c -c %-----------------------------------% -c | Exit in order to compute OP*v_{j} | -c %-----------------------------------% -c - go to 9000 - 50 continue -c -c %-----------------------------------% -c | Back from reverse communication; | -c | WORKD(IRJ:IRJ+N-1) := OP*v_{j}. | -c %-----------------------------------% -c - call igraphsecond (t3) - tmvopx = tmvopx + (t3 - t2) -c - step3 = .false. -c -c %------------------------------------------% -c | Put another copy of OP*v_{j} into RESID. | -c %------------------------------------------% -c - call dcopy (n, workd(irj), 1, resid, 1) -c -c %-------------------------------------------% -c | STEP 4: Finish extending the symmetric | -c | Arnoldi to length j. If MODE = 2 | -c | then B*OP = B*inv(B)*A = A and | -c | we don't need to compute B*OP. | -c | NOTE: If MODE = 2 WORKD(IVJ:IVJ+N-1) is | -c | assumed to have A*v_{j}. | -c %-------------------------------------------% -c - if (mode .eq. 2) go to 65 - call igraphsecond (t2) - if (bmat .eq. 'G') then - nbx = nbx + 1 - step4 = .true. - ipntr(1) = irj - ipntr(2) = ipj - ido = 2 -c -c %-------------------------------------% -c | Exit in order to compute B*OP*v_{j} | -c %-------------------------------------% -c - go to 9000 - else if (bmat .eq. 'I') then - call dcopy(n, resid, 1 , workd(ipj), 1) - end if - 60 continue -c -c %-----------------------------------% -c | Back from reverse communication; | -c | WORKD(IPJ:IPJ+N-1) := B*OP*v_{j}. | -c %-----------------------------------% -c - if (bmat .eq. 'G') then - call igraphsecond (t3) - tmvbx = tmvbx + (t3 - t2) - end if -c - step4 = .false. -c -c %-------------------------------------% -c | The following is needed for STEP 5. | -c | Compute the B-norm of OP*v_{j}. | -c %-------------------------------------% -c - 65 continue - if (mode .eq. 2) then -c -c %----------------------------------% -c | Note that the B-norm of OP*v_{j} | -c | is the inv(B)-norm of A*v_{j}. | -c %----------------------------------% -c - wnorm = ddot (n, resid, 1, workd(ivj), 1) - wnorm = sqrt(abs(wnorm)) - else if (bmat .eq. 'G') then - wnorm = ddot (n, resid, 1, workd(ipj), 1) - wnorm = sqrt(abs(wnorm)) - else if (bmat .eq. 'I') then - wnorm = dnrm2(n, resid, 1) - end if -c -c %-----------------------------------------% -c | Compute the j-th residual corresponding | -c | to the j step factorization. | -c | Use Classical Gram Schmidt and compute: | -c | w_{j} <- V_{j}^T * B * OP * v_{j} | -c | r_{j} <- OP*v_{j} - V_{j} * w_{j} | -c %-----------------------------------------% -c -c -c %------------------------------------------% -c | Compute the j Fourier coefficients w_{j} | -c | WORKD(IPJ:IPJ+N-1) contains B*OP*v_{j}. | -c %------------------------------------------% -c - if (mode .ne. 2 ) then - call dgemv('T', n, j, one, v, ldv, workd(ipj), 1, zero, - & workd(irj), 1) - else if (mode .eq. 2) then - call dgemv('T', n, j, one, v, ldv, workd(ivj), 1, zero, - & workd(irj), 1) - end if -c -c %--------------------------------------% -c | Orthgonalize r_{j} against V_{j}. | -c | RESID contains OP*v_{j}. See STEP 3. | -c %--------------------------------------% -c - call dgemv('N', n, j, -one, v, ldv, workd(irj), 1, one, - & resid, 1) -c -c %--------------------------------------% -c | Extend H to have j rows and columns. | -c %--------------------------------------% -c - h(j,2) = workd(irj + j - 1) - if (j .eq. 1 .or. rstart) then - h(j,1) = zero - else - h(j,1) = rnorm - end if - call igraphsecond (t4) -c - orth1 = .true. - iter = 0 -c - call igraphsecond (t2) - if (bmat .eq. 'G') then - nbx = nbx + 1 - call dcopy (n, resid, 1, workd(irj), 1) - ipntr(1) = irj - ipntr(2) = ipj - ido = 2 -c -c %----------------------------------% -c | Exit in order to compute B*r_{j} | -c %----------------------------------% -c - go to 9000 - else if (bmat .eq. 'I') then - call dcopy (n, resid, 1, workd(ipj), 1) - end if - 70 continue -c -c %---------------------------------------------------% -c | Back from reverse communication if ORTH1 = .true. | -c | WORKD(IPJ:IPJ+N-1) := B*r_{j}. | -c %---------------------------------------------------% -c - if (bmat .eq. 'G') then - call igraphsecond (t3) - tmvbx = tmvbx + (t3 - t2) - end if -c - orth1 = .false. -c -c %------------------------------% -c | Compute the B-norm of r_{j}. | -c %------------------------------% -c - if (bmat .eq. 'G') then - rnorm = ddot (n, resid, 1, workd(ipj), 1) - rnorm = sqrt(abs(rnorm)) - else if (bmat .eq. 'I') then - rnorm = dnrm2(n, resid, 1) - end if -c -c %-----------------------------------------------------------% -c | STEP 5: Re-orthogonalization / Iterative refinement phase | -c | Maximum NITER_ITREF tries. | -c | | -c | s = V_{j}^T * B * r_{j} | -c | r_{j} = r_{j} - V_{j}*s | -c | alphaj = alphaj + s_{j} | -c | | -c | The stopping criteria used for iterative refinement is | -c | discussed in Parlett's book SEP, page 107 and in Gragg & | -c | Reichel ACM TOMS paper; Algorithm 686, Dec. 1990. | -c | Determine if we need to correct the residual. The goal is | -c | to enforce ||v(:,1:j)^T * r_{j}|| .le. eps * || r_{j} || | -c %-----------------------------------------------------------% -c - if (rnorm .gt. 0.717*wnorm) go to 100 - nrorth = nrorth + 1 -c -c %---------------------------------------------------% -c | Enter the Iterative refinement phase. If further | -c | refinement is necessary, loop back here. The loop | -c | variable is ITER. Perform a step of Classical | -c | Gram-Schmidt using all the Arnoldi vectors V_{j} | -c %---------------------------------------------------% -c - 80 continue -c - if (msglvl .gt. 2) then - xtemp(1) = wnorm - xtemp(2) = rnorm - call igraphdvout (logfil, 2, xtemp, ndigit, - & '_saitr: re-orthonalization ; wnorm and rnorm are') - end if -c -c %----------------------------------------------------% -c | Compute V_{j}^T * B * r_{j}. | -c | WORKD(IRJ:IRJ+J-1) = v(:,1:J)'*WORKD(IPJ:IPJ+N-1). | -c %----------------------------------------------------% -c - call dgemv ('T', n, j, one, v, ldv, workd(ipj), 1, - & zero, workd(irj), 1) -c -c %----------------------------------------------% -c | Compute the correction to the residual: | -c | r_{j} = r_{j} - V_{j} * WORKD(IRJ:IRJ+J-1). | -c | The correction to H is v(:,1:J)*H(1:J,1:J) + | -c | v(:,1:J)*WORKD(IRJ:IRJ+J-1)*e'_j, but only | -c | H(j,j) is updated. | -c %----------------------------------------------% -c - call dgemv ('N', n, j, -one, v, ldv, workd(irj), 1, - & one, resid, 1) -c - if (j .eq. 1 .or. rstart) h(j,1) = zero - h(j,2) = h(j,2) + workd(irj + j - 1) -c - orth2 = .true. - call igraphsecond (t2) - if (bmat .eq. 'G') then - nbx = nbx + 1 - call dcopy (n, resid, 1, workd(irj), 1) - ipntr(1) = irj - ipntr(2) = ipj - ido = 2 -c -c %-----------------------------------% -c | Exit in order to compute B*r_{j}. | -c | r_{j} is the corrected residual. | -c %-----------------------------------% -c - go to 9000 - else if (bmat .eq. 'I') then - call dcopy (n, resid, 1, workd(ipj), 1) - end if - 90 continue -c -c %---------------------------------------------------% -c | Back from reverse communication if ORTH2 = .true. | -c %---------------------------------------------------% -c - if (bmat .eq. 'G') then - call igraphsecond (t3) - tmvbx = tmvbx + (t3 - t2) - end if -c -c %-----------------------------------------------------% -c | Compute the B-norm of the corrected residual r_{j}. | -c %-----------------------------------------------------% -c - if (bmat .eq. 'G') then - rnorm1 = ddot (n, resid, 1, workd(ipj), 1) - rnorm1 = sqrt(abs(rnorm1)) - else if (bmat .eq. 'I') then - rnorm1 = dnrm2(n, resid, 1) - end if -c - if (msglvl .gt. 0 .and. iter .gt. 0) then - call igraphivout (logfil, 1, [j], ndigit, - & '_saitr: Iterative refinement for Arnoldi residual') - if (msglvl .gt. 2) then - xtemp(1) = rnorm - xtemp(2) = rnorm1 - call igraphdvout (logfil, 2, xtemp, ndigit, - & '_saitr: iterative refinement ; rnorm and rnorm1 are') - end if - end if -c -c %-----------------------------------------% -c | Determine if we need to perform another | -c | step of re-orthogonalization. | -c %-----------------------------------------% -c - if (rnorm1 .gt. 0.717*rnorm) then -c -c %--------------------------------% -c | No need for further refinement | -c %--------------------------------% -c - rnorm = rnorm1 -c - else -c -c %-------------------------------------------% -c | Another step of iterative refinement step | -c | is required. NITREF is used by stat.h | -c %-------------------------------------------% -c - nitref = nitref + 1 - rnorm = rnorm1 - iter = iter + 1 - if (iter .le. 1) go to 80 -c -c %-------------------------------------------------% -c | Otherwise RESID is numerically in the span of V | -c %-------------------------------------------------% -c - do 95 jj = 1, n - resid(jj) = zero - 95 continue - rnorm = zero - end if -c -c %----------------------------------------------% -c | Branch here directly if iterative refinement | -c | wasn't necessary or after at most NITER_REF | -c | steps of iterative refinement. | -c %----------------------------------------------% -c - 100 continue -c - rstart = .false. - orth2 = .false. -c - call igraphsecond (t5) - titref = titref + (t5 - t4) -c -c %----------------------------------------------------------% -c | Make sure the last off-diagonal element is non negative | -c | If not perform a similarity transformation on H(1:j,1:j) | -c | and scale v(:,j) by -1. | -c %----------------------------------------------------------% -c - if (h(j,1) .lt. zero) then - h(j,1) = -h(j,1) - if ( j .lt. k+np) then - call dscal(n, -one, v(1,j+1), 1) - else - call dscal(n, -one, resid, 1) - end if - end if -c -c %------------------------------------% -c | STEP 6: Update j = j+1; Continue | -c %------------------------------------% -c - j = j + 1 - if (j .gt. k+np) then - call igraphsecond (t1) - tsaitr = tsaitr + (t1 - t0) - ido = 99 -c - if (msglvl .gt. 1) then - call igraphdvout (logfil, k+np, h(1,2), ndigit, - & '_saitr: main diagonal of matrix H of step K+NP.') - if (k+np .gt. 1) then - call igraphdvout (logfil, k+np-1, h(2,1), ndigit, - & '_saitr: sub diagonal of matrix H of step K+NP.') - end if - end if -c - go to 9000 - end if -c -c %--------------------------------------------------------% -c | Loop back to extend the factorization by another step. | -c %--------------------------------------------------------% -c - go to 1000 -c -c %---------------------------------------------------------------% -c | | -c | E N D O F M A I N I T E R A T I O N L O O P | -c | | -c %---------------------------------------------------------------% -c - 9000 continue - return -c -c %---------------% -c | End of igraphdsaitr | -c %---------------% -c - end diff --git a/src/rigraph/vendor/arpack/dsapps.f b/src/rigraph/vendor/arpack/dsapps.f deleted file mode 100644 index 850e3fd..0000000 --- a/src/rigraph/vendor/arpack/dsapps.f +++ /dev/null @@ -1,516 +0,0 @@ -c----------------------------------------------------------------------- -c\BeginDoc -c -c\Name: igraphdsapps -c -c\Description: -c Given the Arnoldi factorization -c -c A*V_{k} - V_{k}*H_{k} = r_{k+p}*e_{k+p}^T, -c -c apply NP shifts implicitly resulting in -c -c A*(V_{k}*Q) - (V_{k}*Q)*(Q^T* H_{k}*Q) = r_{k+p}*e_{k+p}^T * Q -c -c where Q is an orthogonal matrix of order KEV+NP. Q is the product of -c rotations resulting from the NP bulge chasing sweeps. The updated Arnoldi -c factorization becomes: -c -c A*VNEW_{k} - VNEW_{k}*HNEW_{k} = rnew_{k}*e_{k}^T. -c -c\Usage: -c call igraphdsapps -c ( N, KEV, NP, SHIFT, V, LDV, H, LDH, RESID, Q, LDQ, WORKD ) -c -c\Arguments -c N Integer. (INPUT) -c Problem size, i.e. dimension of matrix A. -c -c KEV Integer. (INPUT) -c INPUT: KEV+NP is the size of the input matrix H. -c OUTPUT: KEV is the size of the updated matrix HNEW. -c -c NP Integer. (INPUT) -c Number of implicit shifts to be applied. -c -c SHIFT Double precision array of length NP. (INPUT) -c The shifts to be applied. -c -c V Double precision N by (KEV+NP) array. (INPUT/OUTPUT) -c INPUT: V contains the current KEV+NP Arnoldi vectors. -c OUTPUT: VNEW = V(1:n,1:KEV); the updated Arnoldi vectors -c are in the first KEV columns of V. -c -c LDV Integer. (INPUT) -c Leading dimension of V exactly as declared in the calling -c program. -c -c H Double precision (KEV+NP) by 2 array. (INPUT/OUTPUT) -c INPUT: H contains the symmetric tridiagonal matrix of the -c Arnoldi factorization with the subdiagonal in the 1st column -c starting at H(2,1) and the main diagonal in the 2nd column. -c OUTPUT: H contains the updated tridiagonal matrix in the -c KEV leading submatrix. -c -c LDH Integer. (INPUT) -c Leading dimension of H exactly as declared in the calling -c program. -c -c RESID Double precision array of length (N). (INPUT/OUTPUT) -c INPUT: RESID contains the the residual vector r_{k+p}. -c OUTPUT: RESID is the updated residual vector rnew_{k}. -c -c Q Double precision KEV+NP by KEV+NP work array. (WORKSPACE) -c Work array used to accumulate the rotations during the bulge -c chase sweep. -c -c LDQ Integer. (INPUT) -c Leading dimension of Q exactly as declared in the calling -c program. -c -c WORKD Double precision work array of length 2*N. (WORKSPACE) -c Distributed array used in the application of the accumulated -c orthogonal matrix Q. -c -c\EndDoc -c -c----------------------------------------------------------------------- -c -c\BeginLib -c -c\Local variables: -c xxxxxx real -c -c\References: -c 1. D.C. Sorensen, "Implicit Application of Polynomial Filters in -c a k-Step Arnoldi Method", SIAM J. Matr. Anal. Apps., 13 (1992), -c pp 357-385. -c 2. R.B. Lehoucq, "Analysis and Implementation of an Implicitly -c Restarted Arnoldi Iteration", Rice University Technical Report -c TR95-13, Department of Computational and Applied Mathematics. -c -c\Routines called: -c igraphivout ARPACK utility routine that prints integers. -c igraphsecond ARPACK utility routine for timing. -c igraphdvout ARPACK utility routine that prints vectors. -c dlamch LAPACK routine that determines machine constants. -c dlartg LAPACK Givens rotation construction routine. -c dlacpy LAPACK matrix copy routine. -c dlaset LAPACK matrix initialization routine. -c dgemv Level 2 BLAS routine for matrix vector multiplication. -c daxpy Level 1 BLAS that computes a vector triad. -c dcopy Level 1 BLAS that copies one vector to another. -c dscal Level 1 BLAS that scales a vector. -c -c\Author -c Danny Sorensen Phuong Vu -c Richard Lehoucq CRPC / Rice University -c Dept. of Computational & Houston, Texas -c Applied Mathematics -c Rice University -c Houston, Texas -c -c\Revision history: -c 12/16/93: Version ' 2.1' -c -c\SCCS Information: @(#) -c FILE: sapps.F SID: 2.5 DATE OF SID: 4/19/96 RELEASE: 2 -c -c\Remarks -c 1. In this version, each shift is applied to all the subblocks of -c the tridiagonal matrix H and not just to the submatrix that it -c comes from. This routine assumes that the subdiagonal elements -c of H that are stored in h(1:kev+np,1) are nonegative upon input -c and enforce this condition upon output. This version incorporates -c deflation. See code for documentation. -c -c\EndLib -c -c----------------------------------------------------------------------- -c - subroutine igraphdsapps - & ( n, kev, np, shift, v, ldv, h, ldh, resid, q, ldq, workd ) -c -c %----------------------------------------------------% -c | Include files for debugging and timing information | -c %----------------------------------------------------% -c - include 'debug.h' - include 'stat.h' -c -c %------------------% -c | Scalar Arguments | -c %------------------% -c - integer kev, ldh, ldq, ldv, n, np -c -c %-----------------% -c | Array Arguments | -c %-----------------% -c - Double precision - & h(ldh,2), q(ldq,kev+np), resid(n), shift(np), - & v(ldv,kev+np), workd(2*n) -c -c %------------% -c | Parameters | -c %------------% -c - Double precision - & one, zero - parameter (one = 1.0D+0, zero = 0.0D+0) -c -c %---------------% -c | Local Scalars | -c %---------------% -c - integer i, iend, istart, itop, j, jj, kplusp, msglvl - logical first - Double precision - & a1, a2, a3, a4, big, c, epsmch, f, g, r, s - save epsmch, first -c -c -c %----------------------% -c | External Subroutines | -c %----------------------% -c - external daxpy, dcopy, dscal, dlacpy, dlartg, dlaset, - & igraphdvout, igraphivout, igraphsecond, dgemv -c -c %--------------------% -c | External Functions | -c %--------------------% -c - Double precision - & dlamch - external dlamch -c -c %----------------------% -c | Intrinsics Functions | -c %----------------------% -c - intrinsic abs -c -c %----------------% -c | Data statments | -c %----------------% -c - data first / .true. / -c -c %-----------------------% -c | Executable Statements | -c %-----------------------% -c - if (first) then - epsmch = dlamch('Epsilon-Machine') - first = .false. - end if - itop = 1 -c -c %-------------------------------% -c | Initialize timing statistics | -c | & message level for debugging | -c %-------------------------------% -c - call igraphsecond (t0) - msglvl = msapps -c - kplusp = kev + np -c -c %----------------------------------------------% -c | Initialize Q to the identity matrix of order | -c | kplusp used to accumulate the rotations. | -c %----------------------------------------------% -c - call dlaset ('All', kplusp, kplusp, zero, one, q, ldq) -c -c %----------------------------------------------% -c | Quick return if there are no shifts to apply | -c %----------------------------------------------% -c - if (np .eq. 0) go to 9000 -c -c %----------------------------------------------------------% -c | Apply the np shifts implicitly. Apply each shift to the | -c | whole matrix and not just to the submatrix from which it | -c | comes. | -c %----------------------------------------------------------% -c - do 90 jj = 1, np -c - istart = itop -c -c %----------------------------------------------------------% -c | Check for splitting and deflation. Currently we consider | -c | an off-diagonal element h(i+1,1) negligible if | -c | h(i+1,1) .le. epsmch*( |h(i,2)| + |h(i+1,2)| ) | -c | for i=1:KEV+NP-1. | -c | If above condition tests true then we set h(i+1,1) = 0. | -c | Note that h(1:KEV+NP,1) are assumed to be non negative. | -c %----------------------------------------------------------% -c - 20 continue -c -c %------------------------------------------------% -c | The following loop exits early if we encounter | -c | a negligible off diagonal element. | -c %------------------------------------------------% -c - do 30 i = istart, kplusp-1 - big = abs(h(i,2)) + abs(h(i+1,2)) - if (h(i+1,1) .le. epsmch*big) then - if (msglvl .gt. 0) then - call igraphivout (logfil, 1, i, ndigit, - & '_sapps: deflation at row/column no.') - call igraphivout (logfil, 1, jj, ndigit, - & '_sapps: occured before shift number.') - call igraphdvout (logfil, 1, h(i+1,1), ndigit, - & '_sapps: the corresponding off diagonal element') - end if - h(i+1,1) = zero - iend = i - go to 40 - end if - 30 continue - iend = kplusp - 40 continue -c - if (istart .lt. iend) then -c -c %--------------------------------------------------------% -c | Construct the plane rotation G'(istart,istart+1,theta) | -c | that attempts to drive h(istart+1,1) to zero. | -c %--------------------------------------------------------% -c - f = h(istart,2) - shift(jj) - g = h(istart+1,1) - call dlartg (f, g, c, s, r) -c -c %-------------------------------------------------------% -c | Apply rotation to the left and right of H; | -c | H <- G' * H * G, where G = G(istart,istart+1,theta). | -c | This will create a "bulge". | -c %-------------------------------------------------------% -c - a1 = c*h(istart,2) + s*h(istart+1,1) - a2 = c*h(istart+1,1) + s*h(istart+1,2) - a4 = c*h(istart+1,2) - s*h(istart+1,1) - a3 = c*h(istart+1,1) - s*h(istart,2) - h(istart,2) = c*a1 + s*a2 - h(istart+1,2) = c*a4 - s*a3 - h(istart+1,1) = c*a3 + s*a4 -c -c %----------------------------------------------------% -c | Accumulate the rotation in the matrix Q; Q <- Q*G | -c %----------------------------------------------------% -c - do 60 j = 1, min(istart+jj,kplusp) - a1 = c*q(j,istart) + s*q(j,istart+1) - q(j,istart+1) = - s*q(j,istart) + c*q(j,istart+1) - q(j,istart) = a1 - 60 continue -c -c -c %----------------------------------------------% -c | The following loop chases the bulge created. | -c | Note that the previous rotation may also be | -c | done within the following loop. But it is | -c | kept separate to make the distinction among | -c | the bulge chasing sweeps and the first plane | -c | rotation designed to drive h(istart+1,1) to | -c | zero. | -c %----------------------------------------------% -c - do 70 i = istart+1, iend-1 -c -c %----------------------------------------------% -c | Construct the plane rotation G'(i,i+1,theta) | -c | that zeros the i-th bulge that was created | -c | by G(i-1,i,theta). g represents the bulge. | -c %----------------------------------------------% -c - f = h(i,1) - g = s*h(i+1,1) -c -c %----------------------------------% -c | Final update with G(i-1,i,theta) | -c %----------------------------------% -c - h(i+1,1) = c*h(i+1,1) - call dlartg (f, g, c, s, r) -c -c %-------------------------------------------% -c | The following ensures that h(1:iend-1,1), | -c | the first iend-2 off diagonal of elements | -c | H, remain non negative. | -c %-------------------------------------------% -c - if (r .lt. zero) then - r = -r - c = -c - s = -s - end if -c -c %--------------------------------------------% -c | Apply rotation to the left and right of H; | -c | H <- G * H * G', where G = G(i,i+1,theta) | -c %--------------------------------------------% -c - h(i,1) = r -c - a1 = c*h(i,2) + s*h(i+1,1) - a2 = c*h(i+1,1) + s*h(i+1,2) - a3 = c*h(i+1,1) - s*h(i,2) - a4 = c*h(i+1,2) - s*h(i+1,1) -c - h(i,2) = c*a1 + s*a2 - h(i+1,2) = c*a4 - s*a3 - h(i+1,1) = c*a3 + s*a4 -c -c %----------------------------------------------------% -c | Accumulate the rotation in the matrix Q; Q <- Q*G | -c %----------------------------------------------------% -c - do 50 j = 1, min( j+jj, kplusp ) - a1 = c*q(j,i) + s*q(j,i+1) - q(j,i+1) = - s*q(j,i) + c*q(j,i+1) - q(j,i) = a1 - 50 continue -c - 70 continue -c - end if -c -c %--------------------------% -c | Update the block pointer | -c %--------------------------% -c - istart = iend + 1 -c -c %------------------------------------------% -c | Make sure that h(iend,1) is non-negative | -c | If not then set h(iend,1) <-- -h(iend,1) | -c | and negate the last column of Q. | -c | We have effectively carried out a | -c | similarity on transformation H | -c %------------------------------------------% -c - if (h(iend,1) .lt. zero) then - h(iend,1) = -h(iend,1) - call dscal(kplusp, -one, q(1,iend), 1) - end if -c -c %--------------------------------------------------------% -c | Apply the same shift to the next block if there is any | -c %--------------------------------------------------------% -c - if (iend .lt. kplusp) go to 20 -c -c %-----------------------------------------------------% -c | Check if we can increase the the start of the block | -c %-----------------------------------------------------% -c - do 80 i = itop, kplusp-1 - if (h(i+1,1) .gt. zero) go to 90 - itop = itop + 1 - 80 continue -c -c %-----------------------------------% -c | Finished applying the jj-th shift | -c %-----------------------------------% -c - 90 continue -c -c %------------------------------------------% -c | All shifts have been applied. Check for | -c | more possible deflation that might occur | -c | after the last shift is applied. | -c %------------------------------------------% -c - do 100 i = itop, kplusp-1 - big = abs(h(i,2)) + abs(h(i+1,2)) - if (h(i+1,1) .le. epsmch*big) then - if (msglvl .gt. 0) then - call igraphivout (logfil, 1, i, ndigit, - & '_sapps: deflation at row/column no.') - call igraphdvout (logfil, 1, h(i+1,1), ndigit, - & '_sapps: the corresponding off diagonal element') - end if - h(i+1,1) = zero - end if - 100 continue -c -c %-------------------------------------------------% -c | Compute the (kev+1)-st column of (V*Q) and | -c | temporarily store the result in WORKD(N+1:2*N). | -c | This is not necessary if h(kev+1,1) = 0. | -c %-------------------------------------------------% -c - if ( h(kev+1,1) .gt. zero ) - & call dgemv ('N', n, kplusp, one, v, ldv, - & q(1,kev+1), 1, zero, workd(n+1), 1) -c -c %-------------------------------------------------------% -c | Compute column 1 to kev of (V*Q) in backward order | -c | taking advantage that Q is an upper triangular matrix | -c | with lower bandwidth np. | -c | Place results in v(:,kplusp-kev:kplusp) temporarily. | -c %-------------------------------------------------------% -c - do 130 i = 1, kev - call dgemv ('N', n, kplusp-i+1, one, v, ldv, - & q(1,kev-i+1), 1, zero, workd, 1) - call dcopy (n, workd, 1, v(1,kplusp-i+1), 1) - 130 continue -c -c %-------------------------------------------------% -c | Move v(:,kplusp-kev+1:kplusp) into v(:,1:kev). | -c %-------------------------------------------------% -c - call dlacpy ('All', n, kev, v(1,np+1), ldv, v, ldv) -c -c %--------------------------------------------% -c | Copy the (kev+1)-st column of (V*Q) in the | -c | appropriate place if h(kev+1,1) .ne. zero. | -c %--------------------------------------------% -c - if ( h(kev+1,1) .gt. zero ) - & call dcopy (n, workd(n+1), 1, v(1,kev+1), 1) -c -c %-------------------------------------% -c | Update the residual vector: | -c | r <- sigmak*r + betak*v(:,kev+1) | -c | where | -c | sigmak = (e_{kev+p}'*Q)*e_{kev} | -c | betak = e_{kev+1}'*H*e_{kev} | -c %-------------------------------------% -c - call dscal (n, q(kplusp,kev), resid, 1) - if (h(kev+1,1) .gt. zero) - & call daxpy (n, h(kev+1,1), v(1,kev+1), 1, resid, 1) -c - if (msglvl .gt. 1) then - call igraphdvout (logfil, 1, q(kplusp,kev), ndigit, - & '_sapps: sigmak of the updated residual vector') - call igraphdvout (logfil, 1, h(kev+1,1), ndigit, - & '_sapps: betak of the updated residual vector') - call igraphdvout (logfil, kev, h(1,2), ndigit, - & '_sapps: updated main diagonal of H for next iteration') - if (kev .gt. 1) then - call igraphdvout (logfil, kev-1, h(2,1), ndigit, - & '_sapps: updated sub diagonal of H for next iteration') - end if - end if -c - call igraphsecond (t1) - tsapps = tsapps + (t1 - t0) -c - 9000 continue - return -c -c %---------------% -c | End of igraphdsapps | -c %---------------% -c - end diff --git a/src/rigraph/vendor/arpack/dsaup2.f b/src/rigraph/vendor/arpack/dsaup2.f deleted file mode 100644 index 116dd31..0000000 --- a/src/rigraph/vendor/arpack/dsaup2.f +++ /dev/null @@ -1,853 +0,0 @@ -c----------------------------------------------------------------------- -c\BeginDoc -c -c\Name: igraphdsaup2 -c -c\Description: -c Intermediate level interface called by igraphdsaupd. -c -c\Usage: -c call igraphdsaup2 -c ( IDO, BMAT, N, WHICH, NEV, NP, TOL, RESID, MODE, IUPD, -c ISHIFT, MXITER, V, LDV, H, LDH, RITZ, BOUNDS, Q, LDQ, WORKL, -c IPNTR, WORKD, INFO ) -c -c\Arguments -c -c IDO, BMAT, N, WHICH, NEV, TOL, RESID: same as defined in igraphdsaupd. -c MODE, ISHIFT, MXITER: see the definition of IPARAM in igraphdsaupd. -c -c NP Integer. (INPUT/OUTPUT) -c Contains the number of implicit shifts to apply during -c each Arnoldi/Lanczos iteration. -c If ISHIFT=1, NP is adjusted dynamically at each iteration -c to accelerate convergence and prevent stagnation. -c This is also roughly equal to the number of matrix-vector -c products (involving the operator OP) per Arnoldi iteration. -c The logic for adjusting is contained within the current -c subroutine. -c If ISHIFT=0, NP is the number of shifts the user needs -c to provide via reverse comunication. 0 < NP < NCV-NEV. -c NP may be less than NCV-NEV since a leading block of the current -c upper Tridiagonal matrix has split off and contains "unwanted" -c Ritz values. -c Upon termination of the IRA iteration, NP contains the number -c of "converged" wanted Ritz values. -c -c IUPD Integer. (INPUT) -c IUPD .EQ. 0: use explicit restart instead implicit update. -c IUPD .NE. 0: use implicit update. -c -c V Double precision N by (NEV+NP) array. (INPUT/OUTPUT) -c The Lanczos basis vectors. -c -c LDV Integer. (INPUT) -c Leading dimension of V exactly as declared in the calling -c program. -c -c H Double precision (NEV+NP) by 2 array. (OUTPUT) -c H is used to store the generated symmetric tridiagonal matrix -c The subdiagonal is stored in the first column of H starting -c at H(2,1). The main diagonal is stored in the igraphsecond column -c of H starting at H(1,2). If igraphdsaup2 converges store the -c B-norm of the final residual vector in H(1,1). -c -c LDH Integer. (INPUT) -c Leading dimension of H exactly as declared in the calling -c program. -c -c RITZ Double precision array of length NEV+NP. (OUTPUT) -c RITZ(1:NEV) contains the computed Ritz values of OP. -c -c BOUNDS Double precision array of length NEV+NP. (OUTPUT) -c BOUNDS(1:NEV) contain the error bounds corresponding to RITZ. -c -c Q Double precision (NEV+NP) by (NEV+NP) array. (WORKSPACE) -c Private (replicated) work array used to accumulate the -c rotation in the shift application step. -c -c LDQ Integer. (INPUT) -c Leading dimension of Q exactly as declared in the calling -c program. -c -c WORKL Double precision array of length at least 3*(NEV+NP). (INPUT/WORKSPACE) -c Private (replicated) array on each PE or array allocated on -c the front end. It is used in the computation of the -c tridiagonal eigenvalue problem, the calculation and -c application of the shifts and convergence checking. -c If ISHIFT .EQ. O and IDO .EQ. 3, the first NP locations -c of WORKL are used in reverse communication to hold the user -c supplied shifts. -c -c IPNTR Integer array of length 3. (OUTPUT) -c Pointer to mark the starting locations in the WORKD for -c vectors used by the Lanczos iteration. -c ------------------------------------------------------------- -c IPNTR(1): pointer to the current operand vector X. -c IPNTR(2): pointer to the current result vector Y. -c IPNTR(3): pointer to the vector B * X when used in one of -c the spectral transformation modes. X is the current -c operand. -c ------------------------------------------------------------- -c -c WORKD Double precision work array of length 3*N. (REVERSE COMMUNICATION) -c Distributed array to be used in the basic Lanczos iteration -c for reverse communication. The user should not use WORKD -c as temporary workspace during the iteration !!!!!!!!!! -c See Data Distribution Note in igraphdsaupd. -c -c INFO Integer. (INPUT/OUTPUT) -c If INFO .EQ. 0, a randomly initial residual vector is used. -c If INFO .NE. 0, RESID contains the initial residual vector, -c possibly from a previous run. -c Error flag on output. -c = 0: Normal return. -c = 1: All possible eigenvalues of OP has been found. -c NP returns the size of the invariant subspace -c spanning the operator OP. -c = 2: No shifts could be applied. -c = -8: Error return from trid. eigenvalue calculation; -c This should never happen. -c = -9: Starting vector is zero. -c = -9999: Could not build an Lanczos factorization. -c Size that was built in returned in NP. -c -c\EndDoc -c -c----------------------------------------------------------------------- -c -c\BeginLib -c -c\References: -c 1. D.C. Sorensen, "Implicit Application of Polynomial Filters in -c a k-Step Arnoldi Method", SIAM J. Matr. Anal. Apps., 13 (1992), -c pp 357-385. -c 2. R.B. Lehoucq, "Analysis and Implementation of an Implicitly -c Restarted Arnoldi Iteration", Rice University Technical Report -c TR95-13, Department of Computational and Applied Mathematics. -c 3. B.N. Parlett, "The Symmetric Eigenvalue Problem". Prentice-Hall, -c 1980. -c 4. B.N. Parlett, B. Nour-Omid, "Towards a Black Box Lanczos Program", -c Computer Physics Communications, 53 (1989), pp 169-179. -c 5. B. Nour-Omid, B.N. Parlett, T. Ericson, P.S. Jensen, "How to -c Implement the Spectral Transformation", Math. Comp., 48 (1987), -c pp 663-673. -c 6. R.G. Grimes, J.G. Lewis and H.D. Simon, "A Shifted Block Lanczos -c Algorithm for Solving Sparse Symmetric Generalized Eigenproblems", -c SIAM J. Matr. Anal. Apps., January (1993). -c 7. L. Reichel, W.B. Gragg, "Algorithm 686: FORTRAN Subroutines -c for Updating the QR decomposition", ACM TOMS, December 1990, -c Volume 16 Number 4, pp 369-377. -c -c\Routines called: -c igraphdgetv0 ARPACK initial vector generation routine. -c igraphdsaitr ARPACK Lanczos factorization routine. -c igraphdsapps ARPACK application of implicit shifts routine. -c igraphdsconv ARPACK convergence of Ritz values routine. -c igraphdseigt ARPACK compute Ritz values and error bounds routine. -c igraphdsgets ARPACK reorder Ritz values and error bounds routine. -c igraphdsortr ARPACK sorting routine. -c igraphivout ARPACK utility routine that prints integers. -c igraphsecond ARPACK utility routine for timing. -c igraphdvout ARPACK utility routine that prints vectors. -c dlamch LAPACK routine that determines machine constants. -c dcopy Level 1 BLAS that copies one vector to another. -c ddot Level 1 BLAS that computes the scalar product of two vectors. -c dnrm2 Level 1 BLAS that computes the norm of a vector. -c dscal Level 1 BLAS that scales a vector. -c dswap Level 1 BLAS that swaps two vectors. -c -c\Author -c Danny Sorensen Phuong Vu -c Richard Lehoucq CRPC / Rice University -c Dept. of Computational & Houston, Texas -c Applied Mathematics -c Rice University -c Houston, Texas -c -c\Revision history: -c 12/15/93: Version ' 2.4' -c xx/xx/95: Version ' 2.4'. (R.B. Lehoucq) -c -c\SCCS Information: @(#) -c FILE: saup2.F SID: 2.6 DATE OF SID: 8/16/96 RELEASE: 2 -c -c\EndLib -c -c----------------------------------------------------------------------- -c - subroutine igraphdsaup2 - & ( ido, bmat, n, which, nev, np, tol, resid, mode, iupd, - & ishift, mxiter, v, ldv, h, ldh, ritz, bounds, - & q, ldq, workl, ipntr, workd, info ) -c -c %----------------------------------------------------% -c | Include files for debugging and timing information | -c %----------------------------------------------------% -c - include 'debug.h' - include 'stat.h' -c -c %------------------% -c | Scalar Arguments | -c %------------------% -c - character bmat*1, which*2 - integer ido, info, ishift, iupd, ldh, ldq, ldv, mxiter, - & n, mode, nev, np - Double precision - & tol -c -c %-----------------% -c | Array Arguments | -c %-----------------% -c - integer ipntr(3) - Double precision - & bounds(nev+np), h(ldh,2), q(ldq,nev+np), resid(n), - & ritz(nev+np), v(ldv,nev+np), workd(3*n), - & workl(3*(nev+np)) -c -c %------------% -c | Parameters | -c %------------% -c - Double precision - & one, zero - parameter (one = 1.0D+0, zero = 0.0D+0) -c -c %---------------% -c | Local Scalars | -c %---------------% -c - character wprime*2 - logical cnorm, getv0, initv, update, ushift - integer ierr, iter, j, kplusp, msglvl, nconv, nevbef, nev0, - & np0, nptemp, nevd2, nevm2, kp(3) - Double precision - & rnorm, temp, eps23 - save cnorm, getv0, initv, update, ushift, - & iter, kplusp, msglvl, nconv, nev0, np0, - & rnorm, eps23 -c -c %----------------------% -c | External Subroutines | -c %----------------------% -c - external dcopy, igraphdgetv0, igraphdsaitr, dscal, - & igraphdsconv, igraphdseigt, igraphdsgets, - & igraphdsapps, igraphdsortr, igraphdvout, igraphivout, - & igraphsecond, dswap -c -c %--------------------% -c | External Functions | -c %--------------------% -c - Double precision - & ddot, dnrm2, dlamch - external ddot, dnrm2, dlamch -c -c %---------------------% -c | Intrinsic Functions | -c %---------------------% -c - intrinsic min -c -c %-----------------------% -c | Executable Statements | -c %-----------------------% -c - if (ido .eq. 0) then -c -c %-------------------------------% -c | Initialize timing statistics | -c | & message level for debugging | -c %-------------------------------% -c - call igraphsecond (t0) - msglvl = msaup2 -c -c %---------------------------------% -c | Set machine dependent constant. | -c %---------------------------------% -c - eps23 = dlamch('Epsilon-Machine') - eps23 = eps23**(2.0D+0/3.0D+0) -c -c %-------------------------------------% -c | nev0 and np0 are integer variables | -c | hold the initial values of NEV & NP | -c %-------------------------------------% -c - nev0 = nev - np0 = np -c -c %-------------------------------------% -c | kplusp is the bound on the largest | -c | Lanczos factorization built. | -c | nconv is the current number of | -c | "converged" eigenvlues. | -c | iter is the counter on the current | -c | iteration step. | -c %-------------------------------------% -c - kplusp = nev0 + np0 - nconv = 0 - iter = 0 -c -c %--------------------------------------------% -c | Set flags for computing the first NEV steps | -c | of the Lanczos factorization. | -c %--------------------------------------------% -c - getv0 = .true. - update = .false. - ushift = .false. - cnorm = .false. -c - if (info .ne. 0) then -c -c %--------------------------------------------% -c | User provides the initial residual vector. | -c %--------------------------------------------% -c - initv = .true. - info = 0 - else - initv = .false. - end if - end if -c -c %---------------------------------------------% -c | Get a possibly random starting vector and | -c | force it into the range of the operator OP. | -c %---------------------------------------------% -c - 10 continue -c - if (getv0) then - call igraphdgetv0 (ido, bmat, 1, initv, n, 1, v, ldv, resid, - & rnorm, ipntr, workd, info) -c - if (ido .ne. 99) go to 9000 -c - if (rnorm .eq. zero) then -c -c %-----------------------------------------% -c | The initial vector is zero. Error exit. | -c %-----------------------------------------% -c - info = -9 - go to 1200 - end if - getv0 = .false. - ido = 0 - end if -c -c %------------------------------------------------------------% -c | Back from reverse communication: continue with update step | -c %------------------------------------------------------------% -c - if (update) go to 20 -c -c %-------------------------------------------% -c | Back from computing user specified shifts | -c %-------------------------------------------% -c - if (ushift) go to 50 -c -c %-------------------------------------% -c | Back from computing residual norm | -c | at the end of the current iteration | -c %-------------------------------------% -c - if (cnorm) go to 100 -c -c %----------------------------------------------------------% -c | Compute the first NEV steps of the Lanczos factorization | -c %----------------------------------------------------------% -c - call igraphdsaitr (ido, bmat, n, 0, nev0, mode, resid, rnorm, v, - & ldv, h, ldh, ipntr, workd, info) -c -c %---------------------------------------------------% -c | ido .ne. 99 implies use of reverse communication | -c | to compute operations involving OP and possibly B | -c %---------------------------------------------------% -c - if (ido .ne. 99) go to 9000 -c - if (info .gt. 0) then -c -c %-----------------------------------------------------% -c | igraphdsaitr was unable to build an Lanczos factorization | -c | of length NEV0. INFO is returned with the size of | -c | the factorization built. Exit main loop. | -c %-----------------------------------------------------% -c - np = info - mxiter = iter - info = -9999 - go to 1200 - end if -c -c %--------------------------------------------------------------% -c | | -c | M A I N LANCZOS I T E R A T I O N L O O P | -c | Each iteration implicitly restarts the Lanczos | -c | factorization in place. | -c | | -c %--------------------------------------------------------------% -c - 1000 continue -c - iter = iter + 1 -c - if (msglvl .gt. 0) then - call igraphivout (logfil, 1, [iter], ndigit, - & '_saup2: **** Start of major iteration number ****') - end if - if (msglvl .gt. 1) then - call igraphivout (logfil, 1, [nev], ndigit, - & '_saup2: The length of the current Lanczos factorization') - call igraphivout (logfil, 1, [np], ndigit, - & '_saup2: Extend the Lanczos factorization by') - end if -c -c %------------------------------------------------------------% -c | Compute NP additional steps of the Lanczos factorization. | -c %------------------------------------------------------------% -c - ido = 0 - 20 continue - update = .true. -c - call igraphdsaitr (ido, bmat, n, nev, np, mode, resid, rnorm, - & v, ldv, h, ldh, ipntr, workd, info) -c -c %---------------------------------------------------% -c | ido .ne. 99 implies use of reverse communication | -c | to compute operations involving OP and possibly B | -c %---------------------------------------------------% -c - if (ido .ne. 99) go to 9000 -c - if (info .gt. 0) then -c -c %-----------------------------------------------------% -c | igraphdsaitr was unable to build an Lanczos factorization | -c | of length NEV0+NP0. INFO is returned with the size | -c | of the factorization built. Exit main loop. | -c %-----------------------------------------------------% -c - np = info - mxiter = iter - info = -9999 - go to 1200 - end if - update = .false. -c - if (msglvl .gt. 1) then - call igraphdvout (logfil, 1, [rnorm], ndigit, - & '_saup2: Current B-norm of residual for factorization') - end if -c -c %--------------------------------------------------------% -c | Compute the eigenvalues and corresponding error bounds | -c | of the current symmetric tridiagonal matrix. | -c %--------------------------------------------------------% -c - call igraphdseigt (rnorm, kplusp, h, ldh, ritz, bounds, workl, - & ierr) -c - if (ierr .ne. 0) then - info = -8 - go to 1200 - end if -c -c %----------------------------------------------------% -c | Make a copy of eigenvalues and corresponding error | -c | bounds obtained from _seigt. | -c %----------------------------------------------------% -c - call dcopy(kplusp, ritz, 1, workl(kplusp+1), 1) - call dcopy(kplusp, bounds, 1, workl(2*kplusp+1), 1) -c -c %---------------------------------------------------% -c | Select the wanted Ritz values and their bounds | -c | to be used in the convergence test. | -c | The selection is based on the requested number of | -c | eigenvalues instead of the current NEV and NP to | -c | prevent possible misconvergence. | -c | * Wanted Ritz values := RITZ(NP+1:NEV+NP) | -c | * Shifts := RITZ(1:NP) := WORKL(1:NP) | -c %---------------------------------------------------% -c - nev = nev0 - np = np0 - call igraphdsgets (ishift, which, nev, np, ritz, bounds, workl) -c -c %-------------------% -c | Convergence test. | -c %-------------------% -c - call dcopy (nev, bounds(np+1), 1, workl(np+1), 1) - call igraphdsconv (nev, ritz(np+1), workl(np+1), tol, nconv) -c - if (msglvl .gt. 2) then - kp(1) = nev - kp(2) = np - kp(3) = nconv - call igraphivout (logfil, 3, kp, ndigit, - & '_saup2: NEV, NP, NCONV are') - call igraphdvout (logfil, kplusp, ritz, ndigit, - & '_saup2: The eigenvalues of H') - call igraphdvout (logfil, kplusp, bounds, ndigit, - & '_saup2: Ritz estimates of the current NCV Ritz values') - end if -c -c %---------------------------------------------------------% -c | Count the number of unwanted Ritz values that have zero | -c | Ritz estimates. If any Ritz estimates are equal to zero | -c | then a leading block of H of order equal to at least | -c | the number of Ritz values with zero Ritz estimates has | -c | split off. None of these Ritz values may be removed by | -c | shifting. Decrease NP the number of shifts to apply. If | -c | no shifts may be applied, then prepare to exit | -c %---------------------------------------------------------% -c - nptemp = np - do 30 j=1, nptemp - if (bounds(j) .eq. zero) then - np = np - 1 - nev = nev + 1 - end if - 30 continue -c - if ( (nconv .ge. nev0) .or. - & (iter .gt. mxiter) .or. - & (np .eq. 0) ) then -c -c %------------------------------------------------% -c | Prepare to exit. Put the converged Ritz values | -c | and corresponding bounds in RITZ(1:NCONV) and | -c | BOUNDS(1:NCONV) respectively. Then sort. Be | -c | careful when NCONV > NP since we don't want to | -c | swap overlapping locations. | -c %------------------------------------------------% -c - if (which .eq. 'BE') then -c -c %-----------------------------------------------------% -c | Both ends of the spectrum are requested. | -c | Sort the eigenvalues into algebraically decreasing | -c | order first then swap low end of the spectrum next | -c | to high end in appropriate locations. | -c | NOTE: when np < floor(nev/2) be careful not to swap | -c | overlapping locations. | -c %-----------------------------------------------------% -c - wprime = 'SA' - call igraphdsortr (wprime, .true., kplusp, ritz, bounds) - nevd2 = nev / 2 - nevm2 = nev - nevd2 - if ( nev .gt. 1 ) then - call dswap ( min(nevd2,np), ritz(nevm2+1), 1, - & ritz( max(kplusp-nevd2+1,kplusp-np+1) ), 1) - call dswap ( min(nevd2,np), bounds(nevm2+1), 1, - & bounds( max(kplusp-nevd2+1,kplusp-np)+1 ), 1) - end if -c - else -c -c %--------------------------------------------------% -c | LM, SM, LA, SA case. | -c | Sort the eigenvalues of H into the an order that | -c | is opposite to WHICH, and apply the resulting | -c | order to BOUNDS. The eigenvalues are sorted so | -c | that the wanted part are always within the first | -c | NEV locations. | -c %--------------------------------------------------% -c - if (which .eq. 'LM') wprime = 'SM' - if (which .eq. 'SM') wprime = 'LM' - if (which .eq. 'LA') wprime = 'SA' - if (which .eq. 'SA') wprime = 'LA' -c - call igraphdsortr (wprime, .true., kplusp, ritz, bounds) -c - end if -c -c %--------------------------------------------------% -c | Scale the Ritz estimate of each Ritz value | -c | by 1 / max(eps23,magnitude of the Ritz value). | -c %--------------------------------------------------% -c - do 35 j = 1, nev0 - temp = max( eps23, abs(ritz(j)) ) - bounds(j) = bounds(j)/temp - 35 continue -c -c %----------------------------------------------------% -c | Sort the Ritz values according to the scaled Ritz | -c | esitmates. This will push all the converged ones | -c | towards the front of ritzr, ritzi, bounds | -c | (in the case when NCONV < NEV.) | -c %----------------------------------------------------% -c - wprime = 'LA' - call igraphdsortr(wprime, .true., nev0, bounds, ritz) -c -c %----------------------------------------------% -c | Scale the Ritz estimate back to its original | -c | value. | -c %----------------------------------------------% -c - do 40 j = 1, nev0 - temp = max( eps23, abs(ritz(j)) ) - bounds(j) = bounds(j)*temp - 40 continue -c -c %--------------------------------------------------% -c | Sort the "converged" Ritz values again so that | -c | the "threshold" values and their associated Ritz | -c | estimates appear at the appropriate position in | -c | ritz and bound. | -c %--------------------------------------------------% -c - if (which .eq. 'BE') then -c -c %------------------------------------------------% -c | Sort the "converged" Ritz values in increasing | -c | order. The "threshold" values are in the | -c | middle. | -c %------------------------------------------------% -c - wprime = 'LA' - call igraphdsortr(wprime, .true., nconv, ritz, bounds) -c - else -c -c %----------------------------------------------% -c | In LM, SM, LA, SA case, sort the "converged" | -c | Ritz values according to WHICH so that the | -c | "threshold" value appears at the front of | -c | ritz. | -c %----------------------------------------------% - - call igraphdsortr(which, .true., nconv, ritz, bounds) -c - end if -c -c %------------------------------------------% -c | Use h( 1,1 ) as storage to communicate | -c | rnorm to _seupd if needed | -c %------------------------------------------% -c - h(1,1) = rnorm -c - if (msglvl .gt. 1) then - call igraphdvout (logfil, kplusp, ritz, ndigit, - & '_saup2: Sorted Ritz values.') - call igraphdvout (logfil, kplusp, bounds, ndigit, - & '_saup2: Sorted ritz estimates.') - end if -c -c %------------------------------------% -c | Max iterations have been exceeded. | -c %------------------------------------% -c - if (iter .gt. mxiter .and. nconv .lt. nev) info = 1 -c -c %---------------------% -c | No shifts to apply. | -c %---------------------% -c - if (np .eq. 0 .and. nconv .lt. nev0) info = 2 -c - np = nconv - go to 1100 -c - else if (nconv .lt. nev .and. ishift .eq. 1) then -c -c %---------------------------------------------------% -c | Do not have all the requested eigenvalues yet. | -c | To prevent possible stagnation, adjust the number | -c | of Ritz values and the shifts. | -c %---------------------------------------------------% -c - nevbef = nev - nev = nev + min (nconv, np/2) - if (nev .eq. 1 .and. kplusp .ge. 6) then - nev = kplusp / 2 - else if (nev .eq. 1 .and. kplusp .gt. 2) then - nev = 2 - end if - np = kplusp - nev -c -c %---------------------------------------% -c | If the size of NEV was just increased | -c | resort the eigenvalues. | -c %---------------------------------------% -c - if (nevbef .lt. nev) - & call igraphdsgets (ishift, which, nev, np, ritz, bounds, - & workl) -c - end if -c - if (msglvl .gt. 0) then - call igraphivout (logfil, 1, [nconv], ndigit, - & '_saup2: no. of "converged" Ritz values at this iter.') - if (msglvl .gt. 1) then - kp(1) = nev - kp(2) = np - call igraphivout (logfil, 2, kp, ndigit, - & '_saup2: NEV and NP are') - call igraphdvout (logfil, nev, ritz(np+1), ndigit, - & '_saup2: "wanted" Ritz values.') - call igraphdvout (logfil, nev, bounds(np+1), ndigit, - & '_saup2: Ritz estimates of the "wanted" values ') - end if - end if - -c - if (ishift .eq. 0) then -c -c %-----------------------------------------------------% -c | User specified shifts: reverse communication to | -c | compute the shifts. They are returned in the first | -c | NP locations of WORKL. | -c %-----------------------------------------------------% -c - ushift = .true. - ido = 3 - go to 9000 - end if -c - 50 continue -c -c %------------------------------------% -c | Back from reverse communication; | -c | User specified shifts are returned | -c | in WORKL(1:*NP) | -c %------------------------------------% -c - ushift = .false. -c -c -c %---------------------------------------------------------% -c | Move the NP shifts to the first NP locations of RITZ to | -c | free up WORKL. This is for the non-exact shift case; | -c | in the exact shift case, igraphdsgets already handles this. | -c %---------------------------------------------------------% -c - if (ishift .eq. 0) call dcopy (np, workl, 1, ritz, 1) -c - if (msglvl .gt. 2) then - call igraphivout (logfil, 1, [np], ndigit, - & '_saup2: The number of shifts to apply ') - call igraphdvout (logfil, np, workl, ndigit, - & '_saup2: shifts selected') - if (ishift .eq. 1) then - call igraphdvout (logfil, np, bounds, ndigit, - & '_saup2: corresponding Ritz estimates') - end if - end if -c -c %---------------------------------------------------------% -c | Apply the NP0 implicit shifts by QR bulge chasing. | -c | Each shift is applied to the entire tridiagonal matrix. | -c | The first 2*N locations of WORKD are used as workspace. | -c | After igraphdsapps is done, we have a Lanczos | -c | factorization of length NEV. | -c %---------------------------------------------------------% -c - call igraphdsapps (n, nev, np, ritz, v, ldv, h, ldh, resid, - & q, ldq, workd) -c -c %---------------------------------------------% -c | Compute the B-norm of the updated residual. | -c | Keep B*RESID in WORKD(1:N) to be used in | -c | the first step of the next call to igraphdsaitr. | -c %---------------------------------------------% -c - cnorm = .true. - call igraphsecond (t2) - if (bmat .eq. 'G') then - nbx = nbx + 1 - call dcopy (n, resid, 1, workd(n+1), 1) - ipntr(1) = n + 1 - ipntr(2) = 1 - ido = 2 -c -c %----------------------------------% -c | Exit in order to compute B*RESID | -c %----------------------------------% -c - go to 9000 - else if (bmat .eq. 'I') then - call dcopy (n, resid, 1, workd, 1) - end if -c - 100 continue -c -c %----------------------------------% -c | Back from reverse communication; | -c | WORKD(1:N) := B*RESID | -c %----------------------------------% -c - if (bmat .eq. 'G') then - call igraphsecond (t3) - tmvbx = tmvbx + (t3 - t2) - end if -c - if (bmat .eq. 'G') then - rnorm = ddot (n, resid, 1, workd, 1) - rnorm = sqrt(abs(rnorm)) - else if (bmat .eq. 'I') then - rnorm = dnrm2(n, resid, 1) - end if - cnorm = .false. - 130 continue -c - if (msglvl .gt. 2) then - call igraphdvout (logfil, 1, [rnorm], ndigit, - & '_saup2: B-norm of residual for NEV factorization') - call igraphdvout (logfil, nev, h(1,2), ndigit, - & '_saup2: main diagonal of compressed H matrix') - call igraphdvout (logfil, nev-1, h(2,1), ndigit, - & '_saup2: subdiagonal of compressed H matrix') - end if -c - go to 1000 -c -c %---------------------------------------------------------------% -c | | -c | E N D O F M A I N I T E R A T I O N L O O P | -c | | -c %---------------------------------------------------------------% -c - 1100 continue -c - mxiter = iter - nev = nconv -c - 1200 continue - ido = 99 -c -c %------------% -c | Error exit | -c %------------% -c - call igraphsecond (t1) - tsaup2 = t1 - t0 -c - 9000 continue - return -c -c %---------------% -c | End of igraphdsaup2 | -c %---------------% -c - end diff --git a/src/rigraph/vendor/arpack/dsaupd.f b/src/rigraph/vendor/arpack/dsaupd.f deleted file mode 100644 index 7e85781..0000000 --- a/src/rigraph/vendor/arpack/dsaupd.f +++ /dev/null @@ -1,653 +0,0 @@ -c----------------------------------------------------------------------- -c\BeginDoc -c -c\Name: igraphdsaupd -c -c\Description: -c -c Reverse communication interface for the Implicitly Restarted Arnoldi -c Iteration. For symmetric problems this reduces to a variant of the Lanczos -c method. This method has been designed to compute approximations to a -c few eigenpairs of a linear operator OP that is real and symmetric -c with respect to a real positive semi-definite symmetric matrix B, -c i.e. -c -c B*OP = (OP')*B. -c -c Another way to express this condition is -c -c < x,OPy > = < OPx,y > where < z,w > = z'Bw . -c -c In the standard eigenproblem B is the identity matrix. -c ( A' denotes transpose of A) -c -c The computed approximate eigenvalues are called Ritz values and -c the corresponding approximate eigenvectors are called Ritz vectors. -c -c igraphdsaupd is usually called iteratively to solve one of the -c following problems: -c -c Mode 1: A*x = lambda*x, A symmetric -c ===> OP = A and B = I. -c -c Mode 2: A*x = lambda*M*x, A symmetric, M symmetric positive definite -c ===> OP = inv[M]*A and B = M. -c ===> (If M can be factored see remark 3 below) -c -c Mode 3: K*x = lambda*M*x, K symmetric, M symmetric positive semi-definite -c ===> OP = (inv[K - sigma*M])*M and B = M. -c ===> Shift-and-Invert mode -c -c Mode 4: K*x = lambda*KG*x, K symmetric positive semi-definite, -c KG symmetric indefinite -c ===> OP = (inv[K - sigma*KG])*K and B = K. -c ===> Buckling mode -c -c Mode 5: A*x = lambda*M*x, A symmetric, M symmetric positive semi-definite -c ===> OP = inv[A - sigma*M]*[A + sigma*M] and B = M. -c ===> Cayley transformed mode -c -c NOTE: The action of w <- inv[A - sigma*M]*v or w <- inv[M]*v -c should be accomplished either by a direct method -c using a sparse matrix factorization and solving -c -c [A - sigma*M]*w = v or M*w = v, -c -c or through an iterative method for solving these -c systems. If an iterative method is used, the -c convergence test must be more stringent than -c the accuracy requirements for the eigenvalue -c approximations. -c -c\Usage: -c call igraphdsaupd -c ( IDO, BMAT, N, WHICH, NEV, TOL, RESID, NCV, V, LDV, IPARAM, -c IPNTR, WORKD, WORKL, LWORKL, INFO ) -c -c\Arguments -c IDO Integer. (INPUT/OUTPUT) -c Reverse communication flag. IDO must be zero on the first -c call to igraphdsaupd. IDO will be set internally to -c indicate the type of operation to be performed. Control is -c then given back to the calling routine which has the -c responsibility to carry out the requested operation and call -c igraphdsaupd with the result. The operand is given in -c WORKD(IPNTR(1)), the result must be put in WORKD(IPNTR(2)). -c (If Mode = 2 see remark 5 below) -c ------------------------------------------------------------- -c IDO = 0: first call to the reverse communication interface -c IDO = -1: compute Y = OP * X where -c IPNTR(1) is the pointer into WORKD for X, -c IPNTR(2) is the pointer into WORKD for Y. -c This is for the initialization phase to force the -c starting vector into the range of OP. -c IDO = 1: compute Y = OP * X where -c IPNTR(1) is the pointer into WORKD for X, -c IPNTR(2) is the pointer into WORKD for Y. -c In mode 3,4 and 5, the vector B * X is already -c available in WORKD(ipntr(3)). It does not -c need to be recomputed in forming OP * X. -c IDO = 2: compute Y = B * X where -c IPNTR(1) is the pointer into WORKD for X, -c IPNTR(2) is the pointer into WORKD for Y. -c IDO = 3: compute the IPARAM(8) shifts where -c IPNTR(11) is the pointer into WORKL for -c placing the shifts. See remark 6 below. -c IDO = 99: done -c ------------------------------------------------------------- -c -c BMAT Character*1. (INPUT) -c BMAT specifies the type of the matrix B that defines the -c semi-inner product for the operator OP. -c B = 'I' -> standard eigenvalue problem A*x = lambda*x -c B = 'G' -> generalized eigenvalue problem A*x = lambda*B*x -c -c N Integer. (INPUT) -c Dimension of the eigenproblem. -c -c WHICH Character*2. (INPUT) -c Specify which of the Ritz values of OP to compute. -c -c 'LA' - compute the NEV largest (algebraic) eigenvalues. -c 'SA' - compute the NEV smallest (algebraic) eigenvalues. -c 'LM' - compute the NEV largest (in magnitude) eigenvalues. -c 'SM' - compute the NEV smallest (in magnitude) eigenvalues. -c 'BE' - compute NEV eigenvalues, half from each end of the -c spectrum. When NEV is odd, compute one more from the -c high end than from the low end. -c (see remark 1 below) -c -c NEV Integer. (INPUT) -c Number of eigenvalues of OP to be computed. 0 < NEV < N. -c -c TOL Double precision scalar. (INPUT) -c Stopping criterion: the relative accuracy of the Ritz value -c is considered acceptable if BOUNDS(I) .LE. TOL*ABS(RITZ(I)). -c If TOL .LE. 0. is passed a default is set: -c DEFAULT = DLAMCH('EPS') (machine precision as computed -c by the LAPACK auxiliary subroutine DLAMCH). -c -c RESID Double precision array of length N. (INPUT/OUTPUT) -c On INPUT: -c If INFO .EQ. 0, a random initial residual vector is used. -c If INFO .NE. 0, RESID contains the initial residual vector, -c possibly from a previous run. -c On OUTPUT: -c RESID contains the final residual vector. -c -c NCV Integer. (INPUT) -c Number of columns of the matrix V (less than or equal to N). -c This will indicate how many Lanczos vectors are generated -c at each iteration. After the startup phase in which NEV -c Lanczos vectors are generated, the algorithm generates -c NCV-NEV Lanczos vectors at each subsequent update iteration. -c Most of the cost in generating each Lanczos vector is in the -c matrix-vector product OP*x. (See remark 4 below). -c -c V Double precision N by NCV array. (OUTPUT) -c The NCV columns of V contain the Lanczos basis vectors. -c -c LDV Integer. (INPUT) -c Leading dimension of V exactly as declared in the calling -c program. -c -c IPARAM Integer array of length 11. (INPUT/OUTPUT) -c IPARAM(1) = ISHIFT: method for selecting the implicit shifts. -c The shifts selected at each iteration are used to restart -c the Arnoldi iteration in an implicit fashion. -c ------------------------------------------------------------- -c ISHIFT = 0: the shifts are provided by the user via -c reverse communication. The NCV eigenvalues of -c the current tridiagonal matrix T are returned in -c the part of WORKL array corresponding to RITZ. -c See remark 6 below. -c ISHIFT = 1: exact shifts with respect to the reduced -c tridiagonal matrix T. This is equivalent to -c restarting the iteration with a starting vector -c that is a linear combination of Ritz vectors -c associated with the "wanted" Ritz values. -c ------------------------------------------------------------- -c -c IPARAM(2) = LEVEC -c No longer referenced. See remark 2 below. -c -c IPARAM(3) = MXITER -c On INPUT: maximum number of Arnoldi update iterations allowed. -c On OUTPUT: actual number of Arnoldi update iterations taken. -c -c IPARAM(4) = NB: blocksize to be used in the recurrence. -c The code currently works only for NB = 1. -c -c IPARAM(5) = NCONV: number of "converged" Ritz values. -c This represents the number of Ritz values that satisfy -c the convergence criterion. -c -c IPARAM(6) = IUPD -c No longer referenced. Implicit restarting is ALWAYS used. -c -c IPARAM(7) = MODE -c On INPUT determines what type of eigenproblem is being solved. -c Must be 1,2,3,4,5; See under \Description of igraphdsaupd for the -c five modes available. -c -c IPARAM(8) = NP -c When ido = 3 and the user provides shifts through reverse -c communication (IPARAM(1)=0), igraphdsaupd returns NP, the number -c of shifts the user is to provide. 0 < NP <=NCV-NEV. See Remark -c 6 below. -c -c IPARAM(9) = NUMOP, IPARAM(10) = NUMOPB, IPARAM(11) = NUMREO, -c OUTPUT: NUMOP = total number of OP*x operations, -c NUMOPB = total number of B*x operations if BMAT='G', -c NUMREO = total number of steps of re-orthogonalization. -c -c IPNTR Integer array of length 11. (OUTPUT) -c Pointer to mark the starting locations in the WORKD and WORKL -c arrays for matrices/vectors used by the Lanczos iteration. -c ------------------------------------------------------------- -c IPNTR(1): pointer to the current operand vector X in WORKD. -c IPNTR(2): pointer to the current result vector Y in WORKD. -c IPNTR(3): pointer to the vector B * X in WORKD when used in -c the shift-and-invert mode. -c IPNTR(4): pointer to the next available location in WORKL -c that is untouched by the program. -c IPNTR(5): pointer to the NCV by 2 tridiagonal matrix T in WORKL. -c IPNTR(6): pointer to the NCV RITZ values array in WORKL. -c IPNTR(7): pointer to the Ritz estimates in array WORKL associated -c with the Ritz values located in RITZ in WORKL. -c IPNTR(11): pointer to the NP shifts in WORKL. See Remark 6 below. -c -c Note: IPNTR(8:10) is only referenced by igraphdseupd. See Remark 2. -c IPNTR(8): pointer to the NCV RITZ values of the original system. -c IPNTR(9): pointer to the NCV corresponding error bounds. -c IPNTR(10): pointer to the NCV by NCV matrix of eigenvectors -c of the tridiagonal matrix T. Only referenced by -c igraphdseupd if RVEC = .TRUE. See Remarks. -c ------------------------------------------------------------- -c -c WORKD Double precision work array of length 3*N. (REVERSE COMMUNICATION) -c Distributed array to be used in the basic Arnoldi iteration -c for reverse communication. The user should not use WORKD -c as temporary workspace during the iteration. Upon termination -c WORKD(1:N) contains B*RESID(1:N). If the Ritz vectors are desired -c subroutine igraphdseupd uses this output. -c See Data Distribution Note below. -c -c WORKL Double precision work array of length LWORKL. (OUTPUT/WORKSPACE) -c Private (replicated) array on each PE or array allocated on -c the front end. See Data Distribution Note below. -c -c LWORKL Integer. (INPUT) -c LWORKL must be at least NCV**2 + 8*NCV . -c -c INFO Integer. (INPUT/OUTPUT) -c If INFO .EQ. 0, a randomly initial residual vector is used. -c If INFO .NE. 0, RESID contains the initial residual vector, -c possibly from a previous run. -c Error flag on output. -c = 0: Normal exit. -c = 1: Maximum number of iterations taken. -c All possible eigenvalues of OP has been found. IPARAM(5) -c returns the number of wanted converged Ritz values. -c = 2: No longer an informational error. Deprecated starting -c with release 2 of ARPACK. -c = 3: No shifts could be applied during a cycle of the -c Implicitly restarted Arnoldi iteration. One possibility -c is to increase the size of NCV relative to NEV. -c See remark 4 below. -c = -1: N must be positive. -c = -2: NEV must be positive. -c = -3: NCV must be greater than NEV and less than or equal to N. -c = -4: The maximum number of Arnoldi update iterations allowed -c must be greater than zero. -c = -5: WHICH must be one of 'LM', 'SM', 'LA', 'SA' or 'BE'. -c = -6: BMAT must be one of 'I' or 'G'. -c = -7: Length of private work array WORKL is not sufficient. -c = -8: Error return from trid. eigenvalue calculation; -c Informatinal error from LAPACK routine dsteqr. -c = -9: Starting vector is zero. -c = -10: IPARAM(7) must be 1,2,3,4,5. -c = -11: IPARAM(7) = 1 and BMAT = 'G' are incompatable. -c = -12: IPARAM(1) must be equal to 0 or 1. -c = -13: NEV and WHICH = 'BE' are incompatable. -c = -9999: Could not build an Arnoldi factorization. -c IPARAM(5) returns the size of the current Arnoldi -c factorization. The user is advised to check that -c enough workspace and array storage has been allocated. -c -c -c\Remarks -c 1. The converged Ritz values are always returned in ascending -c algebraic order. The computed Ritz values are approximate -c eigenvalues of OP. The selection of WHICH should be made -c with this in mind when Mode = 3,4,5. After convergence, -c approximate eigenvalues of the original problem may be obtained -c with the ARPACK subroutine igraphdseupd. -c -c 2. If the Ritz vectors corresponding to the converged Ritz values -c are needed, the user must call igraphdseupd immediately following completion -c of igraphdsaupd. This is new starting with version 2.1 of ARPACK. -c -c 3. If M can be factored into a Cholesky factorization M = LL' -c then Mode = 2 should not be selected. Instead one should use -c Mode = 1 with OP = inv(L)*A*inv(L'). Appropriate triangular -c linear systems should be solved with L and L' rather -c than computing inverses. After convergence, an approximate -c eigenvector z of the original problem is recovered by solving -c L'z = x where x is a Ritz vector of OP. -c -c 4. At present there is no a-priori analysis to guide the selection -c of NCV relative to NEV. The only formal requrement is that NCV > NEV. -c However, it is recommended that NCV .ge. 2*NEV. If many problems of -c the same type are to be solved, one should experiment with increasing -c NCV while keeping NEV fixed for a given test problem. This will -c usually decrease the required number of OP*x operations but it -c also increases the work and storage required to maintain the orthogonal -c basis vectors. The optimal "cross-over" with respect to CPU time -c is problem dependent and must be determined empirically. -c -c 5. If IPARAM(7) = 2 then in the Reverse commuication interface the user -c must do the following. When IDO = 1, Y = OP * X is to be computed. -c When IPARAM(7) = 2 OP = inv(B)*A. After computing A*X the user -c must overwrite X with A*X. Y is then the solution to the linear set -c of equations B*Y = A*X. -c -c 6. When IPARAM(1) = 0, and IDO = 3, the user needs to provide the -c NP = IPARAM(8) shifts in locations: -c 1 WORKL(IPNTR(11)) -c 2 WORKL(IPNTR(11)+1) -c . -c . -c . -c NP WORKL(IPNTR(11)+NP-1). -c -c The eigenvalues of the current tridiagonal matrix are located in -c WORKL(IPNTR(6)) through WORKL(IPNTR(6)+NCV-1). They are in the -c order defined by WHICH. The associated Ritz estimates are located in -c WORKL(IPNTR(8)), WORKL(IPNTR(8)+1), ... , WORKL(IPNTR(8)+NCV-1). -c -c----------------------------------------------------------------------- -c -c\Data Distribution Note: -c -c Fortran-D syntax: -c ================ -c REAL RESID(N), V(LDV,NCV), WORKD(3*N), WORKL(LWORKL) -c DECOMPOSE D1(N), D2(N,NCV) -c ALIGN RESID(I) with D1(I) -c ALIGN V(I,J) with D2(I,J) -c ALIGN WORKD(I) with D1(I) range (1:N) -c ALIGN WORKD(I) with D1(I-N) range (N+1:2*N) -c ALIGN WORKD(I) with D1(I-2*N) range (2*N+1:3*N) -c DISTRIBUTE D1(BLOCK), D2(BLOCK,:) -c REPLICATED WORKL(LWORKL) -c -c Cray MPP syntax: -c =============== -c REAL RESID(N), V(LDV,NCV), WORKD(N,3), WORKL(LWORKL) -c SHARED RESID(BLOCK), V(BLOCK,:), WORKD(BLOCK,:) -c REPLICATED WORKL(LWORKL) -c -c -c\BeginLib -c -c\References: -c 1. D.C. Sorensen, "Implicit Application of Polynomial Filters in -c a k-Step Arnoldi Method", SIAM J. Matr. Anal. Apps., 13 (1992), -c pp 357-385. -c 2. R.B. Lehoucq, "Analysis and Implementation of an Implicitly -c Restarted Arnoldi Iteration", Rice University Technical Report -c TR95-13, Department of Computational and Applied Mathematics. -c 3. B.N. Parlett, "The Symmetric Eigenvalue Problem". Prentice-Hall, -c 1980. -c 4. B.N. Parlett, B. Nour-Omid, "Towards a Black Box Lanczos Program", -c Computer Physics Communications, 53 (1989), pp 169-179. -c 5. B. Nour-Omid, B.N. Parlett, T. Ericson, P.S. Jensen, "How to -c Implement the Spectral Transformation", Math. Comp., 48 (1987), -c pp 663-673. -c 6. R.G. Grimes, J.G. Lewis and H.D. Simon, "A Shifted Block Lanczos -c Algorithm for Solving Sparse Symmetric Generalized Eigenproblems", -c SIAM J. Matr. Anal. Apps., January (1993). -c 7. L. Reichel, W.B. Gragg, "Algorithm 686: FORTRAN Subroutines -c for Updating the QR decomposition", ACM TOMS, December 1990, -c Volume 16 Number 4, pp 369-377. -c 8. R.B. Lehoucq, D.C. Sorensen, "Implementation of Some Spectral -c Transformations in a k-Step Arnoldi Method". In Preparation. -c -c\Routines called: -c igraphdsaup2 ARPACK routine that implements the Implicitly Restarted -c Arnoldi Iteration. -c igraphdstats ARPACK routine that initialize timing and other statistics -c variables. -c igraphivout ARPACK utility routine that prints integers. -c igraphsecond ARPACK utility routine for timing. -c igraphdvout ARPACK utility routine that prints vectors. -c dlamch LAPACK routine that determines machine constants. -c -c\Authors -c Danny Sorensen Phuong Vu -c Richard Lehoucq CRPC / Rice University -c Dept. of Computational & Houston, Texas -c Applied Mathematics -c Rice University -c Houston, Texas -c -c\Revision history: -c 12/15/93: Version ' 2.4' -c -c\SCCS Information: @(#) -c FILE: saupd.F SID: 2.7 DATE OF SID: 8/27/96 RELEASE: 2 -c -c\Remarks -c 1. None -c -c\EndLib -c -c----------------------------------------------------------------------- -c - subroutine igraphdsaupd - & ( ido, bmat, n, which, nev, tol, resid, ncv, v, ldv, iparam, - & ipntr, workd, workl, lworkl, info ) -c -c %----------------------------------------------------% -c | Include files for debugging and timing information | -c %----------------------------------------------------% -c - include 'debug.h' - include 'stat.h' -c -c %------------------% -c | Scalar Arguments | -c %------------------% -c - character bmat*1, which*2 - integer ido, info, ldv, lworkl, n, ncv, nev - Double precision - & tol -c -c %-----------------% -c | Array Arguments | -c %-----------------% -c - integer iparam(11), ipntr(11) - Double precision - & resid(n), v(ldv,ncv), workd(3*n), workl(lworkl) -c -c %------------% -c | Parameters | -c %------------% -c - Double precision - & one, zero - parameter (one = 1.0D+0, zero = 0.0D+0) -c -c %---------------% -c | Local Scalars | -c %---------------% -c - integer bounds, ierr, ih, iq, ishift, iupd, iw, - & ldh, ldq, msglvl, mxiter, mode, nb, - & nev0, next, np, ritz, j - save bounds, ierr, ih, iq, ishift, iupd, iw, - & ldh, ldq, msglvl, mxiter, mode, nb, - & nev0, next, np, ritz -c -c %----------------------% -c | External Subroutines | -c %----------------------% -c - external igraphdsaup2, igraphdvout, igraphivout, - & igraphsecond, igraphdstats -c -c %--------------------% -c | External Functions | -c %--------------------% -c - Double precision - & dlamch - external dlamch -c -c %-----------------------% -c | Executable Statements | -c %-----------------------% -c - if (ido .eq. 0) then -c -c %-------------------------------% -c | Initialize timing statistics | -c | & message level for debugging | -c %-------------------------------% -c - call igraphdstats - call igraphsecond (t0) - msglvl = msaupd -c - ierr = 0 - ishift = iparam(1) - mxiter = iparam(3) - nb = iparam(4) -c -c %--------------------------------------------% -c | Revision 2 performs only implicit restart. | -c %--------------------------------------------% -c - iupd = 1 - mode = iparam(7) -c -c %----------------% -c | Error checking | -c %----------------% -c - if (n .le. 0) then - ierr = -1 - else if (nev .le. 0) then - ierr = -2 - else if (ncv .le. nev .or. ncv .gt. n) then - ierr = -3 - end if -c -c %----------------------------------------------% -c | NP is the number of additional steps to | -c | extend the length NEV Lanczos factorization. | -c %----------------------------------------------% -c - np = ncv - nev -c - if (mxiter .le. 0) ierr = -4 - if (which .ne. 'LM' .and. - & which .ne. 'SM' .and. - & which .ne. 'LA' .and. - & which .ne. 'SA' .and. - & which .ne. 'BE') ierr = -5 - if (bmat .ne. 'I' .and. bmat .ne. 'G') ierr = -6 -c - if (lworkl .lt. ncv**2 + 8*ncv) ierr = -7 - if (mode .lt. 1 .or. mode .gt. 5) then - ierr = -10 - else if (mode .eq. 1 .and. bmat .eq. 'G') then - ierr = -11 - else if (ishift .lt. 0 .or. ishift .gt. 1) then - ierr = -12 - else if (nev .eq. 1 .and. which .eq. 'BE') then - ierr = -13 - end if -c -c %------------% -c | Error Exit | -c %------------% -c - if (ierr .ne. 0) then - info = ierr - ido = 99 - go to 9000 - end if -c -c %------------------------% -c | Set default parameters | -c %------------------------% -c - if (nb .le. 0) nb = 1 - if (tol .le. zero) tol = dlamch('EpsMach') -c -c %----------------------------------------------% -c | NP is the number of additional steps to | -c | extend the length NEV Lanczos factorization. | -c | NEV0 is the local variable designating the | -c | size of the invariant subspace desired. | -c %----------------------------------------------% -c - np = ncv - nev - nev0 = nev -c -c %-----------------------------% -c | Zero out internal workspace | -c %-----------------------------% -c - do 10 j = 1, ncv**2 + 8*ncv - workl(j) = zero - 10 continue -c -c %-------------------------------------------------------% -c | Pointer into WORKL for address of H, RITZ, BOUNDS, Q | -c | etc... and the remaining workspace. | -c | Also update pointer to be used on output. | -c | Memory is laid out as follows: | -c | workl(1:2*ncv) := generated tridiagonal matrix | -c | workl(2*ncv+1:2*ncv+ncv) := ritz values | -c | workl(3*ncv+1:3*ncv+ncv) := computed error bounds | -c | workl(4*ncv+1:4*ncv+ncv*ncv) := rotation matrix Q | -c | workl(4*ncv+ncv*ncv+1:7*ncv+ncv*ncv) := workspace | -c %-------------------------------------------------------% -c - ldh = ncv - ldq = ncv - ih = 1 - ritz = ih + 2*ldh - bounds = ritz + ncv - iq = bounds + ncv - iw = iq + ncv**2 - next = iw + 3*ncv -c - ipntr(4) = next - ipntr(5) = ih - ipntr(6) = ritz - ipntr(7) = bounds - ipntr(11) = iw - end if -c -c %-------------------------------------------------------% -c | Carry out the Implicitly restarted Lanczos Iteration. | -c %-------------------------------------------------------% -c - call igraphdsaup2 - & ( ido, bmat, n, which, nev0, np, tol, resid, mode, iupd, - & ishift, mxiter, v, ldv, workl(ih), ldh, workl(ritz), - & workl(bounds), workl(iq), ldq, workl(iw), ipntr, workd, - & info ) -c -c %--------------------------------------------------% -c | ido .ne. 99 implies use of reverse communication | -c | to compute operations involving OP or shifts. | -c %--------------------------------------------------% -c - if (ido .eq. 3) iparam(8) = np - if (ido .ne. 99) go to 9000 -c - iparam(3) = mxiter - iparam(5) = np - iparam(9) = nopx - iparam(10) = nbx - iparam(11) = nrorth -c -c %------------------------------------% -c | Exit if there was an informational | -c | error within igraphdsaup2. | -c %------------------------------------% -c - if (info .lt. 0) go to 9000 - if (info .eq. 2) info = 3 -c - if (msglvl .gt. 0) then - call igraphivout (logfil, 1, mxiter, ndigit, - & '_saupd: number of update iterations taken') - call igraphivout (logfil, 1, np, ndigit, - & '_saupd: number of "converged" Ritz values') - call igraphdvout (logfil, np, workl(Ritz), ndigit, - & '_saupd: final Ritz values') - call igraphdvout (logfil, np, workl(Bounds), ndigit, - & '_saupd: corresponding error bounds') - end if -c - call igraphsecond (t1) - tsaupd = t1 - t0 -c -c - 9000 continue -c - return -c -c %---------------% -c | End of igraphdsaupd | -c %---------------% -c - end diff --git a/src/rigraph/vendor/arpack/dsconv.f b/src/rigraph/vendor/arpack/dsconv.f deleted file mode 100644 index d8bac2e..0000000 --- a/src/rigraph/vendor/arpack/dsconv.f +++ /dev/null @@ -1,138 +0,0 @@ -c----------------------------------------------------------------------- -c\BeginDoc -c -c\Name: igraphdsconv -c -c\Description: -c Convergence testing for the symmetric Arnoldi eigenvalue routine. -c -c\Usage: -c call igraphdsconv -c ( N, RITZ, BOUNDS, TOL, NCONV ) -c -c\Arguments -c N Integer. (INPUT) -c Number of Ritz values to check for convergence. -c -c RITZ Double precision array of length N. (INPUT) -c The Ritz values to be checked for convergence. -c -c BOUNDS Double precision array of length N. (INPUT) -c Ritz estimates associated with the Ritz values in RITZ. -c -c TOL Double precision scalar. (INPUT) -c Desired relative accuracy for a Ritz value to be considered -c "converged". -c -c NCONV Integer scalar. (OUTPUT) -c Number of "converged" Ritz values. -c -c\EndDoc -c -c----------------------------------------------------------------------- -c -c\BeginLib -c -c\Routines called: -c igraphsecond ARPACK utility routine for timing. -c dlamch LAPACK routine that determines machine constants. -c -c\Author -c Danny Sorensen Phuong Vu -c Richard Lehoucq CRPC / Rice University -c Dept. of Computational & Houston, Texas -c Applied Mathematics -c Rice University -c Houston, Texas -c -c\SCCS Information: @(#) -c FILE: sconv.F SID: 2.4 DATE OF SID: 4/19/96 RELEASE: 2 -c -c\Remarks -c 1. Starting with version 2.4, this routine no longer uses the -c Parlett strategy using the gap conditions. -c -c\EndLib -c -c----------------------------------------------------------------------- -c - subroutine igraphdsconv (n, ritz, bounds, tol, nconv) -c -c %----------------------------------------------------% -c | Include files for debugging and timing information | -c %----------------------------------------------------% -c - include 'debug.h' - include 'stat.h' -c -c %------------------% -c | Scalar Arguments | -c %------------------% -c - integer n, nconv - Double precision - & tol -c -c %-----------------% -c | Array Arguments | -c %-----------------% -c - Double precision - & ritz(n), bounds(n) -c -c %---------------% -c | Local Scalars | -c %---------------% -c - integer i - Double precision - & temp, eps23 -c -c %-------------------% -c | External routines | -c %-------------------% -c - Double precision - & dlamch - external dlamch - -c %---------------------% -c | Intrinsic Functions | -c %---------------------% -c - intrinsic abs -c -c %-----------------------% -c | Executable Statements | -c %-----------------------% -c - call igraphsecond (t0) -c - eps23 = dlamch('Epsilon-Machine') - eps23 = eps23**(2.0D+0 / 3.0D+0) -c - nconv = 0 - do 10 i = 1, n -c -c %-----------------------------------------------------% -c | The i-th Ritz value is considered "converged" | -c | when: bounds(i) .le. TOL*max(eps23, abs(ritz(i))) | -c %-----------------------------------------------------% -c - temp = max( eps23, abs(ritz(i)) ) - if ( bounds(i) .le. tol*temp ) then - nconv = nconv + 1 - end if -c - 10 continue -c - call igraphsecond (t1) - tsconv = tsconv + (t1 - t0) -c - return -c -c %---------------% -c | End of igraphdsconv | -c %---------------% -c - end diff --git a/src/rigraph/vendor/arpack/dseigt.f b/src/rigraph/vendor/arpack/dseigt.f deleted file mode 100644 index dc5dccd..0000000 --- a/src/rigraph/vendor/arpack/dseigt.f +++ /dev/null @@ -1,181 +0,0 @@ -c----------------------------------------------------------------------- -c\BeginDoc -c -c\Name: igraphdseigt -c -c\Description: -c Compute the eigenvalues of the current symmetric tridiagonal matrix -c and the corresponding error bounds given the current residual norm. -c -c\Usage: -c call igraphdseigt -c ( RNORM, N, H, LDH, EIG, BOUNDS, WORKL, IERR ) -c -c\Arguments -c RNORM Double precision scalar. (INPUT) -c RNORM contains the residual norm corresponding to the current -c symmetric tridiagonal matrix H. -c -c N Integer. (INPUT) -c Size of the symmetric tridiagonal matrix H. -c -c H Double precision N by 2 array. (INPUT) -c H contains the symmetric tridiagonal matrix with the -c subdiagonal in the first column starting at H(2,1) and the -c main diagonal in igraphsecond column. -c -c LDH Integer. (INPUT) -c Leading dimension of H exactly as declared in the calling -c program. -c -c EIG Double precision array of length N. (OUTPUT) -c On output, EIG contains the N eigenvalues of H possibly -c unsorted. The BOUNDS arrays are returned in the -c same sorted order as EIG. -c -c BOUNDS Double precision array of length N. (OUTPUT) -c On output, BOUNDS contains the error estimates corresponding -c to the eigenvalues EIG. This is equal to RNORM times the -c last components of the eigenvectors corresponding to the -c eigenvalues in EIG. -c -c WORKL Double precision work array of length 3*N. (WORKSPACE) -c Private (replicated) array on each PE or array allocated on -c the front end. -c -c IERR Integer. (OUTPUT) -c Error exit flag from igraphdstqrb. -c -c\EndDoc -c -c----------------------------------------------------------------------- -c -c\BeginLib -c -c\Local variables: -c xxxxxx real -c -c\Routines called: -c igraphdstqrb ARPACK routine that computes the eigenvalues and the -c last components of the eigenvectors of a symmetric -c and tridiagonal matrix. -c igraphsecond ARPACK utility routine for timing. -c igraphdvout ARPACK utility routine that prints vectors. -c dcopy Level 1 BLAS that copies one vector to another. -c -c\Author -c Danny Sorensen Phuong Vu -c Richard Lehoucq CRPC / Rice University -c Dept. of Computational & Houston, Texas -c Applied Mathematics -c Rice University -c Houston, Texas -c -c\Revision history: -c xx/xx/92: Version ' 2.4' -c -c\SCCS Information: @(#) -c FILE: seigt.F SID: 2.4 DATE OF SID: 8/27/96 RELEASE: 2 -c -c\Remarks -c None -c -c\EndLib -c -c----------------------------------------------------------------------- -c - subroutine igraphdseigt - & ( rnorm, n, h, ldh, eig, bounds, workl, ierr ) -c -c %----------------------------------------------------% -c | Include files for debugging and timing information | -c %----------------------------------------------------% -c - include 'debug.h' - include 'stat.h' -c -c %------------------% -c | Scalar Arguments | -c %------------------% -c - integer ierr, ldh, n - Double precision - & rnorm -c -c %-----------------% -c | Array Arguments | -c %-----------------% -c - Double precision - & eig(n), bounds(n), h(ldh,2), workl(3*n) -c -c %------------% -c | Parameters | -c %------------% -c - Double precision - & zero - parameter (zero = 0.0D+0) -c -c %---------------% -c | Local Scalars | -c %---------------% -c - integer i, k, msglvl -c -c %----------------------% -c | External Subroutines | -c %----------------------% -c - external dcopy, igraphdstqrb, igraphdvout, igraphsecond -c -c %-----------------------% -c | Executable Statements | -c %-----------------------% -c -c %-------------------------------% -c | Initialize timing statistics | -c | & message level for debugging | -c %-------------------------------% -c - call igraphsecond (t0) - msglvl = mseigt -c - if (msglvl .gt. 0) then - call igraphdvout (logfil, n, h(1,2), ndigit, - & '_seigt: main diagonal of matrix H') - if (n .gt. 1) then - call igraphdvout (logfil, n-1, h(2,1), ndigit, - & '_seigt: sub diagonal of matrix H') - end if - end if -c - call dcopy (n, h(1,2), 1, eig, 1) - call dcopy (n-1, h(2,1), 1, workl, 1) - call igraphdstqrb (n, eig, workl, bounds, workl(n+1), ierr) - if (ierr .ne. 0) go to 9000 - if (msglvl .gt. 1) then - call igraphdvout (logfil, n, bounds, ndigit, - & '_seigt: last row of the eigenvector matrix for H') - end if -c -c %-----------------------------------------------% -c | Finally determine the error bounds associated | -c | with the n Ritz values of H. | -c %-----------------------------------------------% -c - do 30 k = 1, n - bounds(k) = rnorm*abs(bounds(k)) - 30 continue -c - call igraphsecond (t1) - tseigt = tseigt + (t1 - t0) -c - 9000 continue - return -c -c %---------------% -c | End of igraphdseigt | -c %---------------% -c - end diff --git a/src/rigraph/vendor/arpack/dsesrt.f b/src/rigraph/vendor/arpack/dsesrt.f deleted file mode 100644 index 05e2c36..0000000 --- a/src/rigraph/vendor/arpack/dsesrt.f +++ /dev/null @@ -1,217 +0,0 @@ -c----------------------------------------------------------------------- -c\BeginDoc -c -c\Name: igraphdsesrt -c -c\Description: -c Sort the array X in the order specified by WHICH and optionally -c apply the permutation to the columns of the matrix A. -c -c\Usage: -c call igraphdsesrt -c ( WHICH, APPLY, N, X, NA, A, LDA) -c -c\Arguments -c WHICH Character*2. (Input) -c 'LM' -> X is sorted into increasing order of magnitude. -c 'SM' -> X is sorted into decreasing order of magnitude. -c 'LA' -> X is sorted into increasing order of algebraic. -c 'SA' -> X is sorted into decreasing order of algebraic. -c -c APPLY Logical. (Input) -c APPLY = .TRUE. -> apply the sorted order to A. -c APPLY = .FALSE. -> do not apply the sorted order to A. -c -c N Integer. (INPUT) -c Dimension of the array X. -c -c X Double precision array of length N. (INPUT/OUTPUT) -c The array to be sorted. -c -c NA Integer. (INPUT) -c Number of rows of the matrix A. -c -c A Double precision array of length NA by N. (INPUT/OUTPUT) -c -c LDA Integer. (INPUT) -c Leading dimension of A. -c -c\EndDoc -c -c----------------------------------------------------------------------- -c -c\BeginLib -c -c\Routines -c dswap Level 1 BLAS that swaps the contents of two vectors. -c -c\Authors -c Danny Sorensen Phuong Vu -c Richard Lehoucq CRPC / Rice University -c Dept. of Computational & Houston, Texas -c Applied Mathematics -c Rice University -c Houston, Texas -c -c\Revision history: -c 12/15/93: Version ' 2.1'. -c Adapted from the sort routine in LANSO and -c the ARPACK code igraphdsortr -c -c\SCCS Information: @(#) -c FILE: sesrt.F SID: 2.3 DATE OF SID: 4/19/96 RELEASE: 2 -c -c\EndLib -c -c----------------------------------------------------------------------- -c - subroutine igraphdsesrt (which, apply, n, x, na, a, lda) -c -c %------------------% -c | Scalar Arguments | -c %------------------% -c - character*2 which - logical apply - integer lda, n, na -c -c %-----------------% -c | Array Arguments | -c %-----------------% -c - Double precision - & x(0:n-1), a(lda, 0:n-1) -c -c %---------------% -c | Local Scalars | -c %---------------% -c - integer i, igap, j - Double precision - & temp -c -c %----------------------% -c | External Subroutines | -c %----------------------% -c - external dswap -c -c %-----------------------% -c | Executable Statements | -c %-----------------------% -c - igap = n / 2 -c - if (which .eq. 'SA') then -c -c X is sorted into decreasing order of algebraic. -c - 10 continue - if (igap .eq. 0) go to 9000 - do 30 i = igap, n-1 - j = i-igap - 20 continue -c - if (j.lt.0) go to 30 -c - if (x(j).lt.x(j+igap)) then - temp = x(j) - x(j) = x(j+igap) - x(j+igap) = temp - if (apply) call dswap( na, a(1, j), 1, a(1,j+igap), 1) - else - go to 30 - endif - j = j-igap - go to 20 - 30 continue - igap = igap / 2 - go to 10 -c - else if (which .eq. 'SM') then -c -c X is sorted into decreasing order of magnitude. -c - 40 continue - if (igap .eq. 0) go to 9000 - do 60 i = igap, n-1 - j = i-igap - 50 continue -c - if (j.lt.0) go to 60 -c - if (abs(x(j)).lt.abs(x(j+igap))) then - temp = x(j) - x(j) = x(j+igap) - x(j+igap) = temp - if (apply) call dswap( na, a(1, j), 1, a(1,j+igap), 1) - else - go to 60 - endif - j = j-igap - go to 50 - 60 continue - igap = igap / 2 - go to 40 -c - else if (which .eq. 'LA') then -c -c X is sorted into increasing order of algebraic. -c - 70 continue - if (igap .eq. 0) go to 9000 - do 90 i = igap, n-1 - j = i-igap - 80 continue -c - if (j.lt.0) go to 90 -c - if (x(j).gt.x(j+igap)) then - temp = x(j) - x(j) = x(j+igap) - x(j+igap) = temp - if (apply) call dswap( na, a(1, j), 1, a(1,j+igap), 1) - else - go to 90 - endif - j = j-igap - go to 80 - 90 continue - igap = igap / 2 - go to 70 -c - else if (which .eq. 'LM') then -c -c X is sorted into increasing order of magnitude. -c - 100 continue - if (igap .eq. 0) go to 9000 - do 120 i = igap, n-1 - j = i-igap - 110 continue -c - if (j.lt.0) go to 120 -c - if (abs(x(j)).gt.abs(x(j+igap))) then - temp = x(j) - x(j) = x(j+igap) - x(j+igap) = temp - if (apply) call dswap( na, a(1, j), 1, a(1,j+igap), 1) - else - go to 120 - endif - j = j-igap - go to 110 - 120 continue - igap = igap / 2 - go to 100 - end if -c - 9000 continue - return -c -c %---------------% -c | End of igraphdsesrt | -c %---------------% -c - end diff --git a/src/rigraph/vendor/arpack/dseupd.f b/src/rigraph/vendor/arpack/dseupd.f deleted file mode 100644 index 0336c3a..0000000 --- a/src/rigraph/vendor/arpack/dseupd.f +++ /dev/null @@ -1,905 +0,0 @@ -c\BeginDoc -c -c\Name: igraphdseupd -c -c\Description: -c -c This subroutine returns the converged approximations to eigenvalues -c of A*z = lambda*B*z and (optionally): -c -c (1) the corresponding approximate eigenvectors, -c -c (2) an orthonormal (Lanczos) basis for the associated approximate -c invariant subspace, -c -c (3) Both. -c -c There is negligible additional cost to obtain eigenvectors. An orthonormal -c (Lanczos) basis is always computed. There is an additional storage cost -c of n*nev if both are requested (in this case a separate array Z must be -c supplied). -c -c These quantities are obtained from the Lanczos factorization computed -c by DSAUPD for the linear operator OP prescribed by the MODE selection -c (see IPARAM(7) in DSAUPD documentation.) DSAUPD must be called before -c this routine is called. These approximate eigenvalues and vectors are -c commonly called Ritz values and Ritz vectors respectively. They are -c referred to as such in the comments that follow. The computed orthonormal -c basis for the invariant subspace corresponding to these Ritz values is -c referred to as a Lanczos basis. -c -c See documentation in the header of the subroutine DSAUPD for a definition -c of OP as well as other terms and the relation of computed Ritz values -c and vectors of OP with respect to the given problem A*z = lambda*B*z. -c -c The approximate eigenvalues of the original problem are returned in -c ascending algebraic order. The user may elect to call this routine -c once for each desired Ritz vector and store it peripherally if desired. -c There is also the option of computing a selected set of these vectors -c with a single call. -c -c\Usage: -c call igraphdseupd -c ( RVEC, HOWMNY, SELECT, D, Z, LDZ, SIGMA, BMAT, N, WHICH, NEV, TOL, -c RESID, NCV, V, LDV, IPARAM, IPNTR, WORKD, WORKL, LWORKL, INFO ) -c -c RVEC LOGICAL (INPUT) -c Specifies whether Ritz vectors corresponding to the Ritz value -c approximations to the eigenproblem A*z = lambda*B*z are computed. -c -c RVEC = .FALSE. Compute Ritz values only. -c -c RVEC = .TRUE. Compute Ritz vectors. -c -c HOWMNY Character*1 (INPUT) -c Specifies how many Ritz vectors are wanted and the form of Z -c the matrix of Ritz vectors. See remark 1 below. -c = 'A': compute NEV Ritz vectors; -c = 'S': compute some of the Ritz vectors, specified -c by the logical array SELECT. -c -c SELECT Logical array of dimension NEV. (INPUT) -c If HOWMNY = 'S', SELECT specifies the Ritz vectors to be -c computed. To select the Ritz vector corresponding to a -c Ritz value D(j), SELECT(j) must be set to .TRUE.. -c If HOWMNY = 'A' , SELECT is not referenced. -c -c D Double precision array of dimension NEV. (OUTPUT) -c On exit, D contains the Ritz value approximations to the -c eigenvalues of A*z = lambda*B*z. The values are returned -c in ascending order. If IPARAM(7) = 3,4,5 then D represents -c the Ritz values of OP computed by igraphdsaupd transformed to -c those of the original eigensystem A*z = lambda*B*z. If -c IPARAM(7) = 1,2 then the Ritz values of OP are the same -c as the those of A*z = lambda*B*z. -c -c Z Double precision N by NEV array if HOWMNY = 'A'. (OUTPUT) -c On exit, Z contains the B-orthonormal Ritz vectors of the -c eigensystem A*z = lambda*B*z corresponding to the Ritz -c value approximations. -c If RVEC = .FALSE. then Z is not referenced. -c NOTE: The array Z may be set equal to first NEV columns of the -c Arnoldi/Lanczos basis array V computed by DSAUPD. -c -c LDZ Integer. (INPUT) -c The leading dimension of the array Z. If Ritz vectors are -c desired, then LDZ .ge. max( 1, N ). In any case, LDZ .ge. 1. -c -c SIGMA Double precision (INPUT) -c If IPARAM(7) = 3,4,5 represents the shift. Not referenced if -c IPARAM(7) = 1 or 2. -c -c -c **** The remaining arguments MUST be the same as for the **** -c **** call to DNAUPD that was just completed. **** -c -c NOTE: The remaining arguments -c -c BMAT, N, WHICH, NEV, TOL, RESID, NCV, V, LDV, IPARAM, IPNTR, -c WORKD, WORKL, LWORKL, INFO -c -c must be passed directly to DSEUPD following the last call -c to DSAUPD. These arguments MUST NOT BE MODIFIED between -c the the last call to DSAUPD and the call to DSEUPD. -c -c Two of these parameters (WORKL, INFO) are also output parameters: -c -c WORKL Double precision work array of length LWORKL. (OUTPUT/WORKSPACE) -c WORKL(1:4*ncv) contains information obtained in -c igraphdsaupd. They are not changed by igraphdseupd. -c WORKL(4*ncv+1:ncv*ncv+8*ncv) holds the -c untransformed Ritz values, the computed error estimates, -c and the associated eigenvector matrix of H. -c -c Note: IPNTR(8:10) contains the pointer into WORKL for addresses -c of the above information computed by igraphdseupd. -c ------------------------------------------------------------- -c IPNTR(8): pointer to the NCV RITZ values of the original system. -c IPNTR(9): pointer to the NCV corresponding error bounds. -c IPNTR(10): pointer to the NCV by NCV matrix of eigenvectors -c of the tridiagonal matrix T. Only referenced by -c igraphdseupd if RVEC = .TRUE. See Remarks. -c ------------------------------------------------------------- -c -c INFO Integer. (OUTPUT) -c Error flag on output. -c = 0: Normal exit. -c = -1: N must be positive. -c = -2: NEV must be positive. -c = -3: NCV must be greater than NEV and less than or equal to N. -c = -5: WHICH must be one of 'LM', 'SM', 'LA', 'SA' or 'BE'. -c = -6: BMAT must be one of 'I' or 'G'. -c = -7: Length of private work WORKL array is not sufficient. -c = -8: Error return from trid. eigenvalue calculation; -c Information error from LAPACK routine dsteqr. -c = -9: Starting vector is zero. -c = -10: IPARAM(7) must be 1,2,3,4,5. -c = -11: IPARAM(7) = 1 and BMAT = 'G' are incompatible. -c = -12: NEV and WHICH = 'BE' are incompatible. -c = -14: DSAUPD did not find any eigenvalues to sufficient -c accuracy. -c = -15: HOWMNY must be one of 'A' or 'S' if RVEC = .true. -c = -16: HOWMNY = 'S' not yet implemented -c -c\BeginLib -c -c\References: -c 1. D.C. Sorensen, "Implicit Application of Polynomial Filters in -c a k-Step Arnoldi Method", SIAM J. Matr. Anal. Apps., 13 (1992), -c pp 357-385. -c 2. R.B. Lehoucq, "Analysis and Implementation of an Implicitly -c Restarted Arnoldi Iteration", Rice University Technical Report -c TR95-13, Department of Computational and Applied Mathematics. -c 3. B.N. Parlett, "The Symmetric Eigenvalue Problem". Prentice-Hall, -c 1980. -c 4. B.N. Parlett, B. Nour-Omid, "Towards a Black Box Lanczos Program", -c Computer Physics Communications, 53 (1989), pp 169-179. -c 5. B. Nour-Omid, B.N. Parlett, T. Ericson, P.S. Jensen, "How to -c Implement the Spectral Transformation", Math. Comp., 48 (1987), -c pp 663-673. -c 6. R.G. Grimes, J.G. Lewis and H.D. Simon, "A Shifted Block Lanczos -c Algorithm for Solving Sparse Symmetric Generalized Eigenproblems", -c SIAM J. Matr. Anal. Apps., January (1993). -c 7. L. Reichel, W.B. Gragg, "Algorithm 686: FORTRAN Subroutines -c for Updating the QR decomposition", ACM TOMS, December 1990, -c Volume 16 Number 4, pp 369-377. -c -c\Remarks -c 1. The converged Ritz values are always returned in increasing -c (algebraic) order. -c -c 2. Currently only HOWMNY = 'A' is implemented. It is included at this -c stage for the user who wants to incorporate it. -c -c\Routines called: -c igraphdsesrt ARPACK routine that sorts an array X, and applies the -c corresponding permutation to a matrix A. -c igraphdsortr igraphdsortr ARPACK sorting routine. -c igraphivout ARPACK utility routine that prints integers. -c igraphdvout ARPACK utility routine that prints vectors. -c dgeqr2 LAPACK routine that computes the QR factorization of -c a matrix. -c dlacpy LAPACK matrix copy routine. -c dlamch LAPACK routine that determines machine constants. -c dorm2r LAPACK routine that applies an orthogonal matrix in -c factored form. -c dsteqr LAPACK routine that computes eigenvalues and eigenvectors -c of a tridiagonal matrix. -c dger Level 2 BLAS rank one update to a matrix. -c dcopy Level 1 BLAS that copies one vector to another . -c dnrm2 Level 1 BLAS that computes the norm of a vector. -c dscal Level 1 BLAS that scales a vector. -c dswap Level 1 BLAS that swaps the contents of two vectors. - -c\Authors -c Danny Sorensen Phuong Vu -c Richard Lehoucq CRPC / Rice University -c Chao Yang Houston, Texas -c Dept. of Computational & -c Applied Mathematics -c Rice University -c Houston, Texas -c -c\Revision history: -c 12/15/93: Version ' 2.1' -c -c\SCCS Information: @(#) -c FILE: seupd.F SID: 2.7 DATE OF SID: 8/27/96 RELEASE: 2 -c -c\EndLib -c -c----------------------------------------------------------------------- - subroutine igraphdseupd (rvec, howmny, select, d, z, ldz, - & sigma, bmat, n, which, nev, tol, resid, ncv, v, ldv, iparam, - & ipntr, workd, workl, lworkl, info ) -c -c %----------------------------------------------------% -c | Include files for debugging and timing information | -c %----------------------------------------------------% -c - include 'debug.h' - include 'stat.h' -c -c %------------------% -c | Scalar Arguments | -c %------------------% -c - character bmat, howmny, which*2 - logical rvec, select(ncv) - integer info, ldz, ldv, lworkl, n, ncv, nev - Double precision - & sigma, tol -c -c %-----------------% -c | Array Arguments | -c %-----------------% -c - integer iparam(7), ipntr(11) - Double precision - & d(nev), resid(n), v(ldv,ncv), z(ldz, nev), - & workd(2*n), workl(lworkl) -c -c %------------% -c | Parameters | -c %------------% -c - Double precision - & one, zero - parameter (one = 1.0D+0, zero = 0.0D+0) -c -c %---------------% -c | Local Scalars | -c %---------------% -c - character type*6 - integer bounds, ierr, ih, ihb, ihd, iq, iw, j, k, - & ldh, ldq, mode, msglvl, nconv, next, ritz, - & irz, ibd, ktrord, leftptr, rghtptr, ism, ilg - Double precision - & bnorm2, rnorm, temp, thres1, thres2, tempbnd, eps23 - logical reord -c -c %--------------% -c | Local Arrays | -c %--------------% -c - Double precision - & kv(2) -c -c %----------------------% -c | External Subroutines | -c %----------------------% -c - external dcopy, dger, dgeqr2, dlacpy, dorm2r, dscal, - & igraphdsesrt, dsteqr, dswap, igraphdvout, - & igraphivout, igraphdsortr -c -c %--------------------% -c | External Functions | -c %--------------------% -c - Double precision - & dnrm2, dlamch - external dnrm2, dlamch -c -c %---------------------% -c | Intrinsic Functions | -c %---------------------% -c - intrinsic min -c -c %-----------------------% -c | Executable Statements | -c %-----------------------% -c -c %------------------------% -c | Set default parameters | -c %------------------------% -c - msglvl = mseupd - mode = iparam(7) - nconv = iparam(5) - info = 0 -c -c %--------------% -c | Quick return | -c %--------------% -c - if (nconv .eq. 0) go to 9000 - ierr = 0 -c - if (nconv .le. 0) ierr = -14 - if (n .le. 0) ierr = -1 - if (nev .le. 0) ierr = -2 - if (ncv .le. nev .or. ncv .gt. n) ierr = -3 - if (which .ne. 'LM' .and. - & which .ne. 'SM' .and. - & which .ne. 'LA' .and. - & which .ne. 'SA' .and. - & which .ne. 'BE') ierr = -5 - if (bmat .ne. 'I' .and. bmat .ne. 'G') ierr = -6 - if ( (howmny .ne. 'A' .and. - & howmny .ne. 'P' .and. - & howmny .ne. 'S') .and. rvec ) - & ierr = -15 - if (rvec .and. howmny .eq. 'S') ierr = -16 -c - if (rvec .and. lworkl .lt. ncv**2+8*ncv) ierr = -7 -c - if (mode .eq. 1 .or. mode .eq. 2) then - type = 'REGULR' - else if (mode .eq. 3 ) then - type = 'SHIFTI' - else if (mode .eq. 4 ) then - type = 'BUCKLE' - else if (mode .eq. 5 ) then - type = 'CAYLEY' - else - ierr = -10 - end if - if (mode .eq. 1 .and. bmat .eq. 'G') ierr = -11 - if (nev .eq. 1 .and. which .eq. 'BE') ierr = -12 -c -c %------------% -c | Error Exit | -c %------------% -c - if (ierr .ne. 0) then - info = ierr - go to 9000 - end if -c -c %-------------------------------------------------------% -c | Pointer into WORKL for address of H, RITZ, BOUNDS, Q | -c | etc... and the remaining workspace. | -c | Also update pointer to be used on output. | -c | Memory is laid out as follows: | -c | workl(1:2*ncv) := generated tridiagonal matrix H | -c | The subdiagonal is stored in workl(2:ncv). | -c | The dead spot is workl(1) but upon exiting | -c | igraphdsaupd stores the B-norm of the last residual | -c | vector in workl(1). We use this !!! | -c | workl(2*ncv+1:2*ncv+ncv) := ritz values | -c | The wanted values are in the first NCONV spots. | -c | workl(3*ncv+1:3*ncv+ncv) := computed Ritz estimates | -c | The wanted values are in the first NCONV spots. | -c | NOTE: workl(1:4*ncv) is set by igraphdsaupd and is not | -c | modified by igraphdseupd. | -c %-------------------------------------------------------% -c -c %-------------------------------------------------------% -c | The following is used and set by igraphdseupd. | -c | workl(4*ncv+1:4*ncv+ncv) := used as workspace during | -c | computation of the eigenvectors of H. Stores | -c | the diagonal of H. Upon EXIT contains the NCV | -c | Ritz values of the original system. The first | -c | NCONV spots have the wanted values. If MODE = | -c | 1 or 2 then will equal workl(2*ncv+1:3*ncv). | -c | workl(5*ncv+1:5*ncv+ncv) := used as workspace during | -c | computation of the eigenvectors of H. Stores | -c | the subdiagonal of H. Upon EXIT contains the | -c | NCV corresponding Ritz estimates of the | -c | original system. The first NCONV spots have the | -c | wanted values. If MODE = 1,2 then will equal | -c | workl(3*ncv+1:4*ncv). | -c | workl(6*ncv+1:6*ncv+ncv*ncv) := orthogonal Q that is | -c | the eigenvector matrix for H as returned by | -c | dsteqr. Not referenced if RVEC = .False. | -c | Ordering follows that of workl(4*ncv+1:5*ncv) | -c | workl(6*ncv+ncv*ncv+1:6*ncv+ncv*ncv+2*ncv) := | -c | Workspace. Needed by dsteqr and by igraphdseupd. | -c | GRAND total of NCV*(NCV+8) locations. | -c %-------------------------------------------------------% -c -c - 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 + 2*ncv - ipntr(4) = next - ipntr(8) = ihd - ipntr(9) = ihb - ipntr(10) = iq -c -c %----------------------------------------% -c | irz points to the Ritz values computed | -c | by _seigt before exiting _saup2. | -c | ibd points to the Ritz estimates | -c | computed by _seigt before exiting | -c | _saup2. | -c %----------------------------------------% -c - irz = ipntr(11)+ncv - ibd = irz+ncv -c -c -c %---------------------------------% -c | Set machine dependent constant. | -c %---------------------------------% -c - eps23 = dlamch('Epsilon-Machine') - eps23 = eps23**(2.0D+0 / 3.0D+0) -c -c %---------------------------------------% -c | RNORM is B-norm of the RESID(1:N). | -c | BNORM2 is the 2 norm of B*RESID(1:N). | -c | Upon exit of igraphdsaupd WORKD(1:N) has | -c | B*RESID(1:N). | -c %---------------------------------------% -c - rnorm = workl(ih) - if (bmat .eq. 'I') then - bnorm2 = rnorm - else if (bmat .eq. 'G') then - bnorm2 = dnrm2(n, workd, 1) - end if -c - if (rvec) then -c -c %------------------------------------------------% -c | Get the converged Ritz value on the boundary. | -c | This value will be used to dermine whether we | -c | need to reorder the eigenvalues and | -c | eigenvectors comupted by _steqr, and is | -c | referred to as the "threshold" value. | -c | | -c | A Ritz value gamma is said to be a wanted | -c | one, if | -c | abs(gamma) .ge. threshold, when WHICH = 'LM'; | -c | abs(gamma) .le. threshold, when WHICH = 'SM'; | -c | gamma .ge. threshold, when WHICH = 'LA'; | -c | gamma .le. threshold, when WHICH = 'SA'; | -c | gamma .le. thres1 .or. gamma .ge. thres2 | -c | when WHICH = 'BE'; | -c | | -c | Note: converged Ritz values and associated | -c | Ritz estimates have been placed in the first | -c | NCONV locations in workl(ritz) and | -c | workl(bounds) respectively. They have been | -c | sorted (in _saup2) according to the WHICH | -c | selection criterion. (Except in the case | -c | WHICH = 'BE', they are sorted in an increasing | -c | order.) | -c %------------------------------------------------% -c - if (which .eq. 'LM' .or. which .eq. 'SM' - & .or. which .eq. 'LA' .or. which .eq. 'SA' ) then -c - thres1 = workl(ritz) -c - if (msglvl .gt. 2) then - call igraphdvout(logfil, 1, [thres1], ndigit, - & '_seupd: Threshold eigenvalue used for re-ordering') - end if -c - else if (which .eq. 'BE') then -c -c %------------------------------------------------% -c | Ritz values returned from _saup2 have been | -c | sorted in increasing order. Thus two | -c | "threshold" values (one for the small end, one | -c | for the large end) are in the middle. | -c %------------------------------------------------% -c - ism = max(nev,nconv) / 2 - ilg = ism + 1 - thres1 = workl(ism) - thres2 = workl(ilg) -c - if (msglvl .gt. 2) then - kv(1) = thres1 - kv(2) = thres2 - call igraphdvout(logfil, 2, kv, ndigit, - & '_seupd: Threshold eigenvalues used for re-ordering') - end if -c - end if -c -c %----------------------------------------------------------% -c | Check to see if all converged Ritz values appear within | -c | the first NCONV diagonal elements returned from _seigt. | -c | This is done in the following way: | -c | | -c | 1) For each Ritz value obtained from _seigt, compare it | -c | with the threshold Ritz value computed above to | -c | determine whether it is a wanted one. | -c | | -c | 2) If it is wanted, then check the corresponding Ritz | -c | estimate to see if it has converged. If it has, set | -c | correponding entry in the logical array SELECT to | -c | .TRUE.. | -c | | -c | If SELECT(j) = .TRUE. and j > NCONV, then there is a | -c | converged Ritz value that does not appear at the top of | -c | the diagonal matrix computed by _seigt in _saup2. | -c | Reordering is needed. | -c %----------------------------------------------------------% -c - reord = .false. - ktrord = 0 - do 10 j = 0, ncv-1 - select(j+1) = .false. - if (which .eq. 'LM') then - if (abs(workl(irz+j)) .ge. abs(thres1)) then - tempbnd = max( eps23, abs(workl(irz+j)) ) - if (workl(ibd+j) .le. tol*tempbnd) then - select(j+1) = .true. - end if - end if - else if (which .eq. 'SM') then - if (abs(workl(irz+j)) .le. abs(thres1)) then - tempbnd = max( eps23, abs(workl(irz+j)) ) - if (workl(ibd+j) .le. tol*tempbnd) then - select(j+1) = .true. - end if - end if - else if (which .eq. 'LA') then - if (workl(irz+j) .ge. thres1) then - tempbnd = max( eps23, abs(workl(irz+j)) ) - if (workl(ibd+j) .le. tol*tempbnd) then - select(j+1) = .true. - end if - end if - else if (which .eq. 'SA') then - if (workl(irz+j) .le. thres1) then - tempbnd = max( eps23, abs(workl(irz+j)) ) - if (workl(ibd+j) .le. tol*tempbnd) then - select(j+1) = .true. - end if - end if - else if (which .eq. 'BE') then - if ( workl(irz+j) .le. thres1 .or. - & workl(irz+j) .ge. thres2 ) then - tempbnd = max( eps23, abs(workl(irz+j)) ) - if (workl(ibd+j) .le. tol*tempbnd) then - select(j+1) = .true. - end if - end if - end if - if (j+1 .gt. nconv ) reord = select(j+1) .or. reord - if (select(j+1)) ktrord = ktrord + 1 - 10 continue - -c %-------------------------------------------% -c | If KTRORD .ne. NCONV, something is wrong. | -c %-------------------------------------------% -c - if (msglvl .gt. 2) then - call igraphivout(logfil, 1, [ktrord], ndigit, - & '_seupd: Number of specified eigenvalues') - call igraphivout(logfil, 1, [nconv], ndigit, - & '_seupd: Number of "converged" eigenvalues') - end if -c -c %-----------------------------------------------------------% -c | Call LAPACK routine _steqr to compute the eigenvalues and | -c | eigenvectors of the final symmetric tridiagonal matrix H. | -c | Initialize the eigenvector matrix Q to the identity. | -c %-----------------------------------------------------------% -c - call dcopy (ncv-1, workl(ih+1), 1, workl(ihb), 1) - call dcopy (ncv, workl(ih+ldh), 1, workl(ihd), 1) -c - call dsteqr ('Identity', ncv, workl(ihd), workl(ihb), - & workl(iq), ldq, workl(iw), ierr) -c - if (ierr .ne. 0) then - info = -8 - go to 9000 - end if -c - if (msglvl .gt. 1) then - call dcopy (ncv, workl(iq+ncv-1), ldq, workl(iw), 1) - call igraphdvout (logfil, ncv, workl(ihd), ndigit, - & '_seupd: NCV Ritz values of the final H matrix') - call igraphdvout (logfil, ncv, workl(iw), ndigit, - & '_seupd: last row of the eigenvector matrix for H') - end if -c - if (reord) then -c -c %---------------------------------------------% -c | Reordered the eigenvalues and eigenvectors | -c | computed by _steqr so that the "converged" | -c | eigenvalues appear in the first NCONV | -c | positions of workl(ihd), and the associated | -c | eigenvectors appear in the first NCONV | -c | columns. | -c %---------------------------------------------% -c - leftptr = 1 - rghtptr = ncv -c - if (ncv .eq. 1) go to 30 -c - 20 if (select(leftptr)) then -c -c %-------------------------------------------% -c | Search, from the left, for the first Ritz | -c | value that has not converged. | -c %-------------------------------------------% -c - leftptr = leftptr + 1 -c - else if ( .not. select(rghtptr)) then -c -c %----------------------------------------------% -c | Search, from the right, the first Ritz value | -c | that has converged. | -c %----------------------------------------------% -c - rghtptr = rghtptr - 1 -c - else -c -c %----------------------------------------------% -c | Swap the Ritz value on the left that has not | -c | converged with the Ritz value on the right | -c | that has converged. Swap the associated | -c | eigenvector of the tridiagonal matrix H as | -c | well. | -c %----------------------------------------------% -c - temp = workl(ihd+leftptr-1) - workl(ihd+leftptr-1) = workl(ihd+rghtptr-1) - workl(ihd+rghtptr-1) = temp - call dcopy(ncv, workl(iq+ncv*(leftptr-1)), 1, - & workl(iw), 1) - call dcopy(ncv, workl(iq+ncv*(rghtptr-1)), 1, - & workl(iq+ncv*(leftptr-1)), 1) - call dcopy(ncv, workl(iw), 1, - & workl(iq+ncv*(rghtptr-1)), 1) - leftptr = leftptr + 1 - rghtptr = rghtptr - 1 -c - end if -c - if (leftptr .lt. rghtptr) go to 20 -c - 30 end if -c - if (msglvl .gt. 2) then - call igraphdvout (logfil, ncv, workl(ihd), ndigit, - & '_seupd: The eigenvalues of H--reordered') - end if -c -c %----------------------------------------% -c | Load the converged Ritz values into D. | -c %----------------------------------------% -c - call dcopy(nconv, workl(ihd), 1, d, 1) -c - else -c -c %-----------------------------------------------------% -c | Ritz vectors not required. Load Ritz values into D. | -c %-----------------------------------------------------% -c - call dcopy (nconv, workl(ritz), 1, d, 1) - call dcopy (ncv, workl(ritz), 1, workl(ihd), 1) -c - end if -c -c %------------------------------------------------------------------% -c | Transform the Ritz values and possibly vectors and corresponding | -c | Ritz estimates of OP to those of A*x=lambda*B*x. The Ritz values | -c | (and corresponding data) are returned in ascending order. | -c %------------------------------------------------------------------% -c - if (type .eq. 'REGULR') then -c -c %---------------------------------------------------------% -c | Ascending sort of wanted Ritz values, vectors and error | -c | bounds. Not necessary if only Ritz values are desired. | -c %---------------------------------------------------------% -c - if (rvec) then - call igraphdsesrt ('LA', rvec , nconv, d, ncv, workl(iq), - & ldq) - else - call dcopy (ncv, workl(bounds), 1, workl(ihb), 1) - end if -c - else -c -c %-------------------------------------------------------------% -c | * Make a copy of all the Ritz values. | -c | * Transform the Ritz values back to the original system. | -c | For TYPE = 'SHIFTI' the transformation is | -c | lambda = 1/theta + sigma | -c | For TYPE = 'BUCKLE' the transformation is | -c | lambda = sigma * theta / ( theta - 1 ) | -c | For TYPE = 'CAYLEY' the transformation is | -c | lambda = sigma * (theta + 1) / (theta - 1 ) | -c | where the theta are the Ritz values returned by igraphdsaupd. | -c | NOTES: | -c | *The Ritz vectors are not affected by the transformation. | -c | They are only reordered. | -c %-------------------------------------------------------------% -c - call dcopy (ncv, workl(ihd), 1, workl(iw), 1) - if (type .eq. 'SHIFTI') then - do 40 k=1, ncv - workl(ihd+k-1) = one / workl(ihd+k-1) + sigma - 40 continue - else if (type .eq. 'BUCKLE') then - do 50 k=1, ncv - workl(ihd+k-1) = sigma * workl(ihd+k-1) / - & (workl(ihd+k-1) - one) - 50 continue - else if (type .eq. 'CAYLEY') then - do 60 k=1, ncv - workl(ihd+k-1) = sigma * (workl(ihd+k-1) + one) / - & (workl(ihd+k-1) - one) - 60 continue - end if -c -c %-------------------------------------------------------------% -c | * Store the wanted NCONV lambda values into D. | -c | * Sort the NCONV wanted lambda in WORKL(IHD:IHD+NCONV-1) | -c | into ascending order and apply sort to the NCONV theta | -c | values in the transformed system. We'll need this to | -c | compute Ritz estimates in the original system. | -c | * Finally sort the lambda's into ascending order and apply | -c | to Ritz vectors if wanted. Else just sort lambda's into | -c | ascending order. | -c | NOTES: | -c | *workl(iw:iw+ncv-1) contain the theta ordered so that they | -c | match the ordering of the lambda. We'll use them again for | -c | Ritz vector purification. | -c %-------------------------------------------------------------% -c - call dcopy (nconv, workl(ihd), 1, d, 1) - call igraphdsortr ('LA', .true., nconv, workl(ihd), workl(iw)) - if (rvec) then - call igraphdsesrt ('LA', rvec , nconv, d, ncv, workl(iq), - & ldq) - else - call dcopy (ncv, workl(bounds), 1, workl(ihb), 1) - call dscal (ncv, bnorm2/rnorm, workl(ihb), 1) - call igraphdsortr ('LA', .true., nconv, d, workl(ihb)) - end if -c - end if -c -c %------------------------------------------------% -c | Compute the Ritz vectors. Transform the wanted | -c | eigenvectors of the symmetric tridiagonal H by | -c | the Lanczos basis matrix V. | -c %------------------------------------------------% -c - if (rvec .and. howmny .eq. 'A') then -c -c %----------------------------------------------------------% -c | Compute the QR factorization of the matrix representing | -c | the wanted invariant subspace located in the first NCONV | -c | columns of workl(iq,ldq). | -c %----------------------------------------------------------% -c - call dgeqr2 (ncv, nconv, workl(iq), ldq, workl(iw+ncv), - & workl(ihb), ierr) -c -c -c %--------------------------------------------------------% -c | * Postmultiply V by Q. | -c | * Copy the first NCONV columns of VQ into Z. | -c | The N by NCONV matrix Z is now a matrix representation | -c | of the approximate invariant subspace associated with | -c | the Ritz values in workl(ihd). | -c %--------------------------------------------------------% -c - call dorm2r ('Right', 'Notranspose', n, ncv, nconv, workl(iq), - & ldq, workl(iw+ncv), v, ldv, workd(n+1), ierr) - call dlacpy ('All', n, nconv, v, ldv, z, ldz) -c -c %-----------------------------------------------------% -c | In order to compute the Ritz estimates for the Ritz | -c | values in both systems, need the last row of the | -c | eigenvector matrix. Remember, it's in factored form | -c %-----------------------------------------------------% -c - do 65 j = 1, ncv-1 - workl(ihb+j-1) = zero - 65 continue - workl(ihb+ncv-1) = one - call dorm2r ('Left', 'Transpose', ncv, 1, nconv, workl(iq), - & ldq, workl(iw+ncv), workl(ihb), ncv, temp, ierr) -c - else if (rvec .and. howmny .eq. 'S') then -c -c Not yet implemented. See remark 2 above. -c - end if -c - if (type .eq. 'REGULR' .and. rvec) then -c - do 70 j=1, ncv - workl(ihb+j-1) = rnorm * abs( workl(ihb+j-1) ) - 70 continue -c - else if (type .ne. 'REGULR' .and. rvec) then -c -c %-------------------------------------------------% -c | * Determine Ritz estimates of the theta. | -c | If RVEC = .true. then compute Ritz estimates | -c | of the theta. | -c | If RVEC = .false. then copy Ritz estimates | -c | as computed by igraphdsaupd. | -c | * Determine Ritz estimates of the lambda. | -c %-------------------------------------------------% -c - call dscal (ncv, bnorm2, workl(ihb), 1) - if (type .eq. 'SHIFTI') then -c - do 80 k=1, ncv - workl(ihb+k-1) = abs( workl(ihb+k-1) ) / workl(iw+k-1)**2 - 80 continue -c - else if (type .eq. 'BUCKLE') then -c - do 90 k=1, ncv - workl(ihb+k-1) = sigma * abs( workl(ihb+k-1) ) / - & ( workl(iw+k-1)-one )**2 - 90 continue -c - else if (type .eq. 'CAYLEY') then -c - do 100 k=1, ncv - workl(ihb+k-1) = abs( workl(ihb+k-1) / - & workl(iw+k-1)*(workl(iw+k-1)-one) ) - 100 continue -c - end if -c - end if -c - if (type .ne. 'REGULR' .and. msglvl .gt. 1) then - call igraphdvout (logfil, nconv, d, ndigit, - & '_seupd: Untransformed converged Ritz values') - call igraphdvout (logfil, nconv, workl(ihb), ndigit, - & '_seupd: Ritz estimates of the untransformed Ritz values') - else if (msglvl .gt. 1) then - call igraphdvout (logfil, nconv, d, ndigit, - & '_seupd: Converged Ritz values') - call igraphdvout (logfil, nconv, workl(ihb), ndigit, - & '_seupd: Associated Ritz estimates') - end if -c -c %-------------------------------------------------% -c | Ritz vector purification step. Formally perform | -c | one of inverse subspace iteration. Only used | -c | for MODE = 3,4,5. See reference 7 | -c %-------------------------------------------------% -c - if (rvec .and. (type .eq. 'SHIFTI' .or. type .eq. 'CAYLEY')) then -c - do 110 k=0, nconv-1 - workl(iw+k) = workl(iq+k*ldq+ncv-1) / workl(iw+k) - 110 continue -c - else if (rvec .and. type .eq. 'BUCKLE') then -c - do 120 k=0, nconv-1 - workl(iw+k) = workl(iq+k*ldq+ncv-1) / (workl(iw+k)-one) - 120 continue -c - end if -c - if (type .ne. 'REGULR') - & call dger (n, nconv, one, resid, 1, workl(iw), 1, z, ldz) -c - 9000 continue -c - return -c -c %---------------% -c | End of igraphdseupd | -c %---------------% -c - end diff --git a/src/rigraph/vendor/arpack/dsgets.f b/src/rigraph/vendor/arpack/dsgets.f deleted file mode 100644 index 2b0794f..0000000 --- a/src/rigraph/vendor/arpack/dsgets.f +++ /dev/null @@ -1,220 +0,0 @@ -c----------------------------------------------------------------------- -c\BeginDoc -c -c\Name: igraphdsgets -c -c\Description: -c Given the eigenvalues of the symmetric tridiagonal matrix H, -c computes the NP shifts AMU that are zeros of the polynomial of -c degree NP which filters out components of the unwanted eigenvectors -c corresponding to the AMU's based on some given criteria. -c -c NOTE: This is called even in the case of user specified shifts in -c order to sort the eigenvalues, and error bounds of H for later use. -c -c\Usage: -c call igraphdsgets -c ( ISHIFT, WHICH, KEV, NP, RITZ, BOUNDS, SHIFTS ) -c -c\Arguments -c ISHIFT Integer. (INPUT) -c Method for selecting the implicit shifts at each iteration. -c ISHIFT = 0: user specified shifts -c ISHIFT = 1: exact shift with respect to the matrix H. -c -c WHICH Character*2. (INPUT) -c Shift selection criteria. -c 'LM' -> KEV eigenvalues of largest magnitude are retained. -c 'SM' -> KEV eigenvalues of smallest magnitude are retained. -c 'LA' -> KEV eigenvalues of largest value are retained. -c 'SA' -> KEV eigenvalues of smallest value are retained. -c 'BE' -> KEV eigenvalues, half from each end of the spectrum. -c If KEV is odd, compute one more from the high end. -c -c KEV Integer. (INPUT) -c KEV+NP is the size of the matrix H. -c -c NP Integer. (INPUT) -c Number of implicit shifts to be computed. -c -c RITZ Double precision array of length KEV+NP. (INPUT/OUTPUT) -c On INPUT, RITZ contains the eigenvalues of H. -c On OUTPUT, RITZ are sorted so that the unwanted eigenvalues -c are in the first NP locations and the wanted part is in -c the last KEV locations. When exact shifts are selected, the -c unwanted part corresponds to the shifts to be applied. -c -c BOUNDS Double precision array of length KEV+NP. (INPUT/OUTPUT) -c Error bounds corresponding to the ordering in RITZ. -c -c SHIFTS Double precision array of length NP. (INPUT/OUTPUT) -c On INPUT: contains the user specified shifts if ISHIFT = 0. -c On OUTPUT: contains the shifts sorted into decreasing order -c of magnitude with respect to the Ritz estimates contained in -c BOUNDS. If ISHIFT = 0, SHIFTS is not modified on exit. -c -c\EndDoc -c -c----------------------------------------------------------------------- -c -c\BeginLib -c -c\Local variables: -c xxxxxx real -c -c\Routines called: -c igraphdsortr ARPACK utility sorting routine. -c igraphivout ARPACK utility routine that prints integers. -c igraphsecond ARPACK utility routine for timing. -c igraphdvout ARPACK utility routine that prints vectors. -c dcopy Level 1 BLAS that copies one vector to another. -c dswap Level 1 BLAS that swaps the contents of two vectors. -c -c\Author -c Danny Sorensen Phuong Vu -c Richard Lehoucq CRPC / Rice University -c Dept. of Computational & Houston, Texas -c Applied Mathematics -c Rice University -c Houston, Texas -c -c\Revision history: -c xx/xx/93: Version ' 2.1' -c -c\SCCS Information: @(#) -c FILE: sgets.F SID: 2.4 DATE OF SID: 4/19/96 RELEASE: 2 -c -c\Remarks -c -c\EndLib -c -c----------------------------------------------------------------------- -c - subroutine igraphdsgets ( ishift, which, kev, np, ritz, bounds, - & shifts ) -c -c %----------------------------------------------------% -c | Include files for debugging and timing information | -c %----------------------------------------------------% -c - include 'debug.h' - include 'stat.h' -c -c %------------------% -c | Scalar Arguments | -c %------------------% -c - character*2 which - integer ishift, kev, np -c -c %-----------------% -c | Array Arguments | -c %-----------------% -c - Double precision - & bounds(kev+np), ritz(kev+np), shifts(np) -c -c %------------% -c | Parameters | -c %------------% -c - Double precision - & one, zero - parameter (one = 1.0D+0, zero = 0.0D+0) -c -c %---------------% -c | Local Scalars | -c %---------------% -c - integer kevd2, msglvl -c -c %----------------------% -c | External Subroutines | -c %----------------------% -c - external dswap, dcopy, igraphdsortr, igraphsecond -c -c %---------------------% -c | Intrinsic Functions | -c %---------------------% -c - intrinsic max, min -c -c %-----------------------% -c | Executable Statements | -c %-----------------------% -c -c %-------------------------------% -c | Initialize timing statistics | -c | & message level for debugging | -c %-------------------------------% -c - call igraphsecond (t0) - msglvl = msgets -c - if (which .eq. 'BE') then -c -c %-----------------------------------------------------% -c | Both ends of the spectrum are requested. | -c | Sort the eigenvalues into algebraically increasing | -c | order first then swap high end of the spectrum next | -c | to low end in appropriate locations. | -c | NOTE: when np < floor(kev/2) be careful not to swap | -c | overlapping locations. | -c %-----------------------------------------------------% -c - call igraphdsortr ('LA', .true., kev+np, ritz, bounds) - kevd2 = kev / 2 - if ( kev .gt. 1 ) then - call dswap ( min(kevd2,np), ritz, 1, - & ritz( max(kevd2,np)+1 ), 1) - call dswap ( min(kevd2,np), bounds, 1, - & bounds( max(kevd2,np)+1 ), 1) - end if -c - else -c -c %----------------------------------------------------% -c | LM, SM, LA, SA case. | -c | Sort the eigenvalues of H into the desired order | -c | and apply the resulting order to BOUNDS. | -c | The eigenvalues are sorted so that the wanted part | -c | are always in the last KEV locations. | -c %----------------------------------------------------% -c - call igraphdsortr (which, .true., kev+np, ritz, bounds) - end if -c - if (ishift .eq. 1 .and. np .gt. 0) then -c -c %-------------------------------------------------------% -c | Sort the unwanted Ritz values used as shifts so that | -c | the ones with largest Ritz estimates are first. | -c | This will tend to minimize the effects of the | -c | forward instability of the iteration when the shifts | -c | are applied in subroutine igraphdsapps. | -c %-------------------------------------------------------% -c - call igraphdsortr ('SM', .true., np, bounds, ritz) - call dcopy (np, ritz, 1, shifts, 1) - end if -c - call igraphsecond (t1) - tsgets = tsgets + (t1 - t0) -c - if (msglvl .gt. 0) then - call igraphivout (logfil, 1, kev, ndigit, '_sgets: KEV is') - call igraphivout (logfil, 1, np, ndigit, '_sgets: NP is') - call igraphdvout (logfil, kev+np, ritz, ndigit, - & '_sgets: Eigenvalues of current H matrix') - call igraphdvout (logfil, kev+np, bounds, ndigit, - & '_sgets: Associated Ritz estimates') - end if -c - return -c -c %---------------% -c | End of igraphdsgets | -c %---------------% -c - end diff --git a/src/rigraph/vendor/arpack/dsortc.f b/src/rigraph/vendor/arpack/dsortc.f deleted file mode 100644 index a356adc..0000000 --- a/src/rigraph/vendor/arpack/dsortc.f +++ /dev/null @@ -1,344 +0,0 @@ -c----------------------------------------------------------------------- -c\BeginDoc -c -c\Name: igraphdsortc -c -c\Description: -c Sorts the complex array in XREAL and XIMAG into the order -c specified by WHICH and optionally applies the permutation to the -c real array Y. It is assumed that if an element of XIMAG is -c nonzero, then its negative is also an element. In other words, -c both members of a complex conjugate pair are to be sorted and the -c pairs are kept adjacent to each other. -c -c\Usage: -c call igraphdsortc -c ( WHICH, APPLY, N, XREAL, XIMAG, Y ) -c -c\Arguments -c WHICH Character*2. (Input) -c 'LM' -> sort XREAL,XIMAG into increasing order of magnitude. -c 'SM' -> sort XREAL,XIMAG into decreasing order of magnitude. -c 'LR' -> sort XREAL into increasing order of algebraic. -c 'SR' -> sort XREAL into decreasing order of algebraic. -c 'LI' -> sort XIMAG into increasing order of magnitude. -c 'SI' -> sort XIMAG into decreasing order of magnitude. -c NOTE: If an element of XIMAG is non-zero, then its negative -c is also an element. -c -c APPLY Logical. (Input) -c APPLY = .TRUE. -> apply the sorted order to array Y. -c APPLY = .FALSE. -> do not apply the sorted order to array Y. -c -c N Integer. (INPUT) -c Size of the arrays. -c -c XREAL, Double precision array of length N. (INPUT/OUTPUT) -c XIMAG Real and imaginary part of the array to be sorted. -c -c Y Double precision array of length N. (INPUT/OUTPUT) -c -c\EndDoc -c -c----------------------------------------------------------------------- -c -c\BeginLib -c -c\Author -c Danny Sorensen Phuong Vu -c Richard Lehoucq CRPC / Rice University -c Dept. of Computational & Houston, Texas -c Applied Mathematics -c Rice University -c Houston, Texas -c -c\Revision history: -c xx/xx/92: Version ' 2.1' -c Adapted from the sort routine in LANSO. -c -c\SCCS Information: @(#) -c FILE: sortc.F SID: 2.3 DATE OF SID: 4/20/96 RELEASE: 2 -c -c\EndLib -c -c----------------------------------------------------------------------- -c - subroutine igraphdsortc (which, apply, n, xreal, ximag, y) -c -c %------------------% -c | Scalar Arguments | -c %------------------% -c - character*2 which - logical apply - integer n -c -c %-----------------% -c | Array Arguments | -c %-----------------% -c - Double precision - & xreal(0:n-1), ximag(0:n-1), y(0:n-1) -c -c %---------------% -c | Local Scalars | -c %---------------% -c - integer i, igap, j - Double precision - & temp, temp1, temp2 -c -c %--------------------% -c | External Functions | -c %--------------------% -c - Double precision - & dlapy2 - external dlapy2 -c -c %-----------------------% -c | Executable Statements | -c %-----------------------% -c - igap = n / 2 -c - if (which .eq. 'LM') then -c -c %------------------------------------------------------% -c | Sort XREAL,XIMAG into increasing order of magnitude. | -c %------------------------------------------------------% -c - 10 continue - if (igap .eq. 0) go to 9000 -c - do 30 i = igap, n-1 - j = i-igap - 20 continue -c - if (j.lt.0) go to 30 -c - temp1 = dlapy2(xreal(j),ximag(j)) - temp2 = dlapy2(xreal(j+igap),ximag(j+igap)) -c - if (temp1.gt.temp2) then - temp = xreal(j) - xreal(j) = xreal(j+igap) - xreal(j+igap) = temp -c - temp = ximag(j) - ximag(j) = ximag(j+igap) - ximag(j+igap) = temp -c - if (apply) then - temp = y(j) - y(j) = y(j+igap) - y(j+igap) = temp - end if - else - go to 30 - end if - j = j-igap - go to 20 - 30 continue - igap = igap / 2 - go to 10 -c - else if (which .eq. 'SM') then -c -c %------------------------------------------------------% -c | Sort XREAL,XIMAG into decreasing order of magnitude. | -c %------------------------------------------------------% -c - 40 continue - if (igap .eq. 0) go to 9000 -c - do 60 i = igap, n-1 - j = i-igap - 50 continue -c - if (j .lt. 0) go to 60 -c - temp1 = dlapy2(xreal(j),ximag(j)) - temp2 = dlapy2(xreal(j+igap),ximag(j+igap)) -c - if (temp1.lt.temp2) then - temp = xreal(j) - xreal(j) = xreal(j+igap) - xreal(j+igap) = temp -c - temp = ximag(j) - ximag(j) = ximag(j+igap) - ximag(j+igap) = temp -c - if (apply) then - temp = y(j) - y(j) = y(j+igap) - y(j+igap) = temp - end if - else - go to 60 - endif - j = j-igap - go to 50 - 60 continue - igap = igap / 2 - go to 40 -c - else if (which .eq. 'LR') then -c -c %------------------------------------------------% -c | Sort XREAL into increasing order of algebraic. | -c %------------------------------------------------% -c - 70 continue - if (igap .eq. 0) go to 9000 -c - do 90 i = igap, n-1 - j = i-igap - 80 continue -c - if (j.lt.0) go to 90 -c - if (xreal(j).gt.xreal(j+igap)) then - temp = xreal(j) - xreal(j) = xreal(j+igap) - xreal(j+igap) = temp -c - temp = ximag(j) - ximag(j) = ximag(j+igap) - ximag(j+igap) = temp -c - if (apply) then - temp = y(j) - y(j) = y(j+igap) - y(j+igap) = temp - end if - else - go to 90 - endif - j = j-igap - go to 80 - 90 continue - igap = igap / 2 - go to 70 -c - else if (which .eq. 'SR') then -c -c %------------------------------------------------% -c | Sort XREAL into decreasing order of algebraic. | -c %------------------------------------------------% -c - 100 continue - if (igap .eq. 0) go to 9000 - do 120 i = igap, n-1 - j = i-igap - 110 continue -c - if (j.lt.0) go to 120 -c - if (xreal(j).lt.xreal(j+igap)) then - temp = xreal(j) - xreal(j) = xreal(j+igap) - xreal(j+igap) = temp -c - temp = ximag(j) - ximag(j) = ximag(j+igap) - ximag(j+igap) = temp -c - if (apply) then - temp = y(j) - y(j) = y(j+igap) - y(j+igap) = temp - end if - else - go to 120 - endif - j = j-igap - go to 110 - 120 continue - igap = igap / 2 - go to 100 -c - else if (which .eq. 'LI') then -c -c %------------------------------------------------% -c | Sort XIMAG into increasing order of magnitude. | -c %------------------------------------------------% -c - 130 continue - if (igap .eq. 0) go to 9000 - do 150 i = igap, n-1 - j = i-igap - 140 continue -c - if (j.lt.0) go to 150 -c - if (abs(ximag(j)).gt.abs(ximag(j+igap))) then - temp = xreal(j) - xreal(j) = xreal(j+igap) - xreal(j+igap) = temp -c - temp = ximag(j) - ximag(j) = ximag(j+igap) - ximag(j+igap) = temp -c - if (apply) then - temp = y(j) - y(j) = y(j+igap) - y(j+igap) = temp - end if - else - go to 150 - endif - j = j-igap - go to 140 - 150 continue - igap = igap / 2 - go to 130 -c - else if (which .eq. 'SI') then -c -c %------------------------------------------------% -c | Sort XIMAG into decreasing order of magnitude. | -c %------------------------------------------------% -c - 160 continue - if (igap .eq. 0) go to 9000 - do 180 i = igap, n-1 - j = i-igap - 170 continue -c - if (j.lt.0) go to 180 -c - if (abs(ximag(j)).lt.abs(ximag(j+igap))) then - temp = xreal(j) - xreal(j) = xreal(j+igap) - xreal(j+igap) = temp -c - temp = ximag(j) - ximag(j) = ximag(j+igap) - ximag(j+igap) = temp -c - if (apply) then - temp = y(j) - y(j) = y(j+igap) - y(j+igap) = temp - end if - else - go to 180 - endif - j = j-igap - go to 170 - 180 continue - igap = igap / 2 - go to 160 - end if -c - 9000 continue - return -c -c %---------------% -c | End of igraphdsortc | -c %---------------% -c - end diff --git a/src/rigraph/vendor/arpack/dsortr.f b/src/rigraph/vendor/arpack/dsortr.f deleted file mode 100644 index d75bd61..0000000 --- a/src/rigraph/vendor/arpack/dsortr.f +++ /dev/null @@ -1,218 +0,0 @@ -c----------------------------------------------------------------------- -c\BeginDoc -c -c\Name: igraphdsortr -c -c\Description: -c Sort the array X1 in the order specified by WHICH and optionally -c applies the permutation to the array X2. -c -c\Usage: -c call igraphdsortr -c ( WHICH, APPLY, N, X1, X2 ) -c -c\Arguments -c WHICH Character*2. (Input) -c 'LM' -> X1 is sorted into increasing order of magnitude. -c 'SM' -> X1 is sorted into decreasing order of magnitude. -c 'LA' -> X1 is sorted into increasing order of algebraic. -c 'SA' -> X1 is sorted into decreasing order of algebraic. -c -c APPLY Logical. (Input) -c APPLY = .TRUE. -> apply the sorted order to X2. -c APPLY = .FALSE. -> do not apply the sorted order to X2. -c -c N Integer. (INPUT) -c Size of the arrays. -c -c X1 Double precision array of length N. (INPUT/OUTPUT) -c The array to be sorted. -c -c X2 Double precision array of length N. (INPUT/OUTPUT) -c Only referenced if APPLY = .TRUE. -c -c\EndDoc -c -c----------------------------------------------------------------------- -c -c\BeginLib -c -c\Author -c Danny Sorensen Phuong Vu -c Richard Lehoucq CRPC / Rice University -c Dept. of Computational & Houston, Texas -c Applied Mathematics -c Rice University -c Houston, Texas -c -c\Revision history: -c 12/16/93: Version ' 2.1'. -c Adapted from the sort routine in LANSO. -c -c\SCCS Information: @(#) -c FILE: sortr.F SID: 2.3 DATE OF SID: 4/19/96 RELEASE: 2 -c -c\EndLib -c -c----------------------------------------------------------------------- -c - subroutine igraphdsortr (which, apply, n, x1, x2) -c -c %------------------% -c | Scalar Arguments | -c %------------------% -c - character*2 which - logical apply - integer n -c -c %-----------------% -c | Array Arguments | -c %-----------------% -c - Double precision - & x1(0:n-1), x2(0:n-1) -c -c %---------------% -c | Local Scalars | -c %---------------% -c - integer i, igap, j - Double precision - & temp -c -c %-----------------------% -c | Executable Statements | -c %-----------------------% -c - igap = n / 2 -c - if (which .eq. 'SA') then -c -c X1 is sorted into decreasing order of algebraic. -c - 10 continue - if (igap .eq. 0) go to 9000 - do 30 i = igap, n-1 - j = i-igap - 20 continue -c - if (j.lt.0) go to 30 -c - if (x1(j).lt.x1(j+igap)) then - temp = x1(j) - x1(j) = x1(j+igap) - x1(j+igap) = temp - if (apply) then - temp = x2(j) - x2(j) = x2(j+igap) - x2(j+igap) = temp - end if - else - go to 30 - endif - j = j-igap - go to 20 - 30 continue - igap = igap / 2 - go to 10 -c - else if (which .eq. 'SM') then -c -c X1 is sorted into decreasing order of magnitude. -c - 40 continue - if (igap .eq. 0) go to 9000 - do 60 i = igap, n-1 - j = i-igap - 50 continue -c - if (j.lt.0) go to 60 -c - if (abs(x1(j)).lt.abs(x1(j+igap))) then - temp = x1(j) - x1(j) = x1(j+igap) - x1(j+igap) = temp - if (apply) then - temp = x2(j) - x2(j) = x2(j+igap) - x2(j+igap) = temp - end if - else - go to 60 - endif - j = j-igap - go to 50 - 60 continue - igap = igap / 2 - go to 40 -c - else if (which .eq. 'LA') then -c -c X1 is sorted into increasing order of algebraic. -c - 70 continue - if (igap .eq. 0) go to 9000 - do 90 i = igap, n-1 - j = i-igap - 80 continue -c - if (j.lt.0) go to 90 -c - if (x1(j).gt.x1(j+igap)) then - temp = x1(j) - x1(j) = x1(j+igap) - x1(j+igap) = temp - if (apply) then - temp = x2(j) - x2(j) = x2(j+igap) - x2(j+igap) = temp - end if - else - go to 90 - endif - j = j-igap - go to 80 - 90 continue - igap = igap / 2 - go to 70 -c - else if (which .eq. 'LM') then -c -c X1 is sorted into increasing order of magnitude. -c - 100 continue - if (igap .eq. 0) go to 9000 - do 120 i = igap, n-1 - j = i-igap - 110 continue -c - if (j.lt.0) go to 120 -c - if (abs(x1(j)).gt.abs(x1(j+igap))) then - temp = x1(j) - x1(j) = x1(j+igap) - x1(j+igap) = temp - if (apply) then - temp = x2(j) - x2(j) = x2(j+igap) - x2(j+igap) = temp - end if - else - go to 120 - endif - j = j-igap - go to 110 - 120 continue - igap = igap / 2 - go to 100 - end if -c - 9000 continue - return -c -c %---------------% -c | End of igraphdsortr | -c %---------------% -c - end diff --git a/src/rigraph/vendor/arpack/dstatn.f b/src/rigraph/vendor/arpack/dstatn.f deleted file mode 100644 index afd0a57..0000000 --- a/src/rigraph/vendor/arpack/dstatn.f +++ /dev/null @@ -1,61 +0,0 @@ -c -c %---------------------------------------------% -c | Initialize statistic and timing information | -c | for nonsymmetric Arnoldi code. | -c %---------------------------------------------% -c -c\Author -c Danny Sorensen Phuong Vu -c Richard Lehoucq CRPC / Rice University -c Dept. of Computational & Houston, Texas -c Applied Mathematics -c Rice University -c Houston, Texas -c -c\SCCS Information: @(#) -c FILE: statn.F SID: 2.4 DATE OF SID: 4/20/96 RELEASE: 2 -c - subroutine igraphdstatn -c -c %--------------------------------% -c | See stat.doc for documentation | -c %--------------------------------% -c - include 'stat.h' -c -c %-----------------------% -c | Executable Statements | -c %-----------------------% -c - nopx = 0 - nbx = 0 - nrorth = 0 - nitref = 0 - nrstrt = 0 -c - tnaupd = 0.0D+0 - tnaup2 = 0.0D+0 - tnaitr = 0.0D+0 - tneigh = 0.0D+0 - tngets = 0.0D+0 - tnapps = 0.0D+0 - tnconv = 0.0D+0 - titref = 0.0D+0 - tgetv0 = 0.0D+0 - trvec = 0.0D+0 -c -c %----------------------------------------------------% -c | User time including reverse communication overhead | -c %----------------------------------------------------% -c - tmvopx = 0.0D+0 - tmvbx = 0.0D+0 -c - return -c -c -c %---------------% -c | End of igraphdstatn | -c %---------------% -c - end diff --git a/src/rigraph/vendor/arpack/dstats.f b/src/rigraph/vendor/arpack/dstats.f deleted file mode 100644 index 545ed19..0000000 --- a/src/rigraph/vendor/arpack/dstats.f +++ /dev/null @@ -1,47 +0,0 @@ -c -c\SCCS Information: @(#) -c FILE: stats.F SID: 2.1 DATE OF SID: 4/19/96 RELEASE: 2 -c %---------------------------------------------% -c | Initialize statistic and timing information | -c | for symmetric Arnoldi code. | -c %---------------------------------------------% - - subroutine igraphdstats - -c %--------------------------------% -c | See stat.doc for documentation | -c %--------------------------------% - include 'stat.h' - -c %-----------------------% -c | Executable Statements | -c %-----------------------% - - nopx = 0 - nbx = 0 - nrorth = 0 - nitref = 0 - nrstrt = 0 - - tsaupd = 0.0D+0 - tsaup2 = 0.0D+0 - tsaitr = 0.0D+0 - tseigt = 0.0D+0 - tsgets = 0.0D+0 - tsapps = 0.0D+0 - tsconv = 0.0D+0 - titref = 0.0D+0 - tgetv0 = 0.0D+0 - trvec = 0.0D+0 - -c %----------------------------------------------------% -c | User time including reverse communication overhead | -c %----------------------------------------------------% - tmvopx = 0.0D+0 - tmvbx = 0.0D+0 - - return -c -c End of igraphdstats -c - end diff --git a/src/rigraph/vendor/arpack/dstqrb.f b/src/rigraph/vendor/arpack/dstqrb.f deleted file mode 100644 index eff1369..0000000 --- a/src/rigraph/vendor/arpack/dstqrb.f +++ /dev/null @@ -1,594 +0,0 @@ -c----------------------------------------------------------------------- -c\BeginDoc -c -c\Name: igraphdstqrb -c -c\Description: -c Computes all eigenvalues and the last component of the eigenvectors -c of a symmetric tridiagonal matrix using the implicit QL or QR method. -c -c This is mostly a modification of the LAPACK routine dsteqr. -c See Remarks. -c -c\Usage: -c call igraphdstqrb -c ( N, D, E, Z, WORK, INFO ) -c -c\Arguments -c N Integer. (INPUT) -c The number of rows and columns in the matrix. N >= 0. -c -c D Double precision array, dimension (N). (INPUT/OUTPUT) -c On entry, D contains the diagonal elements of the -c tridiagonal matrix. -c On exit, D contains the eigenvalues, in ascending order. -c If an error exit is made, the eigenvalues are correct -c for indices 1,2,...,INFO-1, but they are unordered and -c may not be the smallest eigenvalues of the matrix. -c -c E Double precision array, dimension (N-1). (INPUT/OUTPUT) -c On entry, E contains the subdiagonal elements of the -c tridiagonal matrix in positions 1 through N-1. -c On exit, E has been destroyed. -c -c Z Double precision array, dimension (N). (OUTPUT) -c On exit, Z contains the last row of the orthonormal -c eigenvector matrix of the symmetric tridiagonal matrix. -c If an error exit is made, Z contains the last row of the -c eigenvector matrix associated with the stored eigenvalues. -c -c WORK Double precision array, dimension (max(1,2*N-2)). (WORKSPACE) -c Workspace used in accumulating the transformation for -c computing the last components of the eigenvectors. -c -c INFO Integer. (OUTPUT) -c = 0: normal return. -c < 0: if INFO = -i, the i-th argument had an illegal value. -c > 0: if INFO = +i, the i-th eigenvalue has not converged -c after a total of 30*N iterations. -c -c\Remarks -c 1. None. -c -c----------------------------------------------------------------------- -c -c\BeginLib -c -c\Local variables: -c xxxxxx real -c -c\Routines called: -c daxpy Level 1 BLAS that computes a vector triad. -c dcopy Level 1 BLAS that copies one vector to another. -c dswap Level 1 BLAS that swaps the contents of two vectors. -c lsame LAPACK character comparison routine. -c dlae2 LAPACK routine that computes the eigenvalues of a 2-by-2 -c symmetric matrix. -c dlaev2 LAPACK routine that eigendecomposition of a 2-by-2 symmetric -c matrix. -c dlamch LAPACK routine that determines machine constants. -c dlanst LAPACK routine that computes the norm of a matrix. -c dlapy2 LAPACK routine to compute sqrt(x**2+y**2) carefully. -c dlartg LAPACK Givens rotation construction routine. -c dlascl LAPACK routine for careful scaling of a matrix. -c dlaset LAPACK matrix initialization routine. -c dlasr LAPACK routine that applies an orthogonal transformation to -c a matrix. -c dlasrt LAPACK sorting routine. -c dsteqr LAPACK routine that computes eigenvalues and eigenvectors -c of a symmetric tridiagonal matrix. -c xerbla LAPACK error handler routine. -c -c\Authors -c Danny Sorensen Phuong Vu -c Richard Lehoucq CRPC / Rice University -c Dept. of Computational & Houston, Texas -c Applied Mathematics -c Rice University -c Houston, Texas -c -c\SCCS Information: @(#) -c FILE: stqrb.F SID: 2.5 DATE OF SID: 8/27/96 RELEASE: 2 -c -c\Remarks -c 1. Starting with version 2.5, this routine is a modified version -c of LAPACK version 2.0 subroutine SSTEQR. No lines are deleted, -c only commeted out and new lines inserted. -c All lines commented out have "c$$$" at the beginning. -c Note that the LAPACK version 1.0 subroutine SSTEQR contained -c bugs. -c -c\EndLib -c -c----------------------------------------------------------------------- -c - subroutine igraphdstqrb ( n, d, e, z, work, info ) -c -c %------------------% -c | Scalar Arguments | -c %------------------% -c - integer info, n -c -c %-----------------% -c | Array Arguments | -c %-----------------% -c - Double precision - & d( n ), e( n-1 ), z( n ), work( 2*n-2 ) -c -c .. parameters .. - Double precision - & zero, one, two, three - parameter ( zero = 0.0D+0, one = 1.0D+0, - & two = 2.0D+0, three = 3.0D+0 ) - integer maxit - parameter ( maxit = 30 ) -c .. -c .. local scalars .. - integer i, icompz, ii, iscale, j, jtot, k, l, l1, lend, - & lendm1, lendp1, lendsv, lm1, lsv, m, mm, mm1, - & nm1, nmaxit - Double precision - & anorm, b, c, eps, eps2, f, g, p, r, rt1, rt2, - & s, safmax, safmin, ssfmax, ssfmin, tst -c .. -c .. external functions .. - logical lsame - Double precision - & dlamch, dlanst, dlapy2 - external lsame, dlamch, dlanst, dlapy2 -c .. -c .. external subroutines .. - external dlae2, dlaev2, dlartg, dlascl, dlaset, dlasr, - & dlasrt, dswap, xerbla -c .. -c .. intrinsic functions .. - intrinsic abs, max, sign, sqrt -c .. -c .. executable statements .. -c -c test the input parameters. -c - info = 0 -c -c$$$ IF( LSAME( COMPZ, 'N' ) ) THEN -c$$$ ICOMPZ = 0 -c$$$ ELSE IF( LSAME( COMPZ, 'V' ) ) THEN -c$$$ ICOMPZ = 1 -c$$$ ELSE IF( LSAME( COMPZ, 'I' ) ) THEN -c$$$ ICOMPZ = 2 -c$$$ ELSE -c$$$ ICOMPZ = -1 -c$$$ END IF -c$$$ IF( ICOMPZ.LT.0 ) THEN -c$$$ INFO = -1 -c$$$ ELSE IF( N.LT.0 ) THEN -c$$$ INFO = -2 -c$$$ ELSE IF( ( LDZ.LT.1 ) .OR. ( ICOMPZ.GT.0 .AND. LDZ.LT.MAX( 1, -c$$$ $ N ) ) ) THEN -c$$$ INFO = -6 -c$$$ END IF -c$$$ IF( INFO.NE.0 ) THEN -c$$$ CALL XERBLA( 'SSTEQR', -INFO ) -c$$$ RETURN -c$$$ END IF -c -c *** New starting with version 2.5 *** -c - icompz = 2 -c ************************************* -c -c quick return if possible -c - if( n.eq.0 ) - $ return -c - if( n.eq.1 ) then - if( icompz.eq.2 ) z( 1 ) = one - return - end if -c -c determine the unit roundoff and over/underflow thresholds. -c - eps = dlamch( 'e' ) - eps2 = eps**2 - safmin = dlamch( 's' ) - safmax = one / safmin - ssfmax = sqrt( safmax ) / three - ssfmin = sqrt( safmin ) / eps2 -c -c compute the eigenvalues and eigenvectors of the tridiagonal -c matrix. -c -c$$ if( icompz.eq.2 ) -c$$$ $ call dlaset( 'full', n, n, zero, one, z, ldz ) -c -c *** New starting with version 2.5 *** -c - if ( icompz .eq. 2 ) then - do 5 j = 1, n-1 - z(j) = zero - 5 continue - z( n ) = one - end if -c ************************************* -c - nmaxit = n*maxit - jtot = 0 -c -c determine where the matrix splits and choose ql or qr iteration -c for each block, according to whether top or bottom diagonal -c element is smaller. -c - l1 = 1 - nm1 = n - 1 -c - 10 continue - if( l1.gt.n ) - $ go to 160 - if( l1.gt.1 ) - $ e( l1-1 ) = zero - if( l1.le.nm1 ) then - do 20 m = l1, nm1 - tst = abs( e( m ) ) - if( tst.eq.zero ) - $ go to 30 - if( tst.le.( sqrt( abs( d( m ) ) )*sqrt( abs( d( m+ - $ 1 ) ) ) )*eps ) then - e( m ) = zero - go to 30 - end if - 20 continue - end if - m = n -c - 30 continue - l = l1 - lsv = l - lend = m - lendsv = lend - l1 = m + 1 - if( lend.eq.l ) - $ go to 10 -c -c scale submatrix in rows and columns l to lend -c - anorm = dlanst( 'i', lend-l+1, d( l ), e( l ) ) - iscale = 0 - if( anorm.eq.zero ) - $ go to 10 - if( anorm.gt.ssfmax ) then - iscale = 1 - call dlascl( 'g', 0, 0, anorm, ssfmax, lend-l+1, 1, d( l ), n, - $ info ) - call dlascl( 'g', 0, 0, anorm, ssfmax, lend-l, 1, e( l ), n, - $ info ) - else if( anorm.lt.ssfmin ) then - iscale = 2 - call dlascl( 'g', 0, 0, anorm, ssfmin, lend-l+1, 1, d( l ), n, - $ info ) - call dlascl( 'g', 0, 0, anorm, ssfmin, lend-l, 1, e( l ), n, - $ info ) - end if -c -c choose between ql and qr iteration -c - if( abs( d( lend ) ).lt.abs( d( l ) ) ) then - lend = lsv - l = lendsv - end if -c - if( lend.gt.l ) then -c -c ql iteration -c -c look for small subdiagonal element. -c - 40 continue - if( l.ne.lend ) then - lendm1 = lend - 1 - do 50 m = l, lendm1 - tst = abs( e( m ) )**2 - if( tst.le.( eps2*abs( d( m ) ) )*abs( d( m+1 ) )+ - $ safmin )go to 60 - 50 continue - end if -c - m = lend -c - 60 continue - if( m.lt.lend ) - $ e( m ) = zero - p = d( l ) - if( m.eq.l ) - $ go to 80 -c -c if remaining matrix is 2-by-2, use dlae2 or dlaev2 -c to compute its eigensystem. -c - if( m.eq.l+1 ) then - if( icompz.gt.0 ) then - call dlaev2( d( l ), e( l ), d( l+1 ), rt1, rt2, c, s ) - work( l ) = c - work( n-1+l ) = s -c$$$ call dlasr( 'r', 'v', 'b', n, 2, work( l ), -c$$$ $ work( n-1+l ), z( 1, l ), ldz ) -c -c *** New starting with version 2.5 *** -c - tst = z(l+1) - z(l+1) = c*tst - s*z(l) - z(l) = s*tst + c*z(l) -c ************************************* - else - call dlae2( d( l ), e( l ), d( l+1 ), rt1, rt2 ) - end if - d( l ) = rt1 - d( l+1 ) = rt2 - e( l ) = zero - l = l + 2 - if( l.le.lend ) - $ go to 40 - go to 140 - end if -c - if( jtot.eq.nmaxit ) - $ go to 140 - jtot = jtot + 1 -c -c form shift. -c - g = ( d( l+1 )-p ) / ( two*e( l ) ) - r = dlapy2( g, one ) - g = d( m ) - p + ( e( l ) / ( g+sign( r, g ) ) ) -c - s = one - c = one - p = zero -c -c inner loop -c - mm1 = m - 1 - do 70 i = mm1, l, -1 - f = s*e( i ) - b = c*e( i ) - call dlartg( g, f, c, s, r ) - if( i.ne.m-1 ) - $ e( i+1 ) = r - g = d( i+1 ) - p - r = ( d( i )-g )*s + two*c*b - p = s*r - d( i+1 ) = g + p - g = c*r - b -c -c if eigenvectors are desired, then save rotations. -c - if( icompz.gt.0 ) then - work( i ) = c - work( n-1+i ) = -s - end if -c - 70 continue -c -c if eigenvectors are desired, then apply saved rotations. -c - if( icompz.gt.0 ) then - mm = m - l + 1 -c$$$ call dlasr( 'r', 'v', 'b', n, mm, work( l ), work( n-1+l ), -c$$$ $ z( 1, l ), ldz ) -c -c *** New starting with version 2.5 *** -c - call dlasr( 'r', 'v', 'b', 1, mm, work( l ), - & work( n-1+l ), z( l ), 1 ) -c ************************************* - end if -c - d( l ) = d( l ) - p - e( l ) = g - go to 40 -c -c eigenvalue found. -c - 80 continue - d( l ) = p -c - l = l + 1 - if( l.le.lend ) - $ go to 40 - go to 140 -c - else -c -c qr iteration -c -c look for small superdiagonal element. -c - 90 continue - if( l.ne.lend ) then - lendp1 = lend + 1 - do 100 m = l, lendp1, -1 - tst = abs( e( m-1 ) )**2 - if( tst.le.( eps2*abs( d( m ) ) )*abs( d( m-1 ) )+ - $ safmin )go to 110 - 100 continue - end if -c - m = lend -c - 110 continue - if( m.gt.lend ) - $ e( m-1 ) = zero - p = d( l ) - if( m.eq.l ) - $ go to 130 -c -c if remaining matrix is 2-by-2, use dlae2 or dlaev2 -c to compute its eigensystem. -c - if( m.eq.l-1 ) then - if( icompz.gt.0 ) then - call dlaev2( d( l-1 ), e( l-1 ), d( l ), rt1, rt2, c, s ) -c$$$ work( m ) = c -c$$$ work( n-1+m ) = s -c$$$ call dlasr( 'r', 'v', 'f', n, 2, work( m ), -c$$$ $ work( n-1+m ), z( 1, l-1 ), ldz ) -c -c *** New starting with version 2.5 *** -c - tst = z(l) - z(l) = c*tst - s*z(l-1) - z(l-1) = s*tst + c*z(l-1) -c ************************************* - else - call dlae2( d( l-1 ), e( l-1 ), d( l ), rt1, rt2 ) - end if - d( l-1 ) = rt1 - d( l ) = rt2 - e( l-1 ) = zero - l = l - 2 - if( l.ge.lend ) - $ go to 90 - go to 140 - end if -c - if( jtot.eq.nmaxit ) - $ go to 140 - jtot = jtot + 1 -c -c form shift. -c - g = ( d( l-1 )-p ) / ( two*e( l-1 ) ) - r = dlapy2( g, one ) - g = d( m ) - p + ( e( l-1 ) / ( g+sign( r, g ) ) ) -c - s = one - c = one - p = zero -c -c inner loop -c - lm1 = l - 1 - do 120 i = m, lm1 - f = s*e( i ) - b = c*e( i ) - call dlartg( g, f, c, s, r ) - if( i.ne.m ) - $ e( i-1 ) = r - g = d( i ) - p - r = ( d( i+1 )-g )*s + two*c*b - p = s*r - d( i ) = g + p - g = c*r - b -c -c if eigenvectors are desired, then save rotations. -c - if( icompz.gt.0 ) then - work( i ) = c - work( n-1+i ) = s - end if -c - 120 continue -c -c if eigenvectors are desired, then apply saved rotations. -c - if( icompz.gt.0 ) then - mm = l - m + 1 -c$$$ call dlasr( 'r', 'v', 'f', n, mm, work( m ), work( n-1+m ), -c$$$ $ z( 1, m ), ldz ) -c -c *** New starting with version 2.5 *** -c - call dlasr( 'r', 'v', 'f', 1, mm, work( m ), work( n-1+m ), - & z( m ), 1 ) -c ************************************* - end if -c - d( l ) = d( l ) - p - e( lm1 ) = g - go to 90 -c -c eigenvalue found. -c - 130 continue - d( l ) = p -c - l = l - 1 - if( l.ge.lend ) - $ go to 90 - go to 140 -c - end if -c -c undo scaling if necessary -c - 140 continue - if( iscale.eq.1 ) then - call dlascl( 'g', 0, 0, ssfmax, anorm, lendsv-lsv+1, 1, - $ d( lsv ), n, info ) - call dlascl( 'g', 0, 0, ssfmax, anorm, lendsv-lsv, 1, e( lsv ), - $ n, info ) - else if( iscale.eq.2 ) then - call dlascl( 'g', 0, 0, ssfmin, anorm, lendsv-lsv+1, 1, - $ d( lsv ), n, info ) - call dlascl( 'g', 0, 0, ssfmin, anorm, lendsv-lsv, 1, e( lsv ), - $ n, info ) - end if -c -c check for no convergence to an eigenvalue after a total -c of n*maxit iterations. -c - if( jtot.lt.nmaxit ) - $ go to 10 - do 150 i = 1, n - 1 - if( e( i ).ne.zero ) - $ info = info + 1 - 150 continue - go to 190 -c -c order eigenvalues and eigenvectors. -c - 160 continue - if( icompz.eq.0 ) then -c -c use quick sort -c - call dlasrt( 'i', n, d, info ) -c - else -c -c use selection sort to minimize swaps of eigenvectors -c - do 180 ii = 2, n - i = ii - 1 - k = i - p = d( i ) - do 170 j = ii, n - if( d( j ).lt.p ) then - k = j - p = d( j ) - end if - 170 continue - if( k.ne.i ) then - d( k ) = d( i ) - d( i ) = p -c$$$ call dswap( n, z( 1, i ), 1, z( 1, k ), 1 ) -c *** New starting with version 2.5 *** -c - p = z(k) - z(k) = z(i) - z(i) = p -c ************************************* - end if - 180 continue - end if -c - 190 continue - return -c -c %---------------% -c | End of igraphdstqrb | -c %---------------% -c - end diff --git a/src/rigraph/vendor/arpack/dvout.f b/src/rigraph/vendor/arpack/dvout.f deleted file mode 100644 index 8bd7b1b..0000000 --- a/src/rigraph/vendor/arpack/dvout.f +++ /dev/null @@ -1,122 +0,0 @@ -*----------------------------------------------------------------------- -* 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 IGRAPHDVOUT( LOUT, N, SX, IDIGIT, IFMT ) -* ... -* ... SPECIFICATIONS FOR ARGUMENTS -* ... -* ... SPECIFICATIONS FOR LOCAL VARIABLES -* .. Scalar Arguments .. - CHARACTER*( * ) IFMT - INTEGER IDIGIT, LOUT, N -* .. -* .. Array Arguments .. - DOUBLE PRECISION SX( * ) -* .. -* .. Local Scalars .. - CHARACTER*80 LINE - INTEGER I, K1, K2, LLL, NDIGIT -* .. -* .. Intrinsic Functions .. - INTRINSIC LEN, MIN, MIN0 -* .. -* .. Executable Statements .. -* ... -* ... FIRST EXECUTABLE STATEMENT -* -* -c$$$ LLL = MIN( LEN( IFMT ), 80 ) -c$$$ DO 10 I = 1, LLL -c$$$ LINE( I: I ) = '-' -c$$$ 10 CONTINUE -c$$$* -c$$$ DO 20 I = LLL + 1, 80 -c$$$ LINE( I: I ) = ' ' -c$$$ 20 CONTINUE -c$$$* -c$$$ WRITE( LOUT, FMT = 9999 )IFMT, LINE( 1: LLL ) -c$$$ 9999 FORMAT( / 1X, A, / 1X, A ) -c$$$* -c$$$ IF( N.LE.0 ) -c$$$ $ RETURN -c$$$ NDIGIT = IDIGIT -c$$$ IF( IDIGIT.EQ.0 ) -c$$$ $ NDIGIT = 4 -c$$$* -c$$$*======================================================================= -c$$$* CODE FOR OUTPUT USING 72 COLUMNS FORMAT -c$$$*======================================================================= -c$$$* -c$$$ IF( IDIGIT.LT.0 ) THEN -c$$$ NDIGIT = -IDIGIT -c$$$ IF( NDIGIT.LE.4 ) THEN -c$$$ DO 30 K1 = 1, N, 5 -c$$$ K2 = MIN0( N, K1+4 ) -c$$$ WRITE( LOUT, FMT = 9998 )K1, K2, ( SX( I ), I = K1, K2 ) -c$$$ 30 CONTINUE -c$$$ ELSE IF( NDIGIT.LE.6 ) THEN -c$$$ DO 40 K1 = 1, N, 4 -c$$$ K2 = MIN0( N, K1+3 ) -c$$$ WRITE( LOUT, FMT = 9997 )K1, K2, ( SX( I ), I = K1, K2 ) -c$$$ 40 CONTINUE -c$$$ ELSE IF( NDIGIT.LE.10 ) THEN -c$$$ DO 50 K1 = 1, N, 3 -c$$$ K2 = MIN0( N, K1+2 ) -c$$$ WRITE( LOUT, FMT = 9996 )K1, K2, ( SX( I ), I = K1, K2 ) -c$$$ 50 CONTINUE -c$$$ ELSE -c$$$ DO 60 K1 = 1, N, 2 -c$$$ K2 = MIN0( N, K1+1 ) -c$$$ WRITE( LOUT, FMT = 9995 )K1, K2, ( SX( I ), I = K1, K2 ) -c$$$ 60 CONTINUE -c$$$ END IF -c$$$* -c$$$*======================================================================= -c$$$* CODE FOR OUTPUT USING 132 COLUMNS FORMAT -c$$$*======================================================================= -c$$$* -c$$$ ELSE -c$$$ IF( NDIGIT.LE.4 ) THEN -c$$$ DO 70 K1 = 1, N, 10 -c$$$ K2 = MIN0( N, K1+9 ) -c$$$ WRITE( LOUT, FMT = 9998 )K1, K2, ( SX( I ), I = K1, K2 ) -c$$$ 70 CONTINUE -c$$$ ELSE IF( NDIGIT.LE.6 ) THEN -c$$$ DO 80 K1 = 1, N, 8 -c$$$ K2 = MIN0( N, K1+7 ) -c$$$ WRITE( LOUT, FMT = 9997 )K1, K2, ( SX( I ), I = K1, K2 ) -c$$$ 80 CONTINUE -c$$$ ELSE IF( NDIGIT.LE.10 ) THEN -c$$$ DO 90 K1 = 1, N, 6 -c$$$ K2 = MIN0( N, K1+5 ) -c$$$ WRITE( LOUT, FMT = 9996 )K1, K2, ( SX( I ), I = K1, K2 ) -c$$$ 90 CONTINUE -c$$$ ELSE -c$$$ DO 100 K1 = 1, N, 5 -c$$$ K2 = MIN0( N, K1+4 ) -c$$$ WRITE( LOUT, FMT = 9995 )K1, K2, ( SX( I ), I = K1, K2 ) -c$$$ 100 CONTINUE -c$$$ END IF -c$$$ END IF -c$$$ WRITE( LOUT, FMT = 9994 ) -c$$$ RETURN -c$$$ 9998 FORMAT( 1X, I4, ' - ', I4, ':', 1P, 10D12.3 ) -c$$$ 9997 FORMAT( 1X, I4, ' - ', I4, ':', 1X, 1P, 8D14.5 ) -c$$$ 9996 FORMAT( 1X, I4, ' - ', I4, ':', 1X, 1P, 6D18.9 ) -c$$$ 9995 FORMAT( 1X, I4, ' - ', I4, ':', 1X, 1P, 5D24.13 ) -c$$$ 9994 FORMAT( 1X, ' ' ) - END diff --git a/src/rigraph/vendor/arpack/ivout.f b/src/rigraph/vendor/arpack/ivout.f deleted file mode 100644 index 3f6089c..0000000 --- a/src/rigraph/vendor/arpack/ivout.f +++ /dev/null @@ -1,120 +0,0 @@ -C----------------------------------------------------------------------- -C Routine: IVOUT -C -C Purpose: Integer vector output routine. -C -C Usage: CALL IVOUT (LOUT, N, IX, IDIGIT, IFMT) -C -C Arguments -C N - Length of array IX. (Input) -C IX - Integer array to be printed. (Input) -C IFMT - Format to be used in printing array IX. (Input) -C IDIGIT - Print up to ABS(IDIGIT) decimal digits / number. (Input) -C If IDIGIT .LT. 0, printing is done with 72 columns. -C If IDIGIT .GT. 0, printing is done with 132 columns. -C -C----------------------------------------------------------------------- -C - SUBROUTINE IGRAPHIVOUT (LOUT, N, IX, IDIGIT, IFMT) -C ... -C ... SPECIFICATIONS FOR ARGUMENTS - INTEGER IX(*), N, IDIGIT, LOUT - CHARACTER IFMT*(*) -C ... -C ... SPECIFICATIONS FOR LOCAL VARIABLES - INTEGER I, NDIGIT, K1, K2, LLL - CHARACTER*80 LINE -* ... -* ... SPECIFICATIONS INTRINSICS - INTRINSIC MIN -* -C -c$$$ LLL = MIN ( LEN ( IFMT ), 80 ) -c$$$ DO 1 I = 1, LLL -c$$$ LINE(I:I) = '-' -c$$$ 1 CONTINUE -c$$$C -c$$$ DO 2 I = LLL+1, 80 -c$$$ LINE(I:I) = ' ' -c$$$ 2 CONTINUE -c$$$C -c$$$ WRITE ( LOUT, 2000 ) IFMT, LINE(1:LLL) -c$$$ 2000 FORMAT ( /1X, A /1X, A ) -c$$$C -c$$$ IF (N .LE. 0) RETURN -c$$$ NDIGIT = IDIGIT -c$$$ IF (IDIGIT .EQ. 0) NDIGIT = 4 -c$$$C -c$$$C======================================================================= -c$$$C CODE FOR OUTPUT USING 72 COLUMNS FORMAT -c$$$C======================================================================= -c$$$C -c$$$ IF (IDIGIT .LT. 0) THEN -c$$$C -c$$$ NDIGIT = -IDIGIT -c$$$ IF (NDIGIT .LE. 4) THEN -c$$$ DO 10 K1 = 1, N, 10 -c$$$ K2 = MIN0(N,K1+9) -c$$$ WRITE(LOUT,1000) K1,K2,(IX(I),I=K1,K2) -c$$$ 10 CONTINUE -c$$$C -c$$$ ELSE IF (NDIGIT .LE. 6) THEN -c$$$ DO 30 K1 = 1, N, 7 -c$$$ K2 = MIN0(N,K1+6) -c$$$ WRITE(LOUT,1001) K1,K2,(IX(I),I=K1,K2) -c$$$ 30 CONTINUE -c$$$C -c$$$ ELSE IF (NDIGIT .LE. 10) THEN -c$$$ DO 50 K1 = 1, N, 5 -c$$$ K2 = MIN0(N,K1+4) -c$$$ WRITE(LOUT,1002) K1,K2,(IX(I),I=K1,K2) -c$$$ 50 CONTINUE -c$$$C -c$$$ ELSE -c$$$ DO 70 K1 = 1, N, 3 -c$$$ K2 = MIN0(N,K1+2) -c$$$ WRITE(LOUT,1003) K1,K2,(IX(I),I=K1,K2) -c$$$ 70 CONTINUE -c$$$ END IF -c$$$C -c$$$C======================================================================= -c$$$C CODE FOR OUTPUT USING 132 COLUMNS FORMAT -c$$$C======================================================================= -c$$$C -c$$$ ELSE -c$$$C -c$$$ IF (NDIGIT .LE. 4) THEN -c$$$ DO 90 K1 = 1, N, 20 -c$$$ K2 = MIN0(N,K1+19) -c$$$ WRITE(LOUT,1000) K1,K2,(IX(I),I=K1,K2) -c$$$ 90 CONTINUE -c$$$C -c$$$ ELSE IF (NDIGIT .LE. 6) THEN -c$$$ DO 110 K1 = 1, N, 15 -c$$$ K2 = MIN0(N,K1+14) -c$$$ WRITE(LOUT,1001) K1,K2,(IX(I),I=K1,K2) -c$$$ 110 CONTINUE -c$$$C -c$$$ ELSE IF (NDIGIT .LE. 10) THEN -c$$$ DO 130 K1 = 1, N, 10 -c$$$ K2 = MIN0(N,K1+9) -c$$$ WRITE(LOUT,1002) K1,K2,(IX(I),I=K1,K2) -c$$$ 130 CONTINUE -c$$$C -c$$$ ELSE -c$$$ DO 150 K1 = 1, N, 7 -c$$$ K2 = MIN0(N,K1+6) -c$$$ WRITE(LOUT,1003) K1,K2,(IX(I),I=K1,K2) -c$$$ 150 CONTINUE -c$$$ END IF -c$$$ END IF -c$$$ WRITE (LOUT,1004) -c$$$C -c$$$ 1000 FORMAT(1X,I4,' - ',I4,':',20(1X,I5)) -c$$$ 1001 FORMAT(1X,I4,' - ',I4,':',15(1X,I7)) -c$$$ 1002 FORMAT(1X,I4,' - ',I4,':',10(1X,I11)) -c$$$ 1003 FORMAT(1X,I4,' - ',I4,':',7(1X,I15)) -c$$$ 1004 FORMAT(1X,' ') -c$$$C - RETURN - END diff --git a/src/rigraph/vendor/arpack/second.f b/src/rigraph/vendor/arpack/second.f deleted file mode 100644 index 37023c3..0000000 --- a/src/rigraph/vendor/arpack/second.f +++ /dev/null @@ -1,35 +0,0 @@ - SUBROUTINE IGRAPHSECOND( T ) -* - REAL T -* -* -- 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 igraphseconds. -* This version gets the time from the system function ETIME. -* -* .. Local Scalars .. - REAL T1 -* .. -* .. Local Arrays .. - REAL TARRAY( 2 ) -* .. -* .. External Functions .. - REAL ETIME -* .. -* .. Executable Statements .. -* - TARRAY( 1 ) = 0.0 - T1 = ETIME( TARRAY ) - T = TARRAY( 1 ) - - RETURN -* -* End of SECOND -* - END diff --git a/src/rigraph/vendor/arpack/stat.h b/src/rigraph/vendor/arpack/stat.h deleted file mode 100644 index ae407cb..0000000 --- a/src/rigraph/vendor/arpack/stat.h +++ /dev/null @@ -1,21 +0,0 @@ -c %--------------------------------% -c | See stat.doc for documentation | -c %--------------------------------% -c -c\SCCS Information: @(#) -c FILE: stat.h SID: 2.2 DATE OF SID: 11/16/95 RELEASE: 2 -c - real t0, t1, t2, t3, t4, t5 -c save t0, t1, t2, t3, t4, t5 -c - integer nopx, nbx, nrorth, nitref, nrstrt - real tsaupd, tsaup2, tsaitr, tseigt, tsgets, tsapps, tsconv, - & tnaupd, tnaup2, tnaitr, tneigh, tngets, tnapps, tnconv, - & tcaupd, tcaup2, tcaitr, tceigh, tcgets, tcapps, tcconv, - & tmvopx, tmvbx, tgetv0, titref, trvec - common /timing/ - & nopx, nbx, nrorth, nitref, nrstrt, - & tsaupd, tsaup2, tsaitr, tseigt, tsgets, tsapps, tsconv, - & tnaupd, tnaup2, tnaitr, tneigh, tngets, tnapps, tnconv, - & tcaupd, tcaup2, tcaitr, tceigh, tcgets, tcapps, tcconv, - & tmvopx, tmvbx, tgetv0, titref, trvec diff --git a/src/rigraph/vendor/arpack/wrap.f b/src/rigraph/vendor/arpack/wrap.f deleted file mode 100644 index 39c4720..0000000 --- a/src/rigraph/vendor/arpack/wrap.f +++ /dev/null @@ -1,151 +0,0 @@ -c----------------------------------------------------------------------- -c Wrapper functions, so we don't need to pass logicals from -c C to Fortran, because that generates LTO warnings, as the compiler -c apparently cannot match a Fortran logical to a C type. -c----------------------------------------------------------------------- -c - subroutine igraphxdsortr (which, apply, n, x1, x2) -c - character*2 which - integer apply - integer n - Double precision - & x1(0:n-1), x2(0:n-1) -c - logical applyx -c - if (apply .eq. 1) then - applyx = .true. - else - applyx = .false. - end if -c - call igraphdsortr(which, applyx, n, x1, x2) -c - return -c - end -c -c----------------------------------------------------------------------- -c - subroutine igraphxdsortc (which, apply, n, xreal, ximag, y) -c - character*2 which - integer apply - integer n -c - Double precision - & xreal(0:n-1), ximag(0:n-1), y(0:n-1) -c - logical applyx -c - if (apply .eq. 1) then - applyx = .true. - else - applyx = .false. - end if -c - call igraphdsortc(which, applyx, n, xreal, ximag, y) -c - return -c - end -c -c----------------------------------------------------------------------- -c - subroutine igraphxdneupd (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) -c - character bmat, howmny, which*2 - integer rvec - integer info, ldz, ldv, lworkl, n, ncv, nev - Double precision - & sigmar, sigmai, tol -c - integer iparam(11), ipntr(14) - integer select(ncv) - Double precision - & dr(nev+1), di(nev+1), resid(n), v(ldv,ncv), z(ldz,*), - & workd(3*n), workl(lworkl), workev(3*ncv) -c - logical rvecx - integer i - logical selectx(ncv) -c - if (rvec .eq. 1) then - rvecx = .true. - else - rvecx = .false. - end if -c - i = 1 - 100 if (i .gt. ncv) then - go to 110 - end if - if (select(i) .eq. 1) then - selectx(i) = .true. - else - selectx(i) = .false. - end if - i = i + 1 - go to 100 - 110 continue -c - call igraphdneupd(rvecx, howmny, selectx, dr, di, z, ldz, - & sigmar, sigmai, workev, bmat, n, which, nev, tol, - & resid, ncv, v, ldv, iparam, ipntr, workd, - & workl, lworkl, info) -c - return -c - end -c -c----------------------------------------------------------------------- -c - subroutine igraphxdseupd (rvec, howmny, select, d, z, ldz, - & sigma, bmat, n, which, nev, tol, resid, ncv, v, ldv, iparam, - & ipntr, workd, workl, lworkl, info ) -c - character bmat, howmny, which*2 - integer rvec, select(ncv) - integer info, ldz, ldv, lworkl, n, ncv, nev - Double precision - & sigma, tol -c - integer iparam(7), ipntr(11) - Double precision - & d(nev), resid(n), v(ldv,ncv), z(ldz, nev), - & workd(2*n), workl(lworkl) -c - logical rvecx - integer i - logical selectx(ncv) -c - if (rvec .eq. 1) then - rvecx = .true. - else - rvecx = .false. - end if -c - i = 1 - 100 if (i .gt. ncv) then - go to 110 - end if - if (select(i) .eq. 1) then - selectx(i) = .true. - else - selectx(i) = .false. - end if - i = i + 1 - go to 100 - 110 continue -c - call igraphdseupd(rvecx, howmny, selectx, d, z, ldz, - & sigma, bmat, n, which, nev, tol, resid, ncv, v, ldv, iparam, - & ipntr, workd, workl, lworkl, info ) -c - return -c - end diff --git a/src/rigraph/vendor/cs/SuiteSparse_config.h b/src/rigraph/vendor/cs/SuiteSparse_config.h deleted file mode 100644 index bd0cced..0000000 --- a/src/rigraph/vendor/cs/SuiteSparse_config.h +++ /dev/null @@ -1,221 +0,0 @@ -/* ========================================================================== */ -/* === SuiteSparse_config =================================================== */ -/* ========================================================================== */ - -/* Configuration file for SuiteSparse: a Suite of Sparse matrix packages - * (AMD, COLAMD, CCOLAMD, CAMD, CHOLMOD, UMFPACK, CXSparse, and others). - * - * SuiteSparse_config.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 SuiteSparse_config.mk: - * - * CFLAGS = -O -D'SuiteSparse_long=long long' \ - * -D'SuiteSparse_long_max=9223372036854775801' -D'SuiteSparse_long_idd="lld"' - * - * This file defines SuiteSparse_long as either long (on all but _WIN64) or - * __int64 on Windows 64. The intent is that a SuiteSparse_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) 2012, Timothy A. Davis. No licensing restrictions apply - * to this file or to the SuiteSparse_config directory. - * Author: Timothy A. Davis. - */ - -#ifndef SUITESPARSE_CONFIG_H -#define SUITESPARSE_CONFIG_H - -#ifdef __cplusplus -extern "C" { -#endif - -#include -#include - -/* ========================================================================== */ -/* === SuiteSparse_long ===================================================== */ -/* ========================================================================== */ - -#ifndef SuiteSparse_long - -#ifdef _WIN64 - -#define SuiteSparse_long __int64 -#define SuiteSparse_long_max _I64_MAX -#define SuiteSparse_long_idd "I64d" - -#else - -#define SuiteSparse_long long -#define SuiteSparse_long_max LONG_MAX -#define SuiteSparse_long_idd "ld" - -#endif -#define SuiteSparse_long_id "%" SuiteSparse_long_idd -#endif - -/* Disable unneeded parts for igraph */ -#if 0 /* start comment */ - -/* ========================================================================== */ -/* === SuiteSparse_config parameters and functions ========================== */ -/* ========================================================================== */ - -/* SuiteSparse-wide parameters are placed in this struct. It is meant to be - an extern, globally-accessible struct. It is not meant to be updated - frequently by multiple threads. Rather, if an application needs to modify - SuiteSparse_config, it should do it once at the beginning of the application, - before multiple threads are launched. - - The intent of these function pointers is that they not be used in your - application directly, except to assign them to the desired user-provided - functions. Rather, you should use the - */ - -struct SuiteSparse_config_struct -{ - void *(*malloc_func) (size_t) ; /* pointer to malloc */ - void *(*calloc_func) (size_t, size_t) ; /* pointer to calloc */ - void *(*realloc_func) (void *, size_t) ; /* pointer to realloc */ - void (*free_func) (void *) ; /* pointer to free */ - int (*printf_func) (const char *, ...) ; /* pointer to printf */ - double (*hypot_func) (double, double) ; /* pointer to hypot */ - int (*divcomplex_func) (double, double, double, double, double *, double *); -} ; - -extern struct SuiteSparse_config_struct SuiteSparse_config ; - -void SuiteSparse_start ( void ) ; /* called to start SuiteSparse */ - -void SuiteSparse_finish ( void ) ; /* called to finish SuiteSparse */ - -void *SuiteSparse_malloc /* pointer to allocated block of memory */ -( - size_t nitems, /* number of items to malloc (>=1 is enforced) */ - size_t size_of_item /* sizeof each item */ -) ; - -void *SuiteSparse_calloc /* pointer to allocated block of memory */ -( - size_t nitems, /* number of items to calloc (>=1 is enforced) */ - size_t size_of_item /* sizeof each item */ -) ; - -void *SuiteSparse_realloc /* pointer to reallocated block of memory, or - to original block if the realloc failed. */ -( - size_t nitems_new, /* new number of items in the object */ - size_t nitems_old, /* old number of items in the object */ - size_t size_of_item, /* sizeof each item */ - void *p, /* old object to reallocate */ - int *ok /* 1 if successful, 0 otherwise */ -) ; - -void *SuiteSparse_free /* always returns NULL */ -( - void *p /* block to free */ -) ; - -void SuiteSparse_tic /* start the timer */ -( - double tic [2] /* output, contents undefined on input */ -) ; - -double SuiteSparse_toc /* return time in seconds since last tic */ -( - double tic [2] /* input: from last call to SuiteSparse_tic */ -) ; - -double SuiteSparse_time /* returns current wall clock time in seconds */ -( - void -) ; - -/* returns sqrt (x^2 + y^2), computed reliably */ -double SuiteSparse_hypot (double x, double y) ; - -/* complex division of c = a/b */ -int SuiteSparse_divcomplex -( - double ar, double ai, /* real and imaginary parts of a */ - double br, double bi, /* real and imaginary parts of b */ - double *cr, double *ci /* real and imaginary parts of c */ -) ; - -/* determine which timer to use, if any */ -#ifndef NTIMER -#ifdef _POSIX_C_SOURCE -#if _POSIX_C_SOURCE >= 199309L -#define SUITESPARSE_TIMER_ENABLED -#endif -#endif -#endif - -/* SuiteSparse printf macro */ -#define SUITESPARSE_PRINTF(params) \ -{ \ - if (SuiteSparse_config.printf_func != NULL) \ - { \ - (void) (SuiteSparse_config.printf_func) params ; \ - } \ -} - -/* ========================================================================== */ -/* === 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, which is also the version number of SuiteSparse_config. - */ - -int SuiteSparse_version /* returns SUITESPARSE_VERSION */ -( - /* output, not defined on input. Not used if NULL. Returns - the three version codes in version [0..2]: - version [0] is SUITESPARSE_MAIN_VERSION - version [1] is SUITESPARSE_SUB_VERSION - version [2] is SUITESPARSE_SUBSUB_VERSION - */ - int version [3] -) ; - -/* Versions prior to 4.2.0 do not have the above function. The following - code fragment will work with any version of SuiteSparse: - - #ifdef SUITESPARSE_HAS_VERSION_FUNCTION - v = SuiteSparse_version (NULL) ; - #else - v = SUITESPARSE_VERSION ; - #endif -*/ -#define SUITESPARSE_HAS_VERSION_FUNCTION - -#endif /* end comment */ - -#define SUITESPARSE_DATE "Mar 3, 2021" -#define SUITESPARSE_VER_CODE(main,sub) ((main) * 1000 + (sub)) -#define SUITESPARSE_MAIN_VERSION 5 -#define SUITESPARSE_SUB_VERSION 9 -#define SUITESPARSE_SUBSUB_VERSION 0 -#define SUITESPARSE_VERSION \ - SUITESPARSE_VER_CODE(SUITESPARSE_MAIN_VERSION,SUITESPARSE_SUB_VERSION) - -#ifdef __cplusplus -} -#endif -#endif diff --git a/src/rigraph/vendor/cs/cs.h b/src/rigraph/vendor/cs/cs.h deleted file mode 100644 index 0e58521..0000000 --- a/src/rigraph/vendor/cs/cs.h +++ /dev/null @@ -1,758 +0,0 @@ -/* ========================================================================== */ -/* CXSparse/Include/cs.h file */ -/* ========================================================================== */ - -/* This is the CXSparse/Include/cs.h file. It has the same name (cs.h) as - the CSparse/Include/cs.h file. The 'make install' for SuiteSparse installs - CXSparse, and this file, instead of CSparse. The two packages have the same - cs.h include filename, because CXSparse is a superset of CSparse. Any user - program that uses CSparse can rely on CXSparse instead, with no change to the - user code. The #include "cs.h" line will work for both versions, in user - code, and the function names and user-visible typedefs from CSparse all - appear in CXSparse. For experimenting and changing the package itself, I - recommend using CSparse since it's simpler and easier to modify. For - using the package in production codes, I recommend CXSparse since it has - more features (support for complex matrices, and both int and long - versions). - */ - -/* ========================================================================== */ - -#ifndef _CXS_H -#define _CXS_H -#include -#include -#include -#include -#ifdef MATLAB_MEX_FILE -#include "mex.h" -#endif - - -#ifdef __cplusplus -#ifndef NCOMPLEX -#include -typedef std::complex cs_complex_t ; -#endif -extern "C" { -#else -#ifndef NCOMPLEX -#include -#define cs_complex_t double _Complex -#endif -#endif - -#define CS_VER 3 /* CXSparse Version */ -#define CS_SUBVER 2 -#define CS_SUBSUB 0 -#define CS_DATE "Sept 12, 2017" /* CSparse release date */ -#define CS_COPYRIGHT "Copyright (c) Timothy A. Davis, 2006-2016" -#define CXSPARSE - -#include "SuiteSparse_config.h" -#define cs_long_t SuiteSparse_long -#define cs_long_t_id SuiteSparse_long_id -#define cs_long_t_max SuiteSparse_long_max - -/* -------------------------------------------------------------------------- */ -/* 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/cs_long_t version of CXSparse */ -/* -------------------------------------------------------------------------- */ - -/* --- primary CSparse routines and data structures ------------------------- */ - -typedef struct cs_dl_sparse /* matrix in compressed-column or triplet form */ -{ - cs_long_t nzmax ; /* maximum number of entries */ - cs_long_t m ; /* number of rows */ - cs_long_t n ; /* number of columns */ - cs_long_t *p ; /* column pointers (size n+1) or col indlces (size nzmax) */ - cs_long_t *i ; /* row indices, size nzmax */ - double *x ; /* numerical values, size nzmax */ - cs_long_t 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) ; -cs_long_t cs_dl_cholsol (cs_long_t order, const cs_dl *A, double *b) ; -cs_long_t cs_dl_dupl (cs_dl *A) ; -cs_long_t cs_dl_entry (cs_dl *T, cs_long_t i, cs_long_t j, double x) ; -cs_long_t cs_dl_lusol (cs_long_t order, const cs_dl *A, double *b, double tol) ; -cs_long_t 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) ; -cs_long_t cs_dl_qrsol (cs_long_t order, const cs_dl *A, double *b) ; -cs_dl *cs_dl_transpose (const cs_dl *A, cs_long_t values) ; -cs_dl *cs_dl_compress (const cs_dl *T) ; -double cs_dl_norm (const cs_dl *A) ; -/*cs_long_t cs_dl_print (const cs_dl *A, cs_long_t brief) ;*/ -cs_dl *cs_dl_load (FILE *f) ; - -/* utilities */ -void *cs_dl_calloc (cs_long_t n, size_t size) ; -void *cs_dl_free (void *p) ; -void *cs_dl_realloc (void *p, cs_long_t n, size_t size, cs_long_t *ok) ; -cs_dl *cs_dl_spalloc (cs_long_t m, cs_long_t n, cs_long_t nzmax, cs_long_t values, - cs_long_t t) ; -cs_dl *cs_dl_spfree (cs_dl *A) ; -cs_long_t cs_dl_sprealloc (cs_dl *A, cs_long_t nzmax) ; -void *cs_dl_malloc (cs_long_t n, size_t size) ; - -/* --- secondary CSparse routines and data structures ----------------------- */ - -typedef struct cs_dl_symbolic /* symbolic Cholesky, LU, or QR analysis */ -{ - cs_long_t *pinv ; /* inverse row perm. for QR, fill red. perm for Chol */ - cs_long_t *q ; /* fill-reducing column permutation for LU and QR */ - cs_long_t *parent ; /* elimination tree for Cholesky and QR */ - cs_long_t *cp ; /* column pointers for Cholesky, row counts for QR */ - cs_long_t *leftmost ; /* leftmost[i] = min(find(A(i,:))), for QR */ - cs_long_t 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 */ - cs_long_t *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 */ -{ - cs_long_t *p ; /* size m, row permutation */ - cs_long_t *q ; /* size n, column permutation */ - cs_long_t *r ; /* size nb+1, block k is rows r[k] to r[k+1]-1 in A(p,q) */ - cs_long_t *s ; /* size nb+1, block k is cols s[k] to s[k+1]-1 in A(p,q) */ - cs_long_t nb ; /* # of blocks in fine dmperm decomposition */ - cs_long_t rr [5] ; /* coarse row decomposition */ - cs_long_t cc [5] ; /* coarse column decomposition */ -} cs_dld ; - -cs_long_t *cs_dl_amd (cs_long_t 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, cs_long_t seed) ; -cs_long_t cs_dl_droptol (cs_dl *A, double tol) ; -cs_long_t cs_dl_dropzeros (cs_dl *A) ; -cs_long_t cs_dl_happly (const cs_dl *V, cs_long_t i, double beta, double *x) ; -cs_long_t cs_dl_ipvec (const cs_long_t *p, const double *b, double *x, cs_long_t n) ; -cs_long_t cs_dl_lsolve (const cs_dl *L, double *x) ; -cs_long_t 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 cs_long_t *pinv, const cs_long_t *q, - cs_long_t values) ; -cs_long_t *cs_dl_pinv (const cs_long_t *p, cs_long_t n) ; -cs_long_t cs_dl_pvec (const cs_long_t *p, const double *b, double *x, cs_long_t n) ; -cs_dln *cs_dl_qr (const cs_dl *A, const cs_dls *S) ; -cs_dls *cs_dl_schol (cs_long_t order, const cs_dl *A) ; -cs_dls *cs_dl_sqr (cs_long_t order, const cs_dl *A, cs_long_t qr) ; -cs_dl *cs_dl_symperm (const cs_dl *A, const cs_long_t *pinv, cs_long_t values) ; -cs_long_t cs_dl_usolve (const cs_dl *U, double *x) ; -cs_long_t cs_dl_utsolve (const cs_dl *U, double *x) ; -cs_long_t cs_dl_updown (cs_dl *L, cs_long_t sigma, const cs_dl *C, - const cs_long_t *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 -------------------------------------------- */ - -cs_long_t *cs_dl_counts (const cs_dl *A, const cs_long_t *parent, - const cs_long_t *post, cs_long_t ata) ; -double cs_dl_cumsum (cs_long_t *p, cs_long_t *c, cs_long_t n) ; -cs_long_t cs_dl_dfs (cs_long_t j, cs_dl *G, cs_long_t top, cs_long_t *xi, - cs_long_t *pstack, const cs_long_t *pinv) ; -cs_long_t *cs_dl_etree (const cs_dl *A, cs_long_t ata) ; -cs_long_t cs_dl_fkeep (cs_dl *A, - cs_long_t (*fkeep) (cs_long_t, cs_long_t, double, void *), void *other) ; -double cs_dl_house (double *x, double *beta, cs_long_t n) ; -cs_long_t *cs_dl_maxtrans (const cs_dl *A, cs_long_t seed) ; -cs_long_t *cs_dl_post (const cs_long_t *parent, cs_long_t n) ; -cs_dld *cs_dl_scc (cs_dl *A) ; -cs_long_t cs_dl_scatter (const cs_dl *A, cs_long_t j, double beta, cs_long_t *w, - double *x, cs_long_t mark,cs_dl *C, cs_long_t nz) ; -cs_long_t cs_dl_tdfs (cs_long_t j, cs_long_t k, cs_long_t *head, const cs_long_t *next, - cs_long_t *post, cs_long_t *stack) ; -cs_long_t cs_dl_leaf (cs_long_t i, cs_long_t j, const cs_long_t *first, - cs_long_t *maxfirst, cs_long_t *prevleaf, cs_long_t *ancestor, cs_long_t *jleaf) ; -cs_long_t cs_dl_reach (cs_dl *G, const cs_dl *B, cs_long_t k, cs_long_t *xi, - const cs_long_t *pinv) ; -cs_long_t cs_dl_spsolve (cs_dl *L, const cs_dl *B, cs_long_t k, cs_long_t *xi, - double *x, const cs_long_t *pinv, cs_long_t lo) ; -cs_long_t cs_dl_ereach (const cs_dl *A, cs_long_t k, const cs_long_t *parent, - cs_long_t *s, cs_long_t *w) ; -cs_long_t *cs_dl_randperm (cs_long_t n, cs_long_t seed) ; - -/* utilities */ -cs_dld *cs_dl_dalloc (cs_long_t m, cs_long_t n) ; -cs_dl *cs_dl_done (cs_dl *C, void *w, void *x, cs_long_t ok) ; -cs_long_t *cs_dl_idone (cs_long_t *p, cs_dl *C, void *w, cs_long_t ok) ; -cs_dln *cs_dl_ndone (cs_dln *N, cs_dl *C, void *w, void *x, cs_long_t ok) ; -cs_dld *cs_dl_ddone (cs_dld *D, cs_dl *C, void *w, cs_long_t 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/cs_long_t version of CXSparse */ -/* -------------------------------------------------------------------------- */ - -/* --- primary CSparse routines and data structures ------------------------- */ - -typedef struct cs_cl_sparse /* matrix in compressed-column or triplet form */ -{ - cs_long_t nzmax ; /* maximum number of entries */ - cs_long_t m ; /* number of rows */ - cs_long_t n ; /* number of columns */ - cs_long_t *p ; /* column pointers (size n+1) or col indlces (size nzmax) */ - cs_long_t *i ; /* row indices, size nzmax */ - cs_complex_t *x ; /* numerical values, size nzmax */ - cs_long_t 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) ; -cs_long_t cs_cl_cholsol (cs_long_t order, const cs_cl *A, cs_complex_t *b) ; -cs_long_t cs_cl_dupl (cs_cl *A) ; -cs_long_t cs_cl_entry (cs_cl *T, cs_long_t i, cs_long_t j, cs_complex_t x) ; -cs_long_t cs_cl_lusol (cs_long_t order, const cs_cl *A, cs_complex_t *b, - double tol) ; -cs_long_t 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) ; -cs_long_t cs_cl_qrsol (cs_long_t order, const cs_cl *A, cs_complex_t *b) ; -cs_cl *cs_cl_transpose (const cs_cl *A, cs_long_t values) ; -cs_cl *cs_cl_compress (const cs_cl *T) ; -double cs_cl_norm (const cs_cl *A) ; -/*cs_long_t cs_cl_print (const cs_cl *A, cs_long_t brief) ;*/ -cs_cl *cs_cl_load (FILE *f) ; - -/* utilities */ -void *cs_cl_calloc (cs_long_t n, size_t size) ; -void *cs_cl_free (void *p) ; -void *cs_cl_realloc (void *p, cs_long_t n, size_t size, cs_long_t *ok) ; -cs_cl *cs_cl_spalloc (cs_long_t m, cs_long_t n, cs_long_t nzmax, cs_long_t values, - cs_long_t t) ; -cs_cl *cs_cl_spfree (cs_cl *A) ; -cs_long_t cs_cl_sprealloc (cs_cl *A, cs_long_t nzmax) ; -void *cs_cl_malloc (cs_long_t n, size_t size) ; - -/* --- secondary CSparse routines and data structures ----------------------- */ - -typedef struct cs_cl_symbolic /* symbolic Cholesky, LU, or QR analysis */ -{ - cs_long_t *pinv ; /* inverse row perm. for QR, fill red. perm for Chol */ - cs_long_t *q ; /* fill-reducing column permutation for LU and QR */ - cs_long_t *parent ; /* elimination tree for Cholesky and QR */ - cs_long_t *cp ; /* column pointers for Cholesky, row counts for QR */ - cs_long_t *leftmost ; /* leftmost[i] = min(find(A(i,:))), for QR */ - cs_long_t 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 */ - cs_long_t *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 */ -{ - cs_long_t *p ; /* size m, row permutation */ - cs_long_t *q ; /* size n, column permutation */ - cs_long_t *r ; /* size nb+1, block k is rows r[k] to r[k+1]-1 in A(p,q) */ - cs_long_t *s ; /* size nb+1, block k is cols s[k] to s[k+1]-1 in A(p,q) */ - cs_long_t nb ; /* # of blocks in fine dmperm decomposition */ - cs_long_t rr [5] ; /* coarse row decomposition */ - cs_long_t cc [5] ; /* coarse column decomposition */ -} cs_cld ; - -cs_long_t *cs_cl_amd (cs_long_t 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, cs_long_t seed) ; -cs_long_t cs_cl_droptol (cs_cl *A, double tol) ; -cs_long_t cs_cl_dropzeros (cs_cl *A) ; -cs_long_t cs_cl_happly (const cs_cl *V, cs_long_t i, double beta, cs_complex_t *x) ; -cs_long_t cs_cl_ipvec (const cs_long_t *p, const cs_complex_t *b, - cs_complex_t *x, cs_long_t n) ; -cs_long_t cs_cl_lsolve (const cs_cl *L, cs_complex_t *x) ; -cs_long_t 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 cs_long_t *pinv, const cs_long_t *q, - cs_long_t values) ; -cs_long_t *cs_cl_pinv (const cs_long_t *p, cs_long_t n) ; -cs_long_t cs_cl_pvec (const cs_long_t *p, const cs_complex_t *b, - cs_complex_t *x, cs_long_t n) ; -cs_cln *cs_cl_qr (const cs_cl *A, const cs_cls *S) ; -cs_cls *cs_cl_schol (cs_long_t order, const cs_cl *A) ; -cs_cls *cs_cl_sqr (cs_long_t order, const cs_cl *A, cs_long_t qr) ; -cs_cl *cs_cl_symperm (const cs_cl *A, const cs_long_t *pinv, cs_long_t values) ; -cs_long_t cs_cl_usolve (const cs_cl *U, cs_complex_t *x) ; -cs_long_t cs_cl_utsolve (const cs_cl *U, cs_complex_t *x) ; -cs_long_t cs_cl_updown (cs_cl *L, cs_long_t sigma, const cs_cl *C, - const cs_long_t *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 -------------------------------------------- */ - -cs_long_t *cs_cl_counts (const cs_cl *A, const cs_long_t *parent, - const cs_long_t *post, cs_long_t ata) ; -double cs_cl_cumsum (cs_long_t *p, cs_long_t *c, cs_long_t n) ; -cs_long_t cs_cl_dfs (cs_long_t j, cs_cl *G, cs_long_t top, cs_long_t *xi, - cs_long_t *pstack, const cs_long_t *pinv) ; -cs_long_t *cs_cl_etree (const cs_cl *A, cs_long_t ata) ; -cs_long_t cs_cl_fkeep (cs_cl *A, - cs_long_t (*fkeep) (cs_long_t, cs_long_t, cs_complex_t, void *), void *other) ; -cs_complex_t cs_cl_house (cs_complex_t *x, double *beta, cs_long_t n) ; -cs_long_t *cs_cl_maxtrans (const cs_cl *A, cs_long_t seed) ; -cs_long_t *cs_cl_post (const cs_long_t *parent, cs_long_t n) ; -cs_cld *cs_cl_scc (cs_cl *A) ; -cs_long_t cs_cl_scatter (const cs_cl *A, cs_long_t j, cs_complex_t beta, - cs_long_t *w, cs_complex_t *x, cs_long_t mark,cs_cl *C, cs_long_t nz) ; -cs_long_t cs_cl_tdfs (cs_long_t j, cs_long_t k, cs_long_t *head, const cs_long_t *next, - cs_long_t *post, cs_long_t *stack) ; -cs_long_t cs_cl_leaf (cs_long_t i, cs_long_t j, const cs_long_t *first, - cs_long_t *maxfirst, cs_long_t *prevleaf, cs_long_t *ancestor, cs_long_t *jleaf) ; -cs_long_t cs_cl_reach (cs_cl *G, const cs_cl *B, cs_long_t k, cs_long_t *xi, - const cs_long_t *pinv) ; -cs_long_t cs_cl_spsolve (cs_cl *L, const cs_cl *B, cs_long_t k, cs_long_t *xi, - cs_complex_t *x, const cs_long_t *pinv, cs_long_t lo) ; -cs_long_t cs_cl_ereach (const cs_cl *A, cs_long_t k, const cs_long_t *parent, - cs_long_t *s, cs_long_t *w) ; -cs_long_t *cs_cl_randperm (cs_long_t n, cs_long_t seed) ; - -/* utilities */ -cs_cld *cs_cl_dalloc (cs_long_t m, cs_long_t n) ; -cs_cl *cs_cl_done (cs_cl *C, void *w, void *x, cs_long_t ok) ; -cs_long_t *cs_cl_idone (cs_long_t *p, cs_cl *C, void *w, cs_long_t ok) ; -cs_cln *cs_cl_ndone (cs_cln *N, cs_cl *C, void *w, void *x, cs_long_t ok) ; -cs_cld *cs_cl_ddone (cs_cld *D, cs_cl *C, void *w, cs_long_t ok) ; - -#endif - -/* -------------------------------------------------------------------------- */ -/* Macros for constructing each version of CSparse */ -/* -------------------------------------------------------------------------- */ - -#ifdef CS_LONG -#define CS_INT cs_long_t -#define CS_INT_MAX cs_long_t_max -#define CS_ID cs_long_t_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, cs_long_t real) ; -cs_cl *cs_l_complex (cs_dl *A, cs_long_t real) ; -#endif - -#ifdef __cplusplus -} -#endif -#endif diff --git a/src/rigraph/vendor/cs/cs_add.c b/src/rigraph/vendor/cs/cs_add.c deleted file mode 100644 index 44b0d3f..0000000 --- a/src/rigraph/vendor/cs/cs_add.c +++ /dev/null @@ -1,28 +0,0 @@ -#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/src/rigraph/vendor/cs/cs_amd.c b/src/rigraph/vendor/cs/cs_amd.c deleted file mode 100644 index 3f5c702..0000000 --- a/src/rigraph/vendor/cs/cs_amd.c +++ /dev/null @@ -1,364 +0,0 @@ -#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 ; - 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 = ((h<0) ? (-h):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/src/rigraph/vendor/cs/cs_chol.c b/src/rigraph/vendor/cs/cs_chol.c deleted file mode 100644 index 535809a..0000000 --- a/src/rigraph/vendor/cs/cs_chol.c +++ /dev/null @@ -1,59 +0,0 @@ -#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/src/rigraph/vendor/cs/cs_cholsol.c b/src/rigraph/vendor/cs/cs_cholsol.c deleted file mode 100644 index 6e7dc4f..0000000 --- a/src/rigraph/vendor/cs/cs_cholsol.c +++ /dev/null @@ -1,26 +0,0 @@ -#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/src/rigraph/vendor/cs/cs_compress.c b/src/rigraph/vendor/cs/cs_compress.c deleted file mode 100644 index dc62eba..0000000 --- a/src/rigraph/vendor/cs/cs_compress.c +++ /dev/null @@ -1,22 +0,0 @@ -#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/src/rigraph/vendor/cs/cs_counts.c b/src/rigraph/vendor/cs/cs_counts.c deleted file mode 100644 index a1b3de0..0000000 --- a/src/rigraph/vendor/cs/cs_counts.c +++ /dev/null @@ -1,61 +0,0 @@ -#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/src/rigraph/vendor/cs/cs_cumsum.c b/src/rigraph/vendor/cs/cs_cumsum.c deleted file mode 100644 index e839497..0000000 --- a/src/rigraph/vendor/cs/cs_cumsum.c +++ /dev/null @@ -1,17 +0,0 @@ -#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/src/rigraph/vendor/cs/cs_dfs.c b/src/rigraph/vendor/cs/cs_dfs.c deleted file mode 100644 index 6c7115d..0000000 --- a/src/rigraph/vendor/cs/cs_dfs.c +++ /dev/null @@ -1,36 +0,0 @@ -#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/src/rigraph/vendor/cs/cs_dmperm.c b/src/rigraph/vendor/cs/cs_dmperm.c deleted file mode 100644 index e213845..0000000 --- a/src/rigraph/vendor/cs/cs_dmperm.c +++ /dev/null @@ -1,144 +0,0 @@ -#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/src/rigraph/vendor/cs/cs_droptol.c b/src/rigraph/vendor/cs/cs_droptol.c deleted file mode 100644 index 59b8df2..0000000 --- a/src/rigraph/vendor/cs/cs_droptol.c +++ /dev/null @@ -1,9 +0,0 @@ -#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/src/rigraph/vendor/cs/cs_dropzeros.c b/src/rigraph/vendor/cs/cs_dropzeros.c deleted file mode 100644 index d93f605..0000000 --- a/src/rigraph/vendor/cs/cs_dropzeros.c +++ /dev/null @@ -1,9 +0,0 @@ -#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/src/rigraph/vendor/cs/cs_dupl.c b/src/rigraph/vendor/cs/cs_dupl.c deleted file mode 100644 index fdf2e1e..0000000 --- a/src/rigraph/vendor/cs/cs_dupl.c +++ /dev/null @@ -1,34 +0,0 @@ -#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/src/rigraph/vendor/cs/cs_entry.c b/src/rigraph/vendor/cs/cs_entry.c deleted file mode 100644 index f712ba7..0000000 --- a/src/rigraph/vendor/cs/cs_entry.c +++ /dev/null @@ -1,13 +0,0 @@ -#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/src/rigraph/vendor/cs/cs_ereach.c b/src/rigraph/vendor/cs/cs_ereach.c deleted file mode 100644 index 9edad52..0000000 --- a/src/rigraph/vendor/cs/cs_ereach.c +++ /dev/null @@ -1,23 +0,0 @@ -#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/src/rigraph/vendor/cs/cs_etree.c b/src/rigraph/vendor/cs/cs_etree.c deleted file mode 100644 index 5620928..0000000 --- a/src/rigraph/vendor/cs/cs_etree.c +++ /dev/null @@ -1,30 +0,0 @@ -#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/src/rigraph/vendor/cs/cs_fkeep.c b/src/rigraph/vendor/cs/cs_fkeep.c deleted file mode 100644 index 09219e8..0000000 --- a/src/rigraph/vendor/cs/cs_fkeep.c +++ /dev/null @@ -1,25 +0,0 @@ -#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/src/rigraph/vendor/cs/cs_gaxpy.c b/src/rigraph/vendor/cs/cs_gaxpy.c deleted file mode 100644 index db93cbc..0000000 --- a/src/rigraph/vendor/cs/cs_gaxpy.c +++ /dev/null @@ -1,17 +0,0 @@ -#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/src/rigraph/vendor/cs/cs_happly.c b/src/rigraph/vendor/cs/cs_happly.c deleted file mode 100644 index 98a306c..0000000 --- a/src/rigraph/vendor/cs/cs_happly.c +++ /dev/null @@ -1,19 +0,0 @@ -#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/src/rigraph/vendor/cs/cs_house.c b/src/rigraph/vendor/cs/cs_house.c deleted file mode 100644 index e825c89..0000000 --- a/src/rigraph/vendor/cs/cs_house.c +++ /dev/null @@ -1,30 +0,0 @@ -#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/src/rigraph/vendor/cs/cs_ipvec.c b/src/rigraph/vendor/cs/cs_ipvec.c deleted file mode 100644 index 4935ace..0000000 --- a/src/rigraph/vendor/cs/cs_ipvec.c +++ /dev/null @@ -1,9 +0,0 @@ -#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/src/rigraph/vendor/cs/cs_leaf.c b/src/rigraph/vendor/cs/cs_leaf.c deleted file mode 100644 index bd93bda..0000000 --- a/src/rigraph/vendor/cs/cs_leaf.c +++ /dev/null @@ -1,22 +0,0 @@ -#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/src/rigraph/vendor/cs/cs_load.c b/src/rigraph/vendor/cs/cs_load.c deleted file mode 100644 index 91e1f37..0000000 --- a/src/rigraph/vendor/cs/cs_load.c +++ /dev/null @@ -1,26 +0,0 @@ -#include "cs.h" -/* load a triplet matrix from a file */ -cs *cs_load (FILE *f) -{ - double i, j ; /* use double for integers to avoid csi conflicts */ - 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, "%lg %lg %lg %lg\n", &i, &j, &x, &xi) == 4) -#else - while (fscanf (f, "%lg %lg %lg\n", &i, &j, &x) == 3) -#endif - { -#ifdef CS_COMPLEX - if (!cs_entry (T, (CS_INT) i, (CS_INT) j, x + xi*I)) return (cs_spfree (T)) ; -#else - if (!cs_entry (T, (CS_INT) i, (CS_INT) j, x)) return (cs_spfree (T)) ; -#endif - } - return (T) ; -} diff --git a/src/rigraph/vendor/cs/cs_lsolve.c b/src/rigraph/vendor/cs/cs_lsolve.c deleted file mode 100644 index 099b0c5..0000000 --- a/src/rigraph/vendor/cs/cs_lsolve.c +++ /dev/null @@ -1,18 +0,0 @@ -#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/src/rigraph/vendor/cs/cs_ltsolve.c b/src/rigraph/vendor/cs/cs_ltsolve.c deleted file mode 100644 index 29b1ca2..0000000 --- a/src/rigraph/vendor/cs/cs_ltsolve.c +++ /dev/null @@ -1,18 +0,0 @@ -#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/src/rigraph/vendor/cs/cs_lu.c b/src/rigraph/vendor/cs/cs_lu.c deleted file mode 100644 index 270172c..0000000 --- a/src/rigraph/vendor/cs/cs_lu.c +++ /dev/null @@ -1,88 +0,0 @@ -#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)) ; - /* tol=1 for partial pivoting; tol<1 gives preference to diagonal */ - 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/src/rigraph/vendor/cs/cs_lusol.c b/src/rigraph/vendor/cs/cs_lusol.c deleted file mode 100644 index e0727e2..0000000 --- a/src/rigraph/vendor/cs/cs_lusol.c +++ /dev/null @@ -1,26 +0,0 @@ -#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/src/rigraph/vendor/cs/cs_malloc.c b/src/rigraph/vendor/cs/cs_malloc.c deleted file mode 100644 index 2a3f6da..0000000 --- a/src/rigraph/vendor/cs/cs_malloc.c +++ /dev/null @@ -1,35 +0,0 @@ -#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/src/rigraph/vendor/cs/cs_maxtrans.c b/src/rigraph/vendor/cs/cs_maxtrans.c deleted file mode 100644 index 4947cee..0000000 --- a/src/rigraph/vendor/cs/cs_maxtrans.c +++ /dev/null @@ -1,92 +0,0 @@ -#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/src/rigraph/vendor/cs/cs_multiply.c b/src/rigraph/vendor/cs/cs_multiply.c deleted file mode 100644 index 34e3a36..0000000 --- a/src/rigraph/vendor/cs/cs_multiply.c +++ /dev/null @@ -1,35 +0,0 @@ -#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/src/rigraph/vendor/cs/cs_norm.c b/src/rigraph/vendor/cs/cs_norm.c deleted file mode 100644 index 0e7b3c6..0000000 --- a/src/rigraph/vendor/cs/cs_norm.c +++ /dev/null @@ -1,16 +0,0 @@ -#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/src/rigraph/vendor/cs/cs_permute.c b/src/rigraph/vendor/cs/cs_permute.c deleted file mode 100644 index 9adae45..0000000 --- a/src/rigraph/vendor/cs/cs_permute.c +++ /dev/null @@ -1,25 +0,0 @@ -#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/src/rigraph/vendor/cs/cs_pinv.c b/src/rigraph/vendor/cs/cs_pinv.c deleted file mode 100644 index de0660e..0000000 --- a/src/rigraph/vendor/cs/cs_pinv.c +++ /dev/null @@ -1,11 +0,0 @@ -#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/src/rigraph/vendor/cs/cs_post.c b/src/rigraph/vendor/cs/cs_post.c deleted file mode 100644 index 0f61203..0000000 --- a/src/rigraph/vendor/cs/cs_post.c +++ /dev/null @@ -1,24 +0,0 @@ -#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/src/rigraph/vendor/cs/cs_print.c b/src/rigraph/vendor/cs/cs_print.c deleted file mode 100644 index 7e7b16d..0000000 --- a/src/rigraph/vendor/cs/cs_print.c +++ /dev/null @@ -1,55 +0,0 @@ -#include "cs.h" -/* print a sparse matrix; use %g for integers to avoid differences with CS_INT */ - -/* Disabled for igraph as it prints to stdio */ -#if 0 -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 ("%g-by-%g, nzmax: %g nnz: %g, 1-norm: %g\n", (double) m, - (double) n, (double) nzmax, (double) (Ap [n]), cs_norm (A)) ; - for (j = 0 ; j < n ; j++) - { - printf (" col %g : locations %g to %g\n", (double) j, - (double) (Ap [j]), (double) (Ap [j+1]-1)) ; - for (p = Ap [j] ; p < Ap [j+1] ; p++) - { - printf (" %g : ", (double) (Ai [p])) ; -#ifdef CS_COMPLEX - printf ("(%g, %g)\n", - Ax ? CS_REAL (Ax [p]) : 1, Ax ? CS_IMAG (Ax [p]) : 0) ; -#else - printf ("%g\n", Ax ? Ax [p] : 1) ; -#endif - if (brief && p > 20) { printf (" ...\n") ; return (1) ; } - } - } - } - else - { - printf ("triplet: %g-by-%g, nzmax: %g nnz: %g\n", (double) m, - (double) n, (double) nzmax, (double) nz) ; - for (p = 0 ; p < nz ; p++) - { - - printf (" %g %g : ", (double) (Ai [p]), (double) (Ap [p])) ; -#ifdef CS_COMPLEX - printf ("(%g, %g)\n", - Ax ? CS_REAL (Ax [p]) : 1, Ax ? CS_IMAG (Ax [p]) : 0) ; -#else - printf ("%g\n", Ax ? Ax [p] : 1) ; -#endif - if (brief && p > 20) { printf (" ...\n") ; return (1) ; } - } - } - return (1) ; -} -#endif diff --git a/src/rigraph/vendor/cs/cs_pvec.c b/src/rigraph/vendor/cs/cs_pvec.c deleted file mode 100644 index 1254c2a..0000000 --- a/src/rigraph/vendor/cs/cs_pvec.c +++ /dev/null @@ -1,9 +0,0 @@ -#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/src/rigraph/vendor/cs/cs_qr.c b/src/rigraph/vendor/cs/cs_qr.c deleted file mode 100644 index 8bce32e..0000000 --- a/src/rigraph/vendor/cs/cs_qr.c +++ /dev/null @@ -1,74 +0,0 @@ -#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, 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) ; - 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/src/rigraph/vendor/cs/cs_qrsol.c b/src/rigraph/vendor/cs/cs_qrsol.c deleted file mode 100644 index d817ef2..0000000 --- a/src/rigraph/vendor/cs/cs_qrsol.c +++ /dev/null @@ -1,53 +0,0 @@ -#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/src/rigraph/vendor/cs/cs_randperm.c b/src/rigraph/vendor/cs/cs_randperm.c deleted file mode 100644 index 4570da0..0000000 --- a/src/rigraph/vendor/cs/cs_randperm.c +++ /dev/null @@ -1,28 +0,0 @@ -#include "cs.h" - -#include "igraph_random.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 = 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/src/rigraph/vendor/cs/cs_reach.c b/src/rigraph/vendor/cs/cs_reach.c deleted file mode 100644 index 0efb342..0000000 --- a/src/rigraph/vendor/cs/cs_reach.c +++ /dev/null @@ -1,19 +0,0 @@ -#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/src/rigraph/vendor/cs/cs_scatter.c b/src/rigraph/vendor/cs/cs_scatter.c deleted file mode 100644 index 734fdb2..0000000 --- a/src/rigraph/vendor/cs/cs_scatter.c +++ /dev/null @@ -1,22 +0,0 @@ -#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/src/rigraph/vendor/cs/cs_scc.c b/src/rigraph/vendor/cs/cs_scc.c deleted file mode 100644 index cc6d805..0000000 --- a/src/rigraph/vendor/cs/cs_scc.c +++ /dev/null @@ -1,41 +0,0 @@ -#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/src/rigraph/vendor/cs/cs_schol.c b/src/rigraph/vendor/cs/cs_schol.c deleted file mode 100644 index 7da2a57..0000000 --- a/src/rigraph/vendor/cs/cs_schol.c +++ /dev/null @@ -1,26 +0,0 @@ -#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/src/rigraph/vendor/cs/cs_spsolve.c b/src/rigraph/vendor/cs/cs_spsolve.c deleted file mode 100644 index 8c6ecce..0000000 --- a/src/rigraph/vendor/cs/cs_spsolve.c +++ /dev/null @@ -1,28 +0,0 @@ -#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/src/rigraph/vendor/cs/cs_sqr.c b/src/rigraph/vendor/cs/cs_sqr.c deleted file mode 100644 index 1b14ca4..0000000 --- a/src/rigraph/vendor/cs/cs_sqr.c +++ /dev/null @@ -1,87 +0,0 @@ -#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] ; - 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/src/rigraph/vendor/cs/cs_symperm.c b/src/rigraph/vendor/cs/cs_symperm.c deleted file mode 100644 index 7bbd6fe..0000000 --- a/src/rigraph/vendor/cs/cs_symperm.c +++ /dev/null @@ -1,39 +0,0 @@ -#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/src/rigraph/vendor/cs/cs_tdfs.c b/src/rigraph/vendor/cs/cs_tdfs.c deleted file mode 100644 index f32077b..0000000 --- a/src/rigraph/vendor/cs/cs_tdfs.c +++ /dev/null @@ -1,24 +0,0 @@ -#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/src/rigraph/vendor/cs/cs_transpose.c b/src/rigraph/vendor/cs/cs_transpose.c deleted file mode 100644 index 15a6c00..0000000 --- a/src/rigraph/vendor/cs/cs_transpose.c +++ /dev/null @@ -1,25 +0,0 @@ -#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/src/rigraph/vendor/cs/cs_updown.c b/src/rigraph/vendor/cs/cs_updown.c deleted file mode 100644 index e49af83..0000000 --- a/src/rigraph/vendor/cs/cs_updown.c +++ /dev/null @@ -1,48 +0,0 @@ -#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/src/rigraph/vendor/cs/cs_usolve.c b/src/rigraph/vendor/cs/cs_usolve.c deleted file mode 100644 index 6e89c83..0000000 --- a/src/rigraph/vendor/cs/cs_usolve.c +++ /dev/null @@ -1,18 +0,0 @@ -#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/src/rigraph/vendor/cs/cs_util.c b/src/rigraph/vendor/cs/cs_util.c deleted file mode 100644 index bb887ea..0000000 --- a/src/rigraph/vendor/cs/cs_util.c +++ /dev/null @@ -1,120 +0,0 @@ -#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 ; - nzmax = CS_MAX (nzmax, 1) ; - 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 *) 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 ((csn *) 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 ((css *) 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 ((csd *) cs_free (D)) ; /* free the csd struct and return NULL */ -} - -/* 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_INT *) cs_free (p)) ; /* return result, or 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/src/rigraph/vendor/cs/cs_utsolve.c b/src/rigraph/vendor/cs/cs_utsolve.c deleted file mode 100644 index d879fae..0000000 --- a/src/rigraph/vendor/cs/cs_utsolve.c +++ /dev/null @@ -1,18 +0,0 @@ -#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/src/rigraph/vendor/mini-gmp/mini-gmp.c b/src/rigraph/vendor/mini-gmp/mini-gmp.c deleted file mode 100644 index 76a691b..0000000 --- a/src/rigraph/vendor/mini-gmp/mini-gmp.c +++ /dev/null @@ -1,4578 +0,0 @@ -/* mini-gmp, a minimalistic implementation of a GNU GMP subset. - - Contributed to the GNU project by Niels Möller - -Copyright 1991-1997, 1999-2019 Free Software Foundation, Inc. - -This file is part of the GNU MP Library. - -The GNU MP Library is free software; you can redistribute it and/or modify -it under the terms of either: - - * the GNU Lesser General Public License as published by the Free - Software Foundation; either version 3 of the License, or (at your - option) any later version. - -or - - * the GNU General Public License as published by the Free Software - Foundation; either version 2 of the License, or (at your option) any - later version. - -or both in parallel, as here. - -The GNU MP Library is distributed in the hope that it will be useful, but -WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY -or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -for more details. - -You should have received copies of the GNU General Public License and the -GNU Lesser General Public License along with the GNU MP Library. If not, -see https://www.gnu.org/licenses/. */ - -/* NOTE: All functions in this file which are not declared in - mini-gmp.h are internal, and are not intended to be compatible - with GMP or with future versions of mini-gmp. */ - -/* Much of the material copied from GMP files, including: gmp-impl.h, - longlong.h, mpn/generic/add_n.c, mpn/generic/addmul_1.c, - mpn/generic/lshift.c, mpn/generic/mul_1.c, - mpn/generic/mul_basecase.c, mpn/generic/rshift.c, - mpn/generic/sbpi1_div_qr.c, mpn/generic/sub_n.c, - mpn/generic/submul_1.c. */ - -#include -#include -#include -#include -#include -#include - -#include "mini-gmp.h" - -#if !defined(MINI_GMP_DONT_USE_FLOAT_H) -#include -#endif - -#include "igraph_error.h" - - -/* Macros */ -#define GMP_LIMB_BITS (sizeof(mp_limb_t) * CHAR_BIT) - -#define GMP_LIMB_MAX ((mp_limb_t) ~ (mp_limb_t) 0) -#define GMP_LIMB_HIGHBIT ((mp_limb_t) 1 << (GMP_LIMB_BITS - 1)) - -#define GMP_HLIMB_BIT ((mp_limb_t) 1 << (GMP_LIMB_BITS / 2)) -#define GMP_LLIMB_MASK (GMP_HLIMB_BIT - 1) - -#define GMP_ULONG_BITS (sizeof(unsigned long) * CHAR_BIT) -#define GMP_ULONG_HIGHBIT ((unsigned long) 1 << (GMP_ULONG_BITS - 1)) - -#define GMP_ABS(x) ((x) >= 0 ? (x) : -(x)) -#define GMP_NEG_CAST(T,x) (-((T)((x) + 1) - 1)) - -#define GMP_MIN(a, b) ((a) < (b) ? (a) : (b)) -#define GMP_MAX(a, b) ((a) > (b) ? (a) : (b)) - -#define GMP_CMP(a,b) (((a) > (b)) - ((a) < (b))) - -#if defined(DBL_MANT_DIG) && FLT_RADIX == 2 -#define GMP_DBL_MANT_BITS DBL_MANT_DIG -#else -#define GMP_DBL_MANT_BITS (53) -#endif - -/* Return non-zero if xp,xsize and yp,ysize overlap. - If xp+xsize<=yp there's no overlap, or if yp+ysize<=xp there's no - overlap. If both these are false, there's an overlap. */ -#define GMP_MPN_OVERLAP_P(xp, xsize, yp, ysize) \ - ((xp) + (xsize) > (yp) && (yp) + (ysize) > (xp)) - -#define gmp_assert_nocarry(x) do { \ - mp_limb_t __cy = (x); \ - assert (__cy == 0); \ - } while (0) - -#define gmp_clz(count, x) do { \ - mp_limb_t __clz_x = (x); \ - unsigned __clz_c = 0; \ - int LOCAL_SHIFT_BITS = 8; \ - if (GMP_LIMB_BITS > LOCAL_SHIFT_BITS) \ - for (; \ - (__clz_x & ((mp_limb_t) 0xff << (GMP_LIMB_BITS - 8))) == 0; \ - __clz_c += 8) \ - { __clz_x <<= LOCAL_SHIFT_BITS; } \ - for (; (__clz_x & GMP_LIMB_HIGHBIT) == 0; __clz_c++) \ - __clz_x <<= 1; \ - (count) = __clz_c; \ - } while (0) - -#define gmp_ctz(count, x) do { \ - mp_limb_t __ctz_x = (x); \ - unsigned __ctz_c = 0; \ - gmp_clz (__ctz_c, __ctz_x & - __ctz_x); \ - (count) = GMP_LIMB_BITS - 1 - __ctz_c; \ - } while (0) - -#define gmp_add_ssaaaa(sh, sl, ah, al, bh, bl) \ - do { \ - mp_limb_t __x; \ - __x = (al) + (bl); \ - (sh) = (ah) + (bh) + (__x < (al)); \ - (sl) = __x; \ - } while (0) - -#define gmp_sub_ddmmss(sh, sl, ah, al, bh, bl) \ - do { \ - mp_limb_t __x; \ - __x = (al) - (bl); \ - (sh) = (ah) - (bh) - ((al) < (bl)); \ - (sl) = __x; \ - } while (0) - -#define gmp_umul_ppmm(w1, w0, u, v) \ - do { \ - int LOCAL_GMP_LIMB_BITS = GMP_LIMB_BITS; \ - if (sizeof(unsigned int) * CHAR_BIT >= 2 * GMP_LIMB_BITS) \ - { \ - unsigned int __ww = (unsigned int) (u) * (v); \ - w0 = (mp_limb_t) __ww; \ - w1 = (mp_limb_t) (__ww >> LOCAL_GMP_LIMB_BITS); \ - } \ - else if (GMP_ULONG_BITS >= 2 * GMP_LIMB_BITS) \ - { \ - unsigned long int __ww = (unsigned long int) (u) * (v); \ - w0 = (mp_limb_t) __ww; \ - w1 = (mp_limb_t) (__ww >> LOCAL_GMP_LIMB_BITS); \ - } \ - else { \ - mp_limb_t __x0, __x1, __x2, __x3; \ - unsigned __ul, __vl, __uh, __vh; \ - mp_limb_t __u = (u), __v = (v); \ - \ - __ul = __u & GMP_LLIMB_MASK; \ - __uh = __u >> (GMP_LIMB_BITS / 2); \ - __vl = __v & GMP_LLIMB_MASK; \ - __vh = __v >> (GMP_LIMB_BITS / 2); \ - \ - __x0 = (mp_limb_t) __ul * __vl; \ - __x1 = (mp_limb_t) __ul * __vh; \ - __x2 = (mp_limb_t) __uh * __vl; \ - __x3 = (mp_limb_t) __uh * __vh; \ - \ - __x1 += __x0 >> (GMP_LIMB_BITS / 2);/* this can't give carry */ \ - __x1 += __x2; /* but this indeed can */ \ - if (__x1 < __x2) /* did we get it? */ \ - __x3 += GMP_HLIMB_BIT; /* yes, add it in the proper pos. */ \ - \ - (w1) = __x3 + (__x1 >> (GMP_LIMB_BITS / 2)); \ - (w0) = (__x1 << (GMP_LIMB_BITS / 2)) + (__x0 & GMP_LLIMB_MASK); \ - } \ - } while (0) - -#define gmp_udiv_qrnnd_preinv(q, r, nh, nl, d, di) \ - do { \ - mp_limb_t _qh, _ql, _r, _mask; \ - gmp_umul_ppmm (_qh, _ql, (nh), (di)); \ - gmp_add_ssaaaa (_qh, _ql, _qh, _ql, (nh) + 1, (nl)); \ - _r = (nl) - _qh * (d); \ - _mask = -(mp_limb_t) (_r > _ql); /* both > and >= are OK */ \ - _qh += _mask; \ - _r += _mask & (d); \ - if (_r >= (d)) \ - { \ - _r -= (d); \ - _qh++; \ - } \ - \ - (r) = _r; \ - (q) = _qh; \ - } while (0) - -#define gmp_udiv_qr_3by2(q, r1, r0, n2, n1, n0, d1, d0, dinv) \ - do { \ - mp_limb_t _q0, _t1, _t0, _mask; \ - gmp_umul_ppmm ((q), _q0, (n2), (dinv)); \ - gmp_add_ssaaaa ((q), _q0, (q), _q0, (n2), (n1)); \ - \ - /* Compute the two most significant limbs of n - q'd */ \ - (r1) = (n1) - (d1) * (q); \ - gmp_sub_ddmmss ((r1), (r0), (r1), (n0), (d1), (d0)); \ - gmp_umul_ppmm (_t1, _t0, (d0), (q)); \ - gmp_sub_ddmmss ((r1), (r0), (r1), (r0), _t1, _t0); \ - (q)++; \ - \ - /* Conditionally adjust q and the remainders */ \ - _mask = - (mp_limb_t) ((r1) >= _q0); \ - (q) += _mask; \ - gmp_add_ssaaaa ((r1), (r0), (r1), (r0), _mask & (d1), _mask & (d0)); \ - if ((r1) >= (d1)) \ - { \ - if ((r1) > (d1) || (r0) >= (d0)) \ - { \ - (q)++; \ - gmp_sub_ddmmss ((r1), (r0), (r1), (r0), (d1), (d0)); \ - } \ - } \ - } while (0) - -/* Swap macros. */ -#define MP_LIMB_T_SWAP(x, y) \ - do { \ - mp_limb_t __mp_limb_t_swap__tmp = (x); \ - (x) = (y); \ - (y) = __mp_limb_t_swap__tmp; \ - } while (0) -#define MP_SIZE_T_SWAP(x, y) \ - do { \ - mp_size_t __mp_size_t_swap__tmp = (x); \ - (x) = (y); \ - (y) = __mp_size_t_swap__tmp; \ - } while (0) -#define MP_BITCNT_T_SWAP(x,y) \ - do { \ - mp_bitcnt_t __mp_bitcnt_t_swap__tmp = (x); \ - (x) = (y); \ - (y) = __mp_bitcnt_t_swap__tmp; \ - } while (0) -#define MP_PTR_SWAP(x, y) \ - do { \ - mp_ptr __mp_ptr_swap__tmp = (x); \ - (x) = (y); \ - (y) = __mp_ptr_swap__tmp; \ - } while (0) -#define MP_SRCPTR_SWAP(x, y) \ - do { \ - mp_srcptr __mp_srcptr_swap__tmp = (x); \ - (x) = (y); \ - (y) = __mp_srcptr_swap__tmp; \ - } while (0) - -#define MPN_PTR_SWAP(xp,xs, yp,ys) \ - do { \ - MP_PTR_SWAP (xp, yp); \ - MP_SIZE_T_SWAP (xs, ys); \ - } while(0) -#define MPN_SRCPTR_SWAP(xp,xs, yp,ys) \ - do { \ - MP_SRCPTR_SWAP (xp, yp); \ - MP_SIZE_T_SWAP (xs, ys); \ - } while(0) - -#define MPZ_PTR_SWAP(x, y) \ - do { \ - mpz_ptr __mpz_ptr_swap__tmp = (x); \ - (x) = (y); \ - (y) = __mpz_ptr_swap__tmp; \ - } while (0) -#define MPZ_SRCPTR_SWAP(x, y) \ - do { \ - mpz_srcptr __mpz_srcptr_swap__tmp = (x); \ - (x) = (y); \ - (y) = __mpz_srcptr_swap__tmp; \ - } while (0) - -const int mp_bits_per_limb = GMP_LIMB_BITS; - - -/* Memory allocation and other helper functions. */ -static void -gmp_die (const char *msg) -{ - /* - fprintf (stderr, "%s\n", msg); - abort(); - */ - IGRAPH_FATAL(msg); -} - -static void * -gmp_default_alloc (size_t size) -{ - void *p; - - assert (size > 0); - - p = malloc (size); - if (!p) - gmp_die("gmp_default_alloc: Virtual memory exhausted."); - - return p; -} - -static void * -gmp_default_realloc (void *old, size_t unused_old_size, size_t new_size) -{ - void * p; - - p = realloc (old, new_size); - - if (!p) - gmp_die("gmp_default_realloc: Virtual memory exhausted."); - - return p; -} - -static void -gmp_default_free (void *p, size_t unused_size) -{ - free (p); -} - -static void * (*gmp_allocate_func) (size_t) = gmp_default_alloc; -static void * (*gmp_reallocate_func) (void *, size_t, size_t) = gmp_default_realloc; -static void (*gmp_free_func) (void *, size_t) = gmp_default_free; - -void -mp_get_memory_functions (void *(**alloc_func) (size_t), - void *(**realloc_func) (void *, size_t, size_t), - void (**free_func) (void *, size_t)) -{ - if (alloc_func) - *alloc_func = gmp_allocate_func; - - if (realloc_func) - *realloc_func = gmp_reallocate_func; - - if (free_func) - *free_func = gmp_free_func; -} - -void -mp_set_memory_functions (void *(*alloc_func) (size_t), - void *(*realloc_func) (void *, size_t, size_t), - void (*free_func) (void *, size_t)) -{ - if (!alloc_func) - alloc_func = gmp_default_alloc; - if (!realloc_func) - realloc_func = gmp_default_realloc; - if (!free_func) - free_func = gmp_default_free; - - gmp_allocate_func = alloc_func; - gmp_reallocate_func = realloc_func; - gmp_free_func = free_func; -} - -#define gmp_xalloc(size) ((*gmp_allocate_func)((size))) -#define gmp_free(p) ((*gmp_free_func) ((p), 0)) - -static mp_ptr -gmp_xalloc_limbs (mp_size_t size) -{ - return (mp_ptr) gmp_xalloc (size * sizeof (mp_limb_t)); -} - -static mp_ptr -gmp_xrealloc_limbs (mp_ptr old, mp_size_t size) -{ - assert (size > 0); - return (mp_ptr) (*gmp_reallocate_func) (old, 0, size * sizeof (mp_limb_t)); -} - - -/* MPN interface */ - -void -mpn_copyi (mp_ptr d, mp_srcptr s, mp_size_t n) -{ - mp_size_t i; - for (i = 0; i < n; i++) - d[i] = s[i]; -} - -void -mpn_copyd (mp_ptr d, mp_srcptr s, mp_size_t n) -{ - while (--n >= 0) - d[n] = s[n]; -} - -int -mpn_cmp (mp_srcptr ap, mp_srcptr bp, mp_size_t n) -{ - while (--n >= 0) - { - if (ap[n] != bp[n]) - return ap[n] > bp[n] ? 1 : -1; - } - return 0; -} - -static int -mpn_cmp4 (mp_srcptr ap, mp_size_t an, mp_srcptr bp, mp_size_t bn) -{ - if (an != bn) - return an < bn ? -1 : 1; - else - return mpn_cmp (ap, bp, an); -} - -static mp_size_t -mpn_normalized_size (mp_srcptr xp, mp_size_t n) -{ - while (n > 0 && xp[n-1] == 0) - --n; - return n; -} - -int -mpn_zero_p(mp_srcptr rp, mp_size_t n) -{ - return mpn_normalized_size (rp, n) == 0; -} - -void -mpn_zero (mp_ptr rp, mp_size_t n) -{ - while (--n >= 0) - rp[n] = 0; -} - -mp_limb_t -mpn_add_1 (mp_ptr rp, mp_srcptr ap, mp_size_t n, mp_limb_t b) -{ - mp_size_t i; - - assert (n > 0); - i = 0; - do - { - mp_limb_t r = ap[i] + b; - /* Carry out */ - b = (r < b); - rp[i] = r; - } - while (++i < n); - - return b; -} - -mp_limb_t -mpn_add_n (mp_ptr rp, mp_srcptr ap, mp_srcptr bp, mp_size_t n) -{ - mp_size_t i; - mp_limb_t cy; - - for (i = 0, cy = 0; i < n; i++) - { - mp_limb_t a, b, r; - a = ap[i]; b = bp[i]; - r = a + cy; - cy = (r < cy); - r += b; - cy += (r < b); - rp[i] = r; - } - return cy; -} - -mp_limb_t -mpn_add (mp_ptr rp, mp_srcptr ap, mp_size_t an, mp_srcptr bp, mp_size_t bn) -{ - mp_limb_t cy; - - assert (an >= bn); - - cy = mpn_add_n (rp, ap, bp, bn); - if (an > bn) - cy = mpn_add_1 (rp + bn, ap + bn, an - bn, cy); - return cy; -} - -mp_limb_t -mpn_sub_1 (mp_ptr rp, mp_srcptr ap, mp_size_t n, mp_limb_t b) -{ - mp_size_t i; - - assert (n > 0); - - i = 0; - do - { - mp_limb_t a = ap[i]; - /* Carry out */ - mp_limb_t cy = a < b; - rp[i] = a - b; - b = cy; - } - while (++i < n); - - return b; -} - -mp_limb_t -mpn_sub_n (mp_ptr rp, mp_srcptr ap, mp_srcptr bp, mp_size_t n) -{ - mp_size_t i; - mp_limb_t cy; - - for (i = 0, cy = 0; i < n; i++) - { - mp_limb_t a, b; - a = ap[i]; b = bp[i]; - b += cy; - cy = (b < cy); - cy += (a < b); - rp[i] = a - b; - } - return cy; -} - -mp_limb_t -mpn_sub (mp_ptr rp, mp_srcptr ap, mp_size_t an, mp_srcptr bp, mp_size_t bn) -{ - mp_limb_t cy; - - assert (an >= bn); - - cy = mpn_sub_n (rp, ap, bp, bn); - if (an > bn) - cy = mpn_sub_1 (rp + bn, ap + bn, an - bn, cy); - return cy; -} - -mp_limb_t -mpn_mul_1 (mp_ptr rp, mp_srcptr up, mp_size_t n, mp_limb_t vl) -{ - mp_limb_t ul, cl, hpl, lpl; - - assert (n >= 1); - - cl = 0; - do - { - ul = *up++; - gmp_umul_ppmm (hpl, lpl, ul, vl); - - lpl += cl; - cl = (lpl < cl) + hpl; - - *rp++ = lpl; - } - while (--n != 0); - - return cl; -} - -mp_limb_t -mpn_addmul_1 (mp_ptr rp, mp_srcptr up, mp_size_t n, mp_limb_t vl) -{ - mp_limb_t ul, cl, hpl, lpl, rl; - - assert (n >= 1); - - cl = 0; - do - { - ul = *up++; - gmp_umul_ppmm (hpl, lpl, ul, vl); - - lpl += cl; - cl = (lpl < cl) + hpl; - - rl = *rp; - lpl = rl + lpl; - cl += lpl < rl; - *rp++ = lpl; - } - while (--n != 0); - - return cl; -} - -mp_limb_t -mpn_submul_1 (mp_ptr rp, mp_srcptr up, mp_size_t n, mp_limb_t vl) -{ - mp_limb_t ul, cl, hpl, lpl, rl; - - assert (n >= 1); - - cl = 0; - do - { - ul = *up++; - gmp_umul_ppmm (hpl, lpl, ul, vl); - - lpl += cl; - cl = (lpl < cl) + hpl; - - rl = *rp; - lpl = rl - lpl; - cl += lpl > rl; - *rp++ = lpl; - } - while (--n != 0); - - return cl; -} - -mp_limb_t -mpn_mul (mp_ptr rp, mp_srcptr up, mp_size_t un, mp_srcptr vp, mp_size_t vn) -{ - assert (un >= vn); - assert (vn >= 1); - assert (!GMP_MPN_OVERLAP_P(rp, un + vn, up, un)); - assert (!GMP_MPN_OVERLAP_P(rp, un + vn, vp, vn)); - - /* We first multiply by the low order limb. This result can be - stored, not added, to rp. We also avoid a loop for zeroing this - way. */ - - rp[un] = mpn_mul_1 (rp, up, un, vp[0]); - - /* Now accumulate the product of up[] and the next higher limb from - vp[]. */ - - while (--vn >= 1) - { - rp += 1, vp += 1; - rp[un] = mpn_addmul_1 (rp, up, un, vp[0]); - } - return rp[un]; -} - -void -mpn_mul_n (mp_ptr rp, mp_srcptr ap, mp_srcptr bp, mp_size_t n) -{ - mpn_mul (rp, ap, n, bp, n); -} - -void -mpn_sqr (mp_ptr rp, mp_srcptr ap, mp_size_t n) -{ - mpn_mul (rp, ap, n, ap, n); -} - -mp_limb_t -mpn_lshift (mp_ptr rp, mp_srcptr up, mp_size_t n, unsigned int cnt) -{ - mp_limb_t high_limb, low_limb; - unsigned int tnc; - mp_limb_t retval; - - assert (n >= 1); - assert (cnt >= 1); - assert (cnt < GMP_LIMB_BITS); - - up += n; - rp += n; - - tnc = GMP_LIMB_BITS - cnt; - low_limb = *--up; - retval = low_limb >> tnc; - high_limb = (low_limb << cnt); - - while (--n != 0) - { - low_limb = *--up; - *--rp = high_limb | (low_limb >> tnc); - high_limb = (low_limb << cnt); - } - *--rp = high_limb; - - return retval; -} - -mp_limb_t -mpn_rshift (mp_ptr rp, mp_srcptr up, mp_size_t n, unsigned int cnt) -{ - mp_limb_t high_limb, low_limb; - unsigned int tnc; - mp_limb_t retval; - - assert (n >= 1); - assert (cnt >= 1); - assert (cnt < GMP_LIMB_BITS); - - tnc = GMP_LIMB_BITS - cnt; - high_limb = *up++; - retval = (high_limb << tnc); - low_limb = high_limb >> cnt; - - while (--n != 0) - { - high_limb = *up++; - *rp++ = low_limb | (high_limb << tnc); - low_limb = high_limb >> cnt; - } - *rp = low_limb; - - return retval; -} - -static mp_bitcnt_t -mpn_common_scan (mp_limb_t limb, mp_size_t i, mp_srcptr up, mp_size_t un, - mp_limb_t ux) -{ - unsigned cnt; - - assert (ux == 0 || ux == GMP_LIMB_MAX); - assert (0 <= i && i <= un ); - - while (limb == 0) - { - i++; - if (i == un) - return (ux == 0 ? ~(mp_bitcnt_t) 0 : un * GMP_LIMB_BITS); - limb = ux ^ up[i]; - } - gmp_ctz (cnt, limb); - return (mp_bitcnt_t) i * GMP_LIMB_BITS + cnt; -} - -mp_bitcnt_t -mpn_scan1 (mp_srcptr ptr, mp_bitcnt_t bit) -{ - mp_size_t i; - i = bit / GMP_LIMB_BITS; - - return mpn_common_scan ( ptr[i] & (GMP_LIMB_MAX << (bit % GMP_LIMB_BITS)), - i, ptr, i, 0); -} - -mp_bitcnt_t -mpn_scan0 (mp_srcptr ptr, mp_bitcnt_t bit) -{ - mp_size_t i; - i = bit / GMP_LIMB_BITS; - - return mpn_common_scan (~ptr[i] & (GMP_LIMB_MAX << (bit % GMP_LIMB_BITS)), - i, ptr, i, GMP_LIMB_MAX); -} - -void -mpn_com (mp_ptr rp, mp_srcptr up, mp_size_t n) -{ - while (--n >= 0) - *rp++ = ~ *up++; -} - -mp_limb_t -mpn_neg (mp_ptr rp, mp_srcptr up, mp_size_t n) -{ - while (*up == 0) - { - *rp = 0; - if (!--n) - return 0; - ++up; ++rp; - } - *rp = - *up; - mpn_com (++rp, ++up, --n); - return 1; -} - - -/* MPN division interface. */ - -/* The 3/2 inverse is defined as - - m = floor( (B^3-1) / (B u1 + u0)) - B -*/ -mp_limb_t -mpn_invert_3by2 (mp_limb_t u1, mp_limb_t u0) -{ - mp_limb_t r, m; - - { - mp_limb_t p, ql; - unsigned ul, uh, qh; - - /* For notation, let b denote the half-limb base, so that B = b^2. - Split u1 = b uh + ul. */ - ul = u1 & GMP_LLIMB_MASK; - uh = u1 >> (GMP_LIMB_BITS / 2); - - /* Approximation of the high half of quotient. Differs from the 2/1 - inverse of the half limb uh, since we have already subtracted - u0. */ - qh = (u1 ^ GMP_LIMB_MAX) / uh; - - /* Adjust to get a half-limb 3/2 inverse, i.e., we want - - qh' = floor( (b^3 - 1) / u) - b = floor ((b^3 - b u - 1) / u - = floor( (b (~u) + b-1) / u), - - and the remainder - - r = b (~u) + b-1 - qh (b uh + ul) - = b (~u - qh uh) + b-1 - qh ul - - Subtraction of qh ul may underflow, which implies adjustments. - But by normalization, 2 u >= B > qh ul, so we need to adjust by - at most 2. - */ - - r = ((~u1 - (mp_limb_t) qh * uh) << (GMP_LIMB_BITS / 2)) | GMP_LLIMB_MASK; - - p = (mp_limb_t) qh * ul; - /* Adjustment steps taken from udiv_qrnnd_c */ - if (r < p) - { - qh--; - r += u1; - if (r >= u1) /* i.e. we didn't get carry when adding to r */ - if (r < p) - { - qh--; - r += u1; - } - } - r -= p; - - /* Low half of the quotient is - - ql = floor ( (b r + b-1) / u1). - - This is a 3/2 division (on half-limbs), for which qh is a - suitable inverse. */ - - p = (r >> (GMP_LIMB_BITS / 2)) * qh + r; - /* Unlike full-limb 3/2, we can add 1 without overflow. For this to - work, it is essential that ql is a full mp_limb_t. */ - ql = (p >> (GMP_LIMB_BITS / 2)) + 1; - - /* By the 3/2 trick, we don't need the high half limb. */ - r = (r << (GMP_LIMB_BITS / 2)) + GMP_LLIMB_MASK - ql * u1; - - if (r >= (GMP_LIMB_MAX & (p << (GMP_LIMB_BITS / 2)))) - { - ql--; - r += u1; - } - m = ((mp_limb_t) qh << (GMP_LIMB_BITS / 2)) + ql; - if (r >= u1) - { - m++; - r -= u1; - } - } - - /* Now m is the 2/1 inverse of u1. If u0 > 0, adjust it to become a - 3/2 inverse. */ - if (u0 > 0) - { - mp_limb_t th, tl; - r = ~r; - r += u0; - if (r < u0) - { - m--; - if (r >= u1) - { - m--; - r -= u1; - } - r -= u1; - } - gmp_umul_ppmm (th, tl, u0, m); - r += th; - if (r < th) - { - m--; - m -= ((r > u1) | ((r == u1) & (tl > u0))); - } - } - - return m; -} - -struct gmp_div_inverse -{ - /* Normalization shift count. */ - unsigned shift; - /* Normalized divisor (d0 unused for mpn_div_qr_1) */ - mp_limb_t d1, d0; - /* Inverse, for 2/1 or 3/2. */ - mp_limb_t di; -}; - -static void -mpn_div_qr_1_invert (struct gmp_div_inverse *inv, mp_limb_t d) -{ - unsigned shift; - - assert (d > 0); - gmp_clz (shift, d); - inv->shift = shift; - inv->d1 = d << shift; - inv->di = mpn_invert_limb (inv->d1); -} - -static void -mpn_div_qr_2_invert (struct gmp_div_inverse *inv, - mp_limb_t d1, mp_limb_t d0) -{ - unsigned shift; - - assert (d1 > 0); - gmp_clz (shift, d1); - inv->shift = shift; - if (shift > 0) - { - d1 = (d1 << shift) | (d0 >> (GMP_LIMB_BITS - shift)); - d0 <<= shift; - } - inv->d1 = d1; - inv->d0 = d0; - inv->di = mpn_invert_3by2 (d1, d0); -} - -static void -mpn_div_qr_invert (struct gmp_div_inverse *inv, - mp_srcptr dp, mp_size_t dn) -{ - assert (dn > 0); - - if (dn == 1) - mpn_div_qr_1_invert (inv, dp[0]); - else if (dn == 2) - mpn_div_qr_2_invert (inv, dp[1], dp[0]); - else - { - unsigned shift; - mp_limb_t d1, d0; - - d1 = dp[dn-1]; - d0 = dp[dn-2]; - assert (d1 > 0); - gmp_clz (shift, d1); - inv->shift = shift; - if (shift > 0) - { - d1 = (d1 << shift) | (d0 >> (GMP_LIMB_BITS - shift)); - d0 = (d0 << shift) | (dp[dn-3] >> (GMP_LIMB_BITS - shift)); - } - inv->d1 = d1; - inv->d0 = d0; - inv->di = mpn_invert_3by2 (d1, d0); - } -} - -/* Not matching current public gmp interface, rather corresponding to - the sbpi1_div_* functions. */ -static mp_limb_t -mpn_div_qr_1_preinv (mp_ptr qp, mp_srcptr np, mp_size_t nn, - const struct gmp_div_inverse *inv) -{ - mp_limb_t d, di; - mp_limb_t r; - mp_ptr tp = NULL; - - if (inv->shift > 0) - { - /* Shift, reusing qp area if possible. In-place shift if qp == np. */ - tp = qp ? qp : gmp_xalloc_limbs (nn); - r = mpn_lshift (tp, np, nn, inv->shift); - np = tp; - } - else - r = 0; - - d = inv->d1; - di = inv->di; - while (--nn >= 0) - { - mp_limb_t q; - - gmp_udiv_qrnnd_preinv (q, r, r, np[nn], d, di); - if (qp) - qp[nn] = q; - } - if ((inv->shift > 0) && (tp != qp)) - gmp_free (tp); - - return r >> inv->shift; -} - -static void -mpn_div_qr_2_preinv (mp_ptr qp, mp_ptr np, mp_size_t nn, - const struct gmp_div_inverse *inv) -{ - unsigned shift; - mp_size_t i; - mp_limb_t d1, d0, di, r1, r0; - - assert (nn >= 2); - shift = inv->shift; - d1 = inv->d1; - d0 = inv->d0; - di = inv->di; - - if (shift > 0) - r1 = mpn_lshift (np, np, nn, shift); - else - r1 = 0; - - r0 = np[nn - 1]; - - i = nn - 2; - do - { - mp_limb_t n0, q; - n0 = np[i]; - gmp_udiv_qr_3by2 (q, r1, r0, r1, r0, n0, d1, d0, di); - - if (qp) - qp[i] = q; - } - while (--i >= 0); - - if (shift > 0) - { - assert ((r0 & (GMP_LIMB_MAX >> (GMP_LIMB_BITS - shift))) == 0); - r0 = (r0 >> shift) | (r1 << (GMP_LIMB_BITS - shift)); - r1 >>= shift; - } - - np[1] = r1; - np[0] = r0; -} - -static void -mpn_div_qr_pi1 (mp_ptr qp, - mp_ptr np, mp_size_t nn, mp_limb_t n1, - mp_srcptr dp, mp_size_t dn, - mp_limb_t dinv) -{ - mp_size_t i; - - mp_limb_t d1, d0; - mp_limb_t cy, cy1; - mp_limb_t q; - - assert (dn > 2); - assert (nn >= dn); - - d1 = dp[dn - 1]; - d0 = dp[dn - 2]; - - assert ((d1 & GMP_LIMB_HIGHBIT) != 0); - /* Iteration variable is the index of the q limb. - * - * We divide - * by - */ - - i = nn - dn; - do - { - mp_limb_t n0 = np[dn-1+i]; - - if (n1 == d1 && n0 == d0) - { - q = GMP_LIMB_MAX; - mpn_submul_1 (np+i, dp, dn, q); - n1 = np[dn-1+i]; /* update n1, last loop's value will now be invalid */ - } - else - { - gmp_udiv_qr_3by2 (q, n1, n0, n1, n0, np[dn-2+i], d1, d0, dinv); - - cy = mpn_submul_1 (np + i, dp, dn-2, q); - - cy1 = n0 < cy; - n0 = n0 - cy; - cy = n1 < cy1; - n1 = n1 - cy1; - np[dn-2+i] = n0; - - if (cy != 0) - { - n1 += d1 + mpn_add_n (np + i, np + i, dp, dn - 1); - q--; - } - } - - if (qp) - qp[i] = q; - } - while (--i >= 0); - - np[dn - 1] = n1; -} - -static void -mpn_div_qr_preinv (mp_ptr qp, mp_ptr np, mp_size_t nn, - mp_srcptr dp, mp_size_t dn, - const struct gmp_div_inverse *inv) -{ - assert (dn > 0); - assert (nn >= dn); - - if (dn == 1) - np[0] = mpn_div_qr_1_preinv (qp, np, nn, inv); - else if (dn == 2) - mpn_div_qr_2_preinv (qp, np, nn, inv); - else - { - mp_limb_t nh; - unsigned shift; - - assert (inv->d1 == dp[dn-1]); - assert (inv->d0 == dp[dn-2]); - assert ((inv->d1 & GMP_LIMB_HIGHBIT) != 0); - - shift = inv->shift; - if (shift > 0) - nh = mpn_lshift (np, np, nn, shift); - else - nh = 0; - - mpn_div_qr_pi1 (qp, np, nn, nh, dp, dn, inv->di); - - if (shift > 0) - gmp_assert_nocarry (mpn_rshift (np, np, dn, shift)); - } -} - -static void -mpn_div_qr (mp_ptr qp, mp_ptr np, mp_size_t nn, mp_srcptr dp, mp_size_t dn) -{ - struct gmp_div_inverse inv; - mp_ptr tp = NULL; - - assert (dn > 0); - assert (nn >= dn); - - mpn_div_qr_invert (&inv, dp, dn); - if (dn > 2 && inv.shift > 0) - { - tp = gmp_xalloc_limbs (dn); - gmp_assert_nocarry (mpn_lshift (tp, dp, dn, inv.shift)); - dp = tp; - } - mpn_div_qr_preinv (qp, np, nn, dp, dn, &inv); - if (tp) - gmp_free (tp); -} - - -/* MPN base conversion. */ -static unsigned -mpn_base_power_of_two_p (unsigned b) -{ - switch (b) - { - case 2: return 1; - case 4: return 2; - case 8: return 3; - case 16: return 4; - case 32: return 5; - case 64: return 6; - case 128: return 7; - case 256: return 8; - default: return 0; - } -} - -struct mpn_base_info -{ - /* bb is the largest power of the base which fits in one limb, and - exp is the corresponding exponent. */ - unsigned exp; - mp_limb_t bb; -}; - -static void -mpn_get_base_info (struct mpn_base_info *info, mp_limb_t b) -{ - mp_limb_t m; - mp_limb_t p; - unsigned exp; - - m = GMP_LIMB_MAX / b; - for (exp = 1, p = b; p <= m; exp++) - p *= b; - - info->exp = exp; - info->bb = p; -} - -static mp_bitcnt_t -mpn_limb_size_in_base_2 (mp_limb_t u) -{ - unsigned shift; - - assert (u > 0); - gmp_clz (shift, u); - return GMP_LIMB_BITS - shift; -} - -static size_t -mpn_get_str_bits (unsigned char *sp, unsigned bits, mp_srcptr up, mp_size_t un) -{ - unsigned char mask; - size_t sn, j; - mp_size_t i; - unsigned shift; - - sn = ((un - 1) * GMP_LIMB_BITS + mpn_limb_size_in_base_2 (up[un-1]) - + bits - 1) / bits; - - mask = (1U << bits) - 1; - - for (i = 0, j = sn, shift = 0; j-- > 0;) - { - unsigned char digit = up[i] >> shift; - - shift += bits; - - if (shift >= GMP_LIMB_BITS && ++i < un) - { - shift -= GMP_LIMB_BITS; - digit |= up[i] << (bits - shift); - } - sp[j] = digit & mask; - } - return sn; -} - -/* We generate digits from the least significant end, and reverse at - the end. */ -static size_t -mpn_limb_get_str (unsigned char *sp, mp_limb_t w, - const struct gmp_div_inverse *binv) -{ - mp_size_t i; - for (i = 0; w > 0; i++) - { - mp_limb_t h, l, r; - - h = w >> (GMP_LIMB_BITS - binv->shift); - l = w << binv->shift; - - gmp_udiv_qrnnd_preinv (w, r, h, l, binv->d1, binv->di); - assert ((r & (GMP_LIMB_MAX >> (GMP_LIMB_BITS - binv->shift))) == 0); - r >>= binv->shift; - - sp[i] = r; - } - return i; -} - -static size_t -mpn_get_str_other (unsigned char *sp, - int base, const struct mpn_base_info *info, - mp_ptr up, mp_size_t un) -{ - struct gmp_div_inverse binv; - size_t sn; - size_t i; - - mpn_div_qr_1_invert (&binv, base); - - sn = 0; - - if (un > 1) - { - struct gmp_div_inverse bbinv; - mpn_div_qr_1_invert (&bbinv, info->bb); - - do - { - mp_limb_t w; - size_t done; - w = mpn_div_qr_1_preinv (up, up, un, &bbinv); - un -= (up[un-1] == 0); - done = mpn_limb_get_str (sp + sn, w, &binv); - - for (sn += done; done < info->exp; done++) - sp[sn++] = 0; - } - while (un > 1); - } - sn += mpn_limb_get_str (sp + sn, up[0], &binv); - - /* Reverse order */ - for (i = 0; 2*i + 1 < sn; i++) - { - unsigned char t = sp[i]; - sp[i] = sp[sn - i - 1]; - sp[sn - i - 1] = t; - } - - return sn; -} - -size_t -mpn_get_str (unsigned char *sp, int base, mp_ptr up, mp_size_t un) -{ - unsigned bits; - - assert (un > 0); - assert (up[un-1] > 0); - - bits = mpn_base_power_of_two_p (base); - if (bits) - return mpn_get_str_bits (sp, bits, up, un); - else - { - struct mpn_base_info info; - - mpn_get_base_info (&info, base); - return mpn_get_str_other (sp, base, &info, up, un); - } -} - -static mp_size_t -mpn_set_str_bits (mp_ptr rp, const unsigned char *sp, size_t sn, - unsigned bits) -{ - mp_size_t rn; - size_t j; - unsigned shift; - - for (j = sn, rn = 0, shift = 0; j-- > 0; ) - { - if (shift == 0) - { - rp[rn++] = sp[j]; - shift += bits; - } - else - { - rp[rn-1] |= (mp_limb_t) sp[j] << shift; - shift += bits; - if (shift >= GMP_LIMB_BITS) - { - shift -= GMP_LIMB_BITS; - if (shift > 0) - rp[rn++] = (mp_limb_t) sp[j] >> (bits - shift); - } - } - } - rn = mpn_normalized_size (rp, rn); - return rn; -} - -/* Result is usually normalized, except for all-zero input, in which - case a single zero limb is written at *RP, and 1 is returned. */ -static mp_size_t -mpn_set_str_other (mp_ptr rp, const unsigned char *sp, size_t sn, - mp_limb_t b, const struct mpn_base_info *info) -{ - mp_size_t rn; - mp_limb_t w; - unsigned k; - size_t j; - - assert (sn > 0); - - k = 1 + (sn - 1) % info->exp; - - j = 0; - w = sp[j++]; - while (--k != 0) - w = w * b + sp[j++]; - - rp[0] = w; - - for (rn = 1; j < sn;) - { - mp_limb_t cy; - - w = sp[j++]; - for (k = 1; k < info->exp; k++) - w = w * b + sp[j++]; - - cy = mpn_mul_1 (rp, rp, rn, info->bb); - cy += mpn_add_1 (rp, rp, rn, w); - if (cy > 0) - rp[rn++] = cy; - } - assert (j == sn); - - return rn; -} - -mp_size_t -mpn_set_str (mp_ptr rp, const unsigned char *sp, size_t sn, int base) -{ - unsigned bits; - - if (sn == 0) - return 0; - - bits = mpn_base_power_of_two_p (base); - if (bits) - return mpn_set_str_bits (rp, sp, sn, bits); - else - { - struct mpn_base_info info; - - mpn_get_base_info (&info, base); - return mpn_set_str_other (rp, sp, sn, base, &info); - } -} - - -/* MPZ interface */ -void -mpz_init (mpz_t r) -{ - static const mp_limb_t dummy_limb = GMP_LIMB_MAX & 0xc1a0; - - r->_mp_alloc = 0; - r->_mp_size = 0; - r->_mp_d = (mp_ptr) &dummy_limb; -} - -/* The utility of this function is a bit limited, since many functions - assigns the result variable using mpz_swap. */ -void -mpz_init2 (mpz_t r, mp_bitcnt_t bits) -{ - mp_size_t rn; - - bits -= (bits != 0); /* Round down, except if 0 */ - rn = 1 + bits / GMP_LIMB_BITS; - - r->_mp_alloc = rn; - r->_mp_size = 0; - r->_mp_d = gmp_xalloc_limbs (rn); -} - -void -mpz_clear (mpz_t r) -{ - if (r->_mp_alloc) - gmp_free (r->_mp_d); -} - -static mp_ptr -mpz_realloc (mpz_t r, mp_size_t size) -{ - size = GMP_MAX (size, 1); - - if (r->_mp_alloc) - r->_mp_d = gmp_xrealloc_limbs (r->_mp_d, size); - else - r->_mp_d = gmp_xalloc_limbs (size); - r->_mp_alloc = size; - - if (GMP_ABS (r->_mp_size) > size) - r->_mp_size = 0; - - return r->_mp_d; -} - -/* Realloc for an mpz_t WHAT if it has less than NEEDED limbs. */ -#define MPZ_REALLOC(z,n) ((n) > (z)->_mp_alloc \ - ? mpz_realloc(z,n) \ - : (z)->_mp_d) - -/* MPZ assignment and basic conversions. */ -void -mpz_set_si (mpz_t r, signed long int x) -{ - if (x >= 0) - mpz_set_ui (r, x); - else /* (x < 0) */ - if (GMP_LIMB_BITS < GMP_ULONG_BITS) - { - mpz_set_ui (r, GMP_NEG_CAST (unsigned long int, x)); - mpz_neg (r, r); - } - else - { - r->_mp_size = -1; - MPZ_REALLOC (r, 1)[0] = GMP_NEG_CAST (unsigned long int, x); - } -} - -void -mpz_set_ui (mpz_t r, unsigned long int x) -{ - if (x > 0) - { - r->_mp_size = 1; - MPZ_REALLOC (r, 1)[0] = x; - if (GMP_LIMB_BITS < GMP_ULONG_BITS) - { - int LOCAL_GMP_LIMB_BITS = GMP_LIMB_BITS; - while (x >>= LOCAL_GMP_LIMB_BITS) - { - ++ r->_mp_size; - MPZ_REALLOC (r, r->_mp_size)[r->_mp_size - 1] = x; - } - } - } - else - r->_mp_size = 0; -} - -void -mpz_set (mpz_t r, const mpz_t x) -{ - /* Allow the NOP r == x */ - if (r != x) - { - mp_size_t n; - mp_ptr rp; - - n = GMP_ABS (x->_mp_size); - rp = MPZ_REALLOC (r, n); - - mpn_copyi (rp, x->_mp_d, n); - r->_mp_size = x->_mp_size; - } -} - -void -mpz_init_set_si (mpz_t r, signed long int x) -{ - mpz_init (r); - mpz_set_si (r, x); -} - -void -mpz_init_set_ui (mpz_t r, unsigned long int x) -{ - mpz_init (r); - mpz_set_ui (r, x); -} - -void -mpz_init_set (mpz_t r, const mpz_t x) -{ - mpz_init (r); - mpz_set (r, x); -} - -int -mpz_fits_slong_p (const mpz_t u) -{ - return (LONG_MAX + LONG_MIN == 0 || mpz_cmp_ui (u, LONG_MAX) <= 0) && - mpz_cmpabs_ui (u, GMP_NEG_CAST (unsigned long int, LONG_MIN)) <= 0; -} - -static int -mpn_absfits_ulong_p (mp_srcptr up, mp_size_t un) -{ - int ulongsize = GMP_ULONG_BITS / GMP_LIMB_BITS; - mp_limb_t ulongrem = 0; - - if (GMP_ULONG_BITS % GMP_LIMB_BITS != 0) - ulongrem = (mp_limb_t) (ULONG_MAX >> GMP_LIMB_BITS * ulongsize) + 1; - - return un <= ulongsize || (up[ulongsize] < ulongrem && un == ulongsize + 1); -} - -int -mpz_fits_ulong_p (const mpz_t u) -{ - mp_size_t us = u->_mp_size; - - return us >= 0 && mpn_absfits_ulong_p (u->_mp_d, us); -} - -long int -mpz_get_si (const mpz_t u) -{ - unsigned long r = mpz_get_ui (u); - unsigned long c = -LONG_MAX - LONG_MIN; - - if (u->_mp_size < 0) - /* This expression is necessary to properly handle -LONG_MIN */ - return -(long) c - (long) ((r - c) & LONG_MAX); - else - return (long) (r & LONG_MAX); -} - -unsigned long int -mpz_get_ui (const mpz_t u) -{ - if (GMP_LIMB_BITS < GMP_ULONG_BITS) - { - int LOCAL_GMP_LIMB_BITS = GMP_LIMB_BITS; - unsigned long r = 0; - mp_size_t n = GMP_ABS (u->_mp_size); - n = GMP_MIN (n, 1 + (mp_size_t) (GMP_ULONG_BITS - 1) / GMP_LIMB_BITS); - while (--n >= 0) - r = (r << LOCAL_GMP_LIMB_BITS) + u->_mp_d[n]; - return r; - } - - return u->_mp_size == 0 ? 0 : u->_mp_d[0]; -} - -size_t -mpz_size (const mpz_t u) -{ - return GMP_ABS (u->_mp_size); -} - -mp_limb_t -mpz_getlimbn (const mpz_t u, mp_size_t n) -{ - if (n >= 0 && n < GMP_ABS (u->_mp_size)) - return u->_mp_d[n]; - else - return 0; -} - -void -mpz_realloc2 (mpz_t x, mp_bitcnt_t n) -{ - mpz_realloc (x, 1 + (n - (n != 0)) / GMP_LIMB_BITS); -} - -mp_srcptr -mpz_limbs_read (mpz_srcptr x) -{ - return x->_mp_d; -} - -mp_ptr -mpz_limbs_modify (mpz_t x, mp_size_t n) -{ - assert (n > 0); - return MPZ_REALLOC (x, n); -} - -mp_ptr -mpz_limbs_write (mpz_t x, mp_size_t n) -{ - return mpz_limbs_modify (x, n); -} - -void -mpz_limbs_finish (mpz_t x, mp_size_t xs) -{ - mp_size_t xn; - xn = mpn_normalized_size (x->_mp_d, GMP_ABS (xs)); - x->_mp_size = xs < 0 ? -xn : xn; -} - -static mpz_srcptr -mpz_roinit_normal_n (mpz_t x, mp_srcptr xp, mp_size_t xs) -{ - x->_mp_alloc = 0; - x->_mp_d = (mp_ptr) xp; - x->_mp_size = xs; - return x; -} - -mpz_srcptr -mpz_roinit_n (mpz_t x, mp_srcptr xp, mp_size_t xs) -{ - mpz_roinit_normal_n (x, xp, xs); - mpz_limbs_finish (x, xs); - return x; -} - - -/* Conversions and comparison to double. */ -void -mpz_set_d (mpz_t r, double x) -{ - int sign; - mp_ptr rp; - mp_size_t rn, i; - double B; - double Bi; - mp_limb_t f; - - /* x != x is true when x is a NaN, and x == x * 0.5 is true when x is - zero or infinity. */ - if (x != x || x == x * 0.5) - { - r->_mp_size = 0; - return; - } - - sign = x < 0.0 ; - if (sign) - x = - x; - - if (x < 1.0) - { - r->_mp_size = 0; - return; - } - B = 4.0 * (double) (GMP_LIMB_HIGHBIT >> 1); - Bi = 1.0 / B; - for (rn = 1; x >= B; rn++) - x *= Bi; - - rp = MPZ_REALLOC (r, rn); - - f = (mp_limb_t) x; - x -= f; - assert (x < 1.0); - i = rn-1; - rp[i] = f; - while (--i >= 0) - { - x = B * x; - f = (mp_limb_t) x; - x -= f; - assert (x < 1.0); - rp[i] = f; - } - - r->_mp_size = sign ? - rn : rn; -} - -void -mpz_init_set_d (mpz_t r, double x) -{ - mpz_init (r); - mpz_set_d (r, x); -} - -double -mpz_get_d (const mpz_t u) -{ - int m; - mp_limb_t l; - mp_size_t un; - double x; - double B = 4.0 * (double) (GMP_LIMB_HIGHBIT >> 1); - - un = GMP_ABS (u->_mp_size); - - if (un == 0) - return 0.0; - - l = u->_mp_d[--un]; - gmp_clz (m, l); - m = m + GMP_DBL_MANT_BITS - GMP_LIMB_BITS; - if (m < 0) - l &= GMP_LIMB_MAX << -m; - - for (x = l; --un >= 0;) - { - x = B*x; - if (m > 0) { - l = u->_mp_d[un]; - m -= GMP_LIMB_BITS; - if (m < 0) - l &= GMP_LIMB_MAX << -m; - x += l; - } - } - - if (u->_mp_size < 0) - x = -x; - - return x; -} - -int -mpz_cmpabs_d (const mpz_t x, double d) -{ - mp_size_t xn; - double B, Bi; - mp_size_t i; - - xn = x->_mp_size; - d = GMP_ABS (d); - - if (xn != 0) - { - xn = GMP_ABS (xn); - - B = 4.0 * (double) (GMP_LIMB_HIGHBIT >> 1); - Bi = 1.0 / B; - - /* Scale d so it can be compared with the top limb. */ - for (i = 1; i < xn; i++) - d *= Bi; - - if (d >= B) - return -1; - - /* Compare floor(d) to top limb, subtract and cancel when equal. */ - for (i = xn; i-- > 0;) - { - mp_limb_t f, xl; - - f = (mp_limb_t) d; - xl = x->_mp_d[i]; - if (xl > f) - return 1; - else if (xl < f) - return -1; - d = B * (d - f); - } - } - return - (d > 0.0); -} - -int -mpz_cmp_d (const mpz_t x, double d) -{ - if (x->_mp_size < 0) - { - if (d >= 0.0) - return -1; - else - return -mpz_cmpabs_d (x, d); - } - else - { - if (d < 0.0) - return 1; - else - return mpz_cmpabs_d (x, d); - } -} - - -/* MPZ comparisons and the like. */ -int -mpz_sgn (const mpz_t u) -{ - return GMP_CMP (u->_mp_size, 0); -} - -int -mpz_cmp_si (const mpz_t u, long v) -{ - mp_size_t usize = u->_mp_size; - - if (v >= 0) - return mpz_cmp_ui (u, v); - else if (usize >= 0) - return 1; - else - return - mpz_cmpabs_ui (u, GMP_NEG_CAST (unsigned long int, v)); -} - -int -mpz_cmp_ui (const mpz_t u, unsigned long v) -{ - mp_size_t usize = u->_mp_size; - - if (usize < 0) - return -1; - else - return mpz_cmpabs_ui (u, v); -} - -int -mpz_cmp (const mpz_t a, const mpz_t b) -{ - mp_size_t asize = a->_mp_size; - mp_size_t bsize = b->_mp_size; - - if (asize != bsize) - return (asize < bsize) ? -1 : 1; - else if (asize >= 0) - return mpn_cmp (a->_mp_d, b->_mp_d, asize); - else - return mpn_cmp (b->_mp_d, a->_mp_d, -asize); -} - -int -mpz_cmpabs_ui (const mpz_t u, unsigned long v) -{ - mp_size_t un = GMP_ABS (u->_mp_size); - - if (! mpn_absfits_ulong_p (u->_mp_d, un)) - return 1; - else - { - unsigned long uu = mpz_get_ui (u); - return GMP_CMP(uu, v); - } -} - -int -mpz_cmpabs (const mpz_t u, const mpz_t v) -{ - return mpn_cmp4 (u->_mp_d, GMP_ABS (u->_mp_size), - v->_mp_d, GMP_ABS (v->_mp_size)); -} - -void -mpz_abs (mpz_t r, const mpz_t u) -{ - mpz_set (r, u); - r->_mp_size = GMP_ABS (r->_mp_size); -} - -void -mpz_neg (mpz_t r, const mpz_t u) -{ - mpz_set (r, u); - r->_mp_size = -r->_mp_size; -} - -void -mpz_swap (mpz_t u, mpz_t v) -{ - MP_SIZE_T_SWAP (u->_mp_size, v->_mp_size); - MP_SIZE_T_SWAP (u->_mp_alloc, v->_mp_alloc); - MP_PTR_SWAP (u->_mp_d, v->_mp_d); -} - - -/* MPZ addition and subtraction */ - - -void -mpz_add_ui (mpz_t r, const mpz_t a, unsigned long b) -{ - mpz_t bb; - mpz_init_set_ui (bb, b); - mpz_add (r, a, bb); - mpz_clear (bb); -} - -void -mpz_sub_ui (mpz_t r, const mpz_t a, unsigned long b) -{ - mpz_ui_sub (r, b, a); - mpz_neg (r, r); -} - -void -mpz_ui_sub (mpz_t r, unsigned long a, const mpz_t b) -{ - mpz_neg (r, b); - mpz_add_ui (r, r, a); -} - -static mp_size_t -mpz_abs_add (mpz_t r, const mpz_t a, const mpz_t b) -{ - mp_size_t an = GMP_ABS (a->_mp_size); - mp_size_t bn = GMP_ABS (b->_mp_size); - mp_ptr rp; - mp_limb_t cy; - - if (an < bn) - { - MPZ_SRCPTR_SWAP (a, b); - MP_SIZE_T_SWAP (an, bn); - } - - rp = MPZ_REALLOC (r, an + 1); - cy = mpn_add (rp, a->_mp_d, an, b->_mp_d, bn); - - rp[an] = cy; - - return an + cy; -} - -static mp_size_t -mpz_abs_sub (mpz_t r, const mpz_t a, const mpz_t b) -{ - mp_size_t an = GMP_ABS (a->_mp_size); - mp_size_t bn = GMP_ABS (b->_mp_size); - int cmp; - mp_ptr rp; - - cmp = mpn_cmp4 (a->_mp_d, an, b->_mp_d, bn); - if (cmp > 0) - { - rp = MPZ_REALLOC (r, an); - gmp_assert_nocarry (mpn_sub (rp, a->_mp_d, an, b->_mp_d, bn)); - return mpn_normalized_size (rp, an); - } - else if (cmp < 0) - { - rp = MPZ_REALLOC (r, bn); - gmp_assert_nocarry (mpn_sub (rp, b->_mp_d, bn, a->_mp_d, an)); - return -mpn_normalized_size (rp, bn); - } - else - return 0; -} - -void -mpz_add (mpz_t r, const mpz_t a, const mpz_t b) -{ - mp_size_t rn; - - if ( (a->_mp_size ^ b->_mp_size) >= 0) - rn = mpz_abs_add (r, a, b); - else - rn = mpz_abs_sub (r, a, b); - - r->_mp_size = a->_mp_size >= 0 ? rn : - rn; -} - -void -mpz_sub (mpz_t r, const mpz_t a, const mpz_t b) -{ - mp_size_t rn; - - if ( (a->_mp_size ^ b->_mp_size) >= 0) - rn = mpz_abs_sub (r, a, b); - else - rn = mpz_abs_add (r, a, b); - - r->_mp_size = a->_mp_size >= 0 ? rn : - rn; -} - - -/* MPZ multiplication */ -void -mpz_mul_si (mpz_t r, const mpz_t u, long int v) -{ - if (v < 0) - { - mpz_mul_ui (r, u, GMP_NEG_CAST (unsigned long int, v)); - mpz_neg (r, r); - } - else - mpz_mul_ui (r, u, v); -} - -void -mpz_mul_ui (mpz_t r, const mpz_t u, unsigned long int v) -{ - mpz_t vv; - mpz_init_set_ui (vv, v); - mpz_mul (r, u, vv); - mpz_clear (vv); - return; -} - -void -mpz_mul (mpz_t r, const mpz_t u, const mpz_t v) -{ - int sign; - mp_size_t un, vn, rn; - mpz_t t; - mp_ptr tp; - - un = u->_mp_size; - vn = v->_mp_size; - - if (un == 0 || vn == 0) - { - r->_mp_size = 0; - return; - } - - sign = (un ^ vn) < 0; - - un = GMP_ABS (un); - vn = GMP_ABS (vn); - - mpz_init2 (t, (un + vn) * GMP_LIMB_BITS); - - tp = t->_mp_d; - if (un >= vn) - mpn_mul (tp, u->_mp_d, un, v->_mp_d, vn); - else - mpn_mul (tp, v->_mp_d, vn, u->_mp_d, un); - - rn = un + vn; - rn -= tp[rn-1] == 0; - - t->_mp_size = sign ? - rn : rn; - mpz_swap (r, t); - mpz_clear (t); -} - -void -mpz_mul_2exp (mpz_t r, const mpz_t u, mp_bitcnt_t bits) -{ - mp_size_t un, rn; - mp_size_t limbs; - unsigned shift; - mp_ptr rp; - - un = GMP_ABS (u->_mp_size); - if (un == 0) - { - r->_mp_size = 0; - return; - } - - limbs = bits / GMP_LIMB_BITS; - shift = bits % GMP_LIMB_BITS; - - rn = un + limbs + (shift > 0); - rp = MPZ_REALLOC (r, rn); - if (shift > 0) - { - mp_limb_t cy = mpn_lshift (rp + limbs, u->_mp_d, un, shift); - rp[rn-1] = cy; - rn -= (cy == 0); - } - else - mpn_copyd (rp + limbs, u->_mp_d, un); - - mpn_zero (rp, limbs); - - r->_mp_size = (u->_mp_size < 0) ? - rn : rn; -} - -void -mpz_addmul_ui (mpz_t r, const mpz_t u, unsigned long int v) -{ - mpz_t t; - mpz_init_set_ui (t, v); - mpz_mul (t, u, t); - mpz_add (r, r, t); - mpz_clear (t); -} - -void -mpz_submul_ui (mpz_t r, const mpz_t u, unsigned long int v) -{ - mpz_t t; - mpz_init_set_ui (t, v); - mpz_mul (t, u, t); - mpz_sub (r, r, t); - mpz_clear (t); -} - -void -mpz_addmul (mpz_t r, const mpz_t u, const mpz_t v) -{ - mpz_t t; - mpz_init (t); - mpz_mul (t, u, v); - mpz_add (r, r, t); - mpz_clear (t); -} - -void -mpz_submul (mpz_t r, const mpz_t u, const mpz_t v) -{ - mpz_t t; - mpz_init (t); - mpz_mul (t, u, v); - mpz_sub (r, r, t); - mpz_clear (t); -} - - -/* MPZ division */ -enum mpz_div_round_mode { GMP_DIV_FLOOR, GMP_DIV_CEIL, GMP_DIV_TRUNC }; - -/* Allows q or r to be zero. Returns 1 iff remainder is non-zero. */ -static int -mpz_div_qr (mpz_t q, mpz_t r, - const mpz_t n, const mpz_t d, enum mpz_div_round_mode mode) -{ - mp_size_t ns, ds, nn, dn, qs; - ns = n->_mp_size; - ds = d->_mp_size; - - if (ds == 0) - gmp_die("mpz_div_qr: Divide by zero."); - - if (ns == 0) - { - if (q) - q->_mp_size = 0; - if (r) - r->_mp_size = 0; - return 0; - } - - nn = GMP_ABS (ns); - dn = GMP_ABS (ds); - - qs = ds ^ ns; - - if (nn < dn) - { - if (mode == GMP_DIV_CEIL && qs >= 0) - { - /* q = 1, r = n - d */ - if (r) - mpz_sub (r, n, d); - if (q) - mpz_set_ui (q, 1); - } - else if (mode == GMP_DIV_FLOOR && qs < 0) - { - /* q = -1, r = n + d */ - if (r) - mpz_add (r, n, d); - if (q) - mpz_set_si (q, -1); - } - else - { - /* q = 0, r = d */ - if (r) - mpz_set (r, n); - if (q) - q->_mp_size = 0; - } - return 1; - } - else - { - mp_ptr np, qp; - mp_size_t qn, rn; - mpz_t tq, tr; - - mpz_init_set (tr, n); - np = tr->_mp_d; - - qn = nn - dn + 1; - - if (q) - { - mpz_init2 (tq, qn * GMP_LIMB_BITS); - qp = tq->_mp_d; - } - else - qp = NULL; - - mpn_div_qr (qp, np, nn, d->_mp_d, dn); - - if (qp) - { - qn -= (qp[qn-1] == 0); - - tq->_mp_size = qs < 0 ? -qn : qn; - } - rn = mpn_normalized_size (np, dn); - tr->_mp_size = ns < 0 ? - rn : rn; - - if (mode == GMP_DIV_FLOOR && qs < 0 && rn != 0) - { - if (q) - mpz_sub_ui (tq, tq, 1); - if (r) - mpz_add (tr, tr, d); - } - else if (mode == GMP_DIV_CEIL && qs >= 0 && rn != 0) - { - if (q) - mpz_add_ui (tq, tq, 1); - if (r) - mpz_sub (tr, tr, d); - } - - if (q) - { - mpz_swap (tq, q); - mpz_clear (tq); - } - if (r) - mpz_swap (tr, r); - - mpz_clear (tr); - - return rn != 0; - } -} - -void -mpz_cdiv_qr (mpz_t q, mpz_t r, const mpz_t n, const mpz_t d) -{ - mpz_div_qr (q, r, n, d, GMP_DIV_CEIL); -} - -void -mpz_fdiv_qr (mpz_t q, mpz_t r, const mpz_t n, const mpz_t d) -{ - mpz_div_qr (q, r, n, d, GMP_DIV_FLOOR); -} - -void -mpz_tdiv_qr (mpz_t q, mpz_t r, const mpz_t n, const mpz_t d) -{ - mpz_div_qr (q, r, n, d, GMP_DIV_TRUNC); -} - -void -mpz_cdiv_q (mpz_t q, const mpz_t n, const mpz_t d) -{ - mpz_div_qr (q, NULL, n, d, GMP_DIV_CEIL); -} - -void -mpz_fdiv_q (mpz_t q, const mpz_t n, const mpz_t d) -{ - mpz_div_qr (q, NULL, n, d, GMP_DIV_FLOOR); -} - -void -mpz_tdiv_q (mpz_t q, const mpz_t n, const mpz_t d) -{ - mpz_div_qr (q, NULL, n, d, GMP_DIV_TRUNC); -} - -void -mpz_cdiv_r (mpz_t r, const mpz_t n, const mpz_t d) -{ - mpz_div_qr (NULL, r, n, d, GMP_DIV_CEIL); -} - -void -mpz_fdiv_r (mpz_t r, const mpz_t n, const mpz_t d) -{ - mpz_div_qr (NULL, r, n, d, GMP_DIV_FLOOR); -} - -void -mpz_tdiv_r (mpz_t r, const mpz_t n, const mpz_t d) -{ - mpz_div_qr (NULL, r, n, d, GMP_DIV_TRUNC); -} - -void -mpz_mod (mpz_t r, const mpz_t n, const mpz_t d) -{ - mpz_div_qr (NULL, r, n, d, d->_mp_size >= 0 ? GMP_DIV_FLOOR : GMP_DIV_CEIL); -} - -static void -mpz_div_q_2exp (mpz_t q, const mpz_t u, mp_bitcnt_t bit_index, - enum mpz_div_round_mode mode) -{ - mp_size_t un, qn; - mp_size_t limb_cnt; - mp_ptr qp; - int adjust; - - un = u->_mp_size; - if (un == 0) - { - q->_mp_size = 0; - return; - } - limb_cnt = bit_index / GMP_LIMB_BITS; - qn = GMP_ABS (un) - limb_cnt; - bit_index %= GMP_LIMB_BITS; - - if (mode == ((un > 0) ? GMP_DIV_CEIL : GMP_DIV_FLOOR)) /* un != 0 here. */ - /* Note: Below, the final indexing at limb_cnt is valid because at - that point we have qn > 0. */ - adjust = (qn <= 0 - || !mpn_zero_p (u->_mp_d, limb_cnt) - || (u->_mp_d[limb_cnt] - & (((mp_limb_t) 1 << bit_index) - 1))); - else - adjust = 0; - - if (qn <= 0) - qn = 0; - else - { - qp = MPZ_REALLOC (q, qn); - - if (bit_index != 0) - { - mpn_rshift (qp, u->_mp_d + limb_cnt, qn, bit_index); - qn -= qp[qn - 1] == 0; - } - else - { - mpn_copyi (qp, u->_mp_d + limb_cnt, qn); - } - } - - q->_mp_size = qn; - - if (adjust) - mpz_add_ui (q, q, 1); - if (un < 0) - mpz_neg (q, q); -} - -static void -mpz_div_r_2exp (mpz_t r, const mpz_t u, mp_bitcnt_t bit_index, - enum mpz_div_round_mode mode) -{ - mp_size_t us, un, rn; - mp_ptr rp; - mp_limb_t mask; - - us = u->_mp_size; - if (us == 0 || bit_index == 0) - { - r->_mp_size = 0; - return; - } - rn = (bit_index + GMP_LIMB_BITS - 1) / GMP_LIMB_BITS; - assert (rn > 0); - - rp = MPZ_REALLOC (r, rn); - un = GMP_ABS (us); - - mask = GMP_LIMB_MAX >> (rn * GMP_LIMB_BITS - bit_index); - - if (rn > un) - { - /* Quotient (with truncation) is zero, and remainder is - non-zero */ - if (mode == ((us > 0) ? GMP_DIV_CEIL : GMP_DIV_FLOOR)) /* us != 0 here. */ - { - /* Have to negate and sign extend. */ - mp_size_t i; - - gmp_assert_nocarry (! mpn_neg (rp, u->_mp_d, un)); - for (i = un; i < rn - 1; i++) - rp[i] = GMP_LIMB_MAX; - - rp[rn-1] = mask; - us = -us; - } - else - { - /* Just copy */ - if (r != u) - mpn_copyi (rp, u->_mp_d, un); - - rn = un; - } - } - else - { - if (r != u) - mpn_copyi (rp, u->_mp_d, rn - 1); - - rp[rn-1] = u->_mp_d[rn-1] & mask; - - if (mode == ((us > 0) ? GMP_DIV_CEIL : GMP_DIV_FLOOR)) /* us != 0 here. */ - { - /* If r != 0, compute 2^{bit_count} - r. */ - mpn_neg (rp, rp, rn); - - rp[rn-1] &= mask; - - /* us is not used for anything else, so we can modify it - here to indicate flipped sign. */ - us = -us; - } - } - rn = mpn_normalized_size (rp, rn); - r->_mp_size = us < 0 ? -rn : rn; -} - -void -mpz_cdiv_q_2exp (mpz_t r, const mpz_t u, mp_bitcnt_t cnt) -{ - mpz_div_q_2exp (r, u, cnt, GMP_DIV_CEIL); -} - -void -mpz_fdiv_q_2exp (mpz_t r, const mpz_t u, mp_bitcnt_t cnt) -{ - mpz_div_q_2exp (r, u, cnt, GMP_DIV_FLOOR); -} - -void -mpz_tdiv_q_2exp (mpz_t r, const mpz_t u, mp_bitcnt_t cnt) -{ - mpz_div_q_2exp (r, u, cnt, GMP_DIV_TRUNC); -} - -void -mpz_cdiv_r_2exp (mpz_t r, const mpz_t u, mp_bitcnt_t cnt) -{ - mpz_div_r_2exp (r, u, cnt, GMP_DIV_CEIL); -} - -void -mpz_fdiv_r_2exp (mpz_t r, const mpz_t u, mp_bitcnt_t cnt) -{ - mpz_div_r_2exp (r, u, cnt, GMP_DIV_FLOOR); -} - -void -mpz_tdiv_r_2exp (mpz_t r, const mpz_t u, mp_bitcnt_t cnt) -{ - mpz_div_r_2exp (r, u, cnt, GMP_DIV_TRUNC); -} - -void -mpz_divexact (mpz_t q, const mpz_t n, const mpz_t d) -{ - gmp_assert_nocarry (mpz_div_qr (q, NULL, n, d, GMP_DIV_TRUNC)); -} - -int -mpz_divisible_p (const mpz_t n, const mpz_t d) -{ - return mpz_div_qr (NULL, NULL, n, d, GMP_DIV_TRUNC) == 0; -} - -int -mpz_congruent_p (const mpz_t a, const mpz_t b, const mpz_t m) -{ - mpz_t t; - int res; - - /* a == b (mod 0) iff a == b */ - if (mpz_sgn (m) == 0) - return (mpz_cmp (a, b) == 0); - - mpz_init (t); - mpz_sub (t, a, b); - res = mpz_divisible_p (t, m); - mpz_clear (t); - - return res; -} - -static unsigned long -mpz_div_qr_ui (mpz_t q, mpz_t r, - const mpz_t n, unsigned long d, enum mpz_div_round_mode mode) -{ - unsigned long ret; - mpz_t rr, dd; - - mpz_init (rr); - mpz_init_set_ui (dd, d); - mpz_div_qr (q, rr, n, dd, mode); - mpz_clear (dd); - ret = mpz_get_ui (rr); - - if (r) - mpz_swap (r, rr); - mpz_clear (rr); - - return ret; -} - -unsigned long -mpz_cdiv_qr_ui (mpz_t q, mpz_t r, const mpz_t n, unsigned long d) -{ - return mpz_div_qr_ui (q, r, n, d, GMP_DIV_CEIL); -} - -unsigned long -mpz_fdiv_qr_ui (mpz_t q, mpz_t r, const mpz_t n, unsigned long d) -{ - return mpz_div_qr_ui (q, r, n, d, GMP_DIV_FLOOR); -} - -unsigned long -mpz_tdiv_qr_ui (mpz_t q, mpz_t r, const mpz_t n, unsigned long d) -{ - return mpz_div_qr_ui (q, r, n, d, GMP_DIV_TRUNC); -} - -unsigned long -mpz_cdiv_q_ui (mpz_t q, const mpz_t n, unsigned long d) -{ - return mpz_div_qr_ui (q, NULL, n, d, GMP_DIV_CEIL); -} - -unsigned long -mpz_fdiv_q_ui (mpz_t q, const mpz_t n, unsigned long d) -{ - return mpz_div_qr_ui (q, NULL, n, d, GMP_DIV_FLOOR); -} - -unsigned long -mpz_tdiv_q_ui (mpz_t q, const mpz_t n, unsigned long d) -{ - return mpz_div_qr_ui (q, NULL, n, d, GMP_DIV_TRUNC); -} - -unsigned long -mpz_cdiv_r_ui (mpz_t r, const mpz_t n, unsigned long d) -{ - return mpz_div_qr_ui (NULL, r, n, d, GMP_DIV_CEIL); -} -unsigned long -mpz_fdiv_r_ui (mpz_t r, const mpz_t n, unsigned long d) -{ - return mpz_div_qr_ui (NULL, r, n, d, GMP_DIV_FLOOR); -} -unsigned long -mpz_tdiv_r_ui (mpz_t r, const mpz_t n, unsigned long d) -{ - return mpz_div_qr_ui (NULL, r, n, d, GMP_DIV_TRUNC); -} - -unsigned long -mpz_cdiv_ui (const mpz_t n, unsigned long d) -{ - return mpz_div_qr_ui (NULL, NULL, n, d, GMP_DIV_CEIL); -} - -unsigned long -mpz_fdiv_ui (const mpz_t n, unsigned long d) -{ - return mpz_div_qr_ui (NULL, NULL, n, d, GMP_DIV_FLOOR); -} - -unsigned long -mpz_tdiv_ui (const mpz_t n, unsigned long d) -{ - return mpz_div_qr_ui (NULL, NULL, n, d, GMP_DIV_TRUNC); -} - -unsigned long -mpz_mod_ui (mpz_t r, const mpz_t n, unsigned long d) -{ - return mpz_div_qr_ui (NULL, r, n, d, GMP_DIV_FLOOR); -} - -void -mpz_divexact_ui (mpz_t q, const mpz_t n, unsigned long d) -{ - gmp_assert_nocarry (mpz_div_qr_ui (q, NULL, n, d, GMP_DIV_TRUNC)); -} - -int -mpz_divisible_ui_p (const mpz_t n, unsigned long d) -{ - return mpz_div_qr_ui (NULL, NULL, n, d, GMP_DIV_TRUNC) == 0; -} - - -/* GCD */ -static mp_limb_t -mpn_gcd_11 (mp_limb_t u, mp_limb_t v) -{ - unsigned shift; - - assert ( (u | v) > 0); - - if (u == 0) - return v; - else if (v == 0) - return u; - - gmp_ctz (shift, u | v); - - u >>= shift; - v >>= shift; - - if ( (u & 1) == 0) - MP_LIMB_T_SWAP (u, v); - - while ( (v & 1) == 0) - v >>= 1; - - while (u != v) - { - if (u > v) - { - u -= v; - do - u >>= 1; - while ( (u & 1) == 0); - } - else - { - v -= u; - do - v >>= 1; - while ( (v & 1) == 0); - } - } - return u << shift; -} - -unsigned long -mpz_gcd_ui (mpz_t g, const mpz_t u, unsigned long v) -{ - mpz_t t; - mpz_init_set_ui(t, v); - mpz_gcd (t, u, t); - if (v > 0) - v = mpz_get_ui (t); - - if (g) - mpz_swap (t, g); - - mpz_clear (t); - - return v; -} - -static mp_bitcnt_t -mpz_make_odd (mpz_t r) -{ - mp_bitcnt_t shift; - - assert (r->_mp_size > 0); - /* Count trailing zeros, equivalent to mpn_scan1, because we know that there is a 1 */ - shift = mpn_common_scan (r->_mp_d[0], 0, r->_mp_d, 0, 0); - mpz_tdiv_q_2exp (r, r, shift); - - return shift; -} - -void -mpz_gcd (mpz_t g, const mpz_t u, const mpz_t v) -{ - mpz_t tu, tv; - mp_bitcnt_t uz, vz, gz; - - if (u->_mp_size == 0) - { - mpz_abs (g, v); - return; - } - if (v->_mp_size == 0) - { - mpz_abs (g, u); - return; - } - - mpz_init (tu); - mpz_init (tv); - - mpz_abs (tu, u); - uz = mpz_make_odd (tu); - mpz_abs (tv, v); - vz = mpz_make_odd (tv); - gz = GMP_MIN (uz, vz); - - if (tu->_mp_size < tv->_mp_size) - mpz_swap (tu, tv); - - mpz_tdiv_r (tu, tu, tv); - if (tu->_mp_size == 0) - { - mpz_swap (g, tv); - } - else - for (;;) - { - int c; - - mpz_make_odd (tu); - c = mpz_cmp (tu, tv); - if (c == 0) - { - mpz_swap (g, tu); - break; - } - if (c < 0) - mpz_swap (tu, tv); - - if (tv->_mp_size == 1) - { - mp_limb_t vl = tv->_mp_d[0]; - mp_limb_t ul = mpz_tdiv_ui (tu, vl); - mpz_set_ui (g, mpn_gcd_11 (ul, vl)); - break; - } - mpz_sub (tu, tu, tv); - } - mpz_clear (tu); - mpz_clear (tv); - mpz_mul_2exp (g, g, gz); -} - -void -mpz_gcdext (mpz_t g, mpz_t s, mpz_t t, const mpz_t u, const mpz_t v) -{ - mpz_t tu, tv, s0, s1, t0, t1; - mp_bitcnt_t uz, vz, gz; - mp_bitcnt_t power; - - if (u->_mp_size == 0) - { - /* g = 0 u + sgn(v) v */ - signed long sign = mpz_sgn (v); - mpz_abs (g, v); - if (s) - s->_mp_size = 0; - if (t) - mpz_set_si (t, sign); - return; - } - - if (v->_mp_size == 0) - { - /* g = sgn(u) u + 0 v */ - signed long sign = mpz_sgn (u); - mpz_abs (g, u); - if (s) - mpz_set_si (s, sign); - if (t) - t->_mp_size = 0; - return; - } - - mpz_init (tu); - mpz_init (tv); - mpz_init (s0); - mpz_init (s1); - mpz_init (t0); - mpz_init (t1); - - mpz_abs (tu, u); - uz = mpz_make_odd (tu); - mpz_abs (tv, v); - vz = mpz_make_odd (tv); - gz = GMP_MIN (uz, vz); - - uz -= gz; - vz -= gz; - - /* Cofactors corresponding to odd gcd. gz handled later. */ - if (tu->_mp_size < tv->_mp_size) - { - mpz_swap (tu, tv); - MPZ_SRCPTR_SWAP (u, v); - MPZ_PTR_SWAP (s, t); - MP_BITCNT_T_SWAP (uz, vz); - } - - /* Maintain - * - * u = t0 tu + t1 tv - * v = s0 tu + s1 tv - * - * where u and v denote the inputs with common factors of two - * eliminated, and det (s0, t0; s1, t1) = 2^p. Then - * - * 2^p tu = s1 u - t1 v - * 2^p tv = -s0 u + t0 v - */ - - /* After initial division, tu = q tv + tu', we have - * - * u = 2^uz (tu' + q tv) - * v = 2^vz tv - * - * or - * - * t0 = 2^uz, t1 = 2^uz q - * s0 = 0, s1 = 2^vz - */ - - mpz_setbit (t0, uz); - mpz_tdiv_qr (t1, tu, tu, tv); - mpz_mul_2exp (t1, t1, uz); - - mpz_setbit (s1, vz); - power = uz + vz; - - if (tu->_mp_size > 0) - { - mp_bitcnt_t shift; - shift = mpz_make_odd (tu); - mpz_mul_2exp (t0, t0, shift); - mpz_mul_2exp (s0, s0, shift); - power += shift; - - for (;;) - { - int c; - c = mpz_cmp (tu, tv); - if (c == 0) - break; - - if (c < 0) - { - /* tv = tv' + tu - * - * u = t0 tu + t1 (tv' + tu) = (t0 + t1) tu + t1 tv' - * v = s0 tu + s1 (tv' + tu) = (s0 + s1) tu + s1 tv' */ - - mpz_sub (tv, tv, tu); - mpz_add (t0, t0, t1); - mpz_add (s0, s0, s1); - - shift = mpz_make_odd (tv); - mpz_mul_2exp (t1, t1, shift); - mpz_mul_2exp (s1, s1, shift); - } - else - { - mpz_sub (tu, tu, tv); - mpz_add (t1, t0, t1); - mpz_add (s1, s0, s1); - - shift = mpz_make_odd (tu); - mpz_mul_2exp (t0, t0, shift); - mpz_mul_2exp (s0, s0, shift); - } - power += shift; - } - } - - /* Now tv = odd part of gcd, and -s0 and t0 are corresponding - cofactors. */ - - mpz_mul_2exp (tv, tv, gz); - mpz_neg (s0, s0); - - /* 2^p g = s0 u + t0 v. Eliminate one factor of two at a time. To - adjust cofactors, we need u / g and v / g */ - - mpz_divexact (s1, v, tv); - mpz_abs (s1, s1); - mpz_divexact (t1, u, tv); - mpz_abs (t1, t1); - - while (power-- > 0) - { - /* s0 u + t0 v = (s0 - v/g) u - (t0 + u/g) v */ - if (mpz_odd_p (s0) || mpz_odd_p (t0)) - { - mpz_sub (s0, s0, s1); - mpz_add (t0, t0, t1); - } - assert (mpz_even_p (t0) && mpz_even_p (s0)); - mpz_tdiv_q_2exp (s0, s0, 1); - mpz_tdiv_q_2exp (t0, t0, 1); - } - - /* Arrange so that |s| < |u| / 2g */ - mpz_add (s1, s0, s1); - if (mpz_cmpabs (s0, s1) > 0) - { - mpz_swap (s0, s1); - mpz_sub (t0, t0, t1); - } - if (u->_mp_size < 0) - mpz_neg (s0, s0); - if (v->_mp_size < 0) - mpz_neg (t0, t0); - - mpz_swap (g, tv); - if (s) - mpz_swap (s, s0); - if (t) - mpz_swap (t, t0); - - mpz_clear (tu); - mpz_clear (tv); - mpz_clear (s0); - mpz_clear (s1); - mpz_clear (t0); - mpz_clear (t1); -} - -void -mpz_lcm (mpz_t r, const mpz_t u, const mpz_t v) -{ - mpz_t g; - - if (u->_mp_size == 0 || v->_mp_size == 0) - { - r->_mp_size = 0; - return; - } - - mpz_init (g); - - mpz_gcd (g, u, v); - mpz_divexact (g, u, g); - mpz_mul (r, g, v); - - mpz_clear (g); - mpz_abs (r, r); -} - -void -mpz_lcm_ui (mpz_t r, const mpz_t u, unsigned long v) -{ - if (v == 0 || u->_mp_size == 0) - { - r->_mp_size = 0; - return; - } - - v /= mpz_gcd_ui (NULL, u, v); - mpz_mul_ui (r, u, v); - - mpz_abs (r, r); -} - -int -mpz_invert (mpz_t r, const mpz_t u, const mpz_t m) -{ - mpz_t g, tr; - int invertible; - - if (u->_mp_size == 0 || mpz_cmpabs_ui (m, 1) <= 0) - return 0; - - mpz_init (g); - mpz_init (tr); - - mpz_gcdext (g, tr, NULL, u, m); - invertible = (mpz_cmp_ui (g, 1) == 0); - - if (invertible) - { - if (tr->_mp_size < 0) - { - if (m->_mp_size >= 0) - mpz_add (tr, tr, m); - else - mpz_sub (tr, tr, m); - } - mpz_swap (r, tr); - } - - mpz_clear (g); - mpz_clear (tr); - return invertible; -} - - -/* Higher level operations (sqrt, pow and root) */ - -void -mpz_pow_ui (mpz_t r, const mpz_t b, unsigned long e) -{ - unsigned long bit; - mpz_t tr; - mpz_init_set_ui (tr, 1); - - bit = GMP_ULONG_HIGHBIT; - do - { - mpz_mul (tr, tr, tr); - if (e & bit) - mpz_mul (tr, tr, b); - bit >>= 1; - } - while (bit > 0); - - mpz_swap (r, tr); - mpz_clear (tr); -} - -void -mpz_ui_pow_ui (mpz_t r, unsigned long blimb, unsigned long e) -{ - mpz_t b; - - mpz_init_set_ui (b, blimb); - mpz_pow_ui (r, b, e); - mpz_clear (b); -} - -void -mpz_powm (mpz_t r, const mpz_t b, const mpz_t e, const mpz_t m) -{ - mpz_t tr; - mpz_t base; - mp_size_t en, mn; - mp_srcptr mp; - struct gmp_div_inverse minv; - unsigned shift; - mp_ptr tp = NULL; - - en = GMP_ABS (e->_mp_size); - mn = GMP_ABS (m->_mp_size); - if (mn == 0) - gmp_die ("mpz_powm: Zero modulo."); - - if (en == 0) - { - mpz_set_ui (r, 1); - return; - } - - mp = m->_mp_d; - mpn_div_qr_invert (&minv, mp, mn); - shift = minv.shift; - - if (shift > 0) - { - /* To avoid shifts, we do all our reductions, except the final - one, using a *normalized* m. */ - minv.shift = 0; - - tp = gmp_xalloc_limbs (mn); - gmp_assert_nocarry (mpn_lshift (tp, mp, mn, shift)); - mp = tp; - } - - mpz_init (base); - - if (e->_mp_size < 0) - { - if (!mpz_invert (base, b, m)) - gmp_die ("mpz_powm: Negative exponent and non-invertible base."); - } - else - { - mp_size_t bn; - mpz_abs (base, b); - - bn = base->_mp_size; - if (bn >= mn) - { - mpn_div_qr_preinv (NULL, base->_mp_d, base->_mp_size, mp, mn, &minv); - bn = mn; - } - - /* We have reduced the absolute value. Now take care of the - sign. Note that we get zero represented non-canonically as - m. */ - if (b->_mp_size < 0) - { - mp_ptr bp = MPZ_REALLOC (base, mn); - gmp_assert_nocarry (mpn_sub (bp, mp, mn, bp, bn)); - bn = mn; - } - base->_mp_size = mpn_normalized_size (base->_mp_d, bn); - } - mpz_init_set_ui (tr, 1); - - while (--en >= 0) - { - mp_limb_t w = e->_mp_d[en]; - mp_limb_t bit; - - bit = GMP_LIMB_HIGHBIT; - do - { - mpz_mul (tr, tr, tr); - if (w & bit) - mpz_mul (tr, tr, base); - if (tr->_mp_size > mn) - { - mpn_div_qr_preinv (NULL, tr->_mp_d, tr->_mp_size, mp, mn, &minv); - tr->_mp_size = mpn_normalized_size (tr->_mp_d, mn); - } - bit >>= 1; - } - while (bit > 0); - } - - /* Final reduction */ - if (tr->_mp_size >= mn) - { - minv.shift = shift; - mpn_div_qr_preinv (NULL, tr->_mp_d, tr->_mp_size, mp, mn, &minv); - tr->_mp_size = mpn_normalized_size (tr->_mp_d, mn); - } - if (tp) - gmp_free (tp); - - mpz_swap (r, tr); - mpz_clear (tr); - mpz_clear (base); -} - -void -mpz_powm_ui (mpz_t r, const mpz_t b, unsigned long elimb, const mpz_t m) -{ - mpz_t e; - - mpz_init_set_ui (e, elimb); - mpz_powm (r, b, e, m); - mpz_clear (e); -} - -/* x=trunc(y^(1/z)), r=y-x^z */ -void -mpz_rootrem (mpz_t x, mpz_t r, const mpz_t y, unsigned long z) -{ - int sgn; - mpz_t t, u; - - sgn = y->_mp_size < 0; - if ((~z & sgn) != 0) - gmp_die ("mpz_rootrem: Negative argument, with even root."); - if (z == 0) - gmp_die ("mpz_rootrem: Zeroth root."); - - if (mpz_cmpabs_ui (y, 1) <= 0) { - if (x) - mpz_set (x, y); - if (r) - r->_mp_size = 0; - return; - } - - mpz_init (u); - mpz_init (t); - mpz_setbit (t, mpz_sizeinbase (y, 2) / z + 1); - - if (z == 2) /* simplify sqrt loop: z-1 == 1 */ - do { - mpz_swap (u, t); /* u = x */ - mpz_tdiv_q (t, y, u); /* t = y/x */ - mpz_add (t, t, u); /* t = y/x + x */ - mpz_tdiv_q_2exp (t, t, 1); /* x'= (y/x + x)/2 */ - } while (mpz_cmpabs (t, u) < 0); /* |x'| < |x| */ - else /* z != 2 */ { - mpz_t v; - - mpz_init (v); - if (sgn) - mpz_neg (t, t); - - do { - mpz_swap (u, t); /* u = x */ - mpz_pow_ui (t, u, z - 1); /* t = x^(z-1) */ - mpz_tdiv_q (t, y, t); /* t = y/x^(z-1) */ - mpz_mul_ui (v, u, z - 1); /* v = x*(z-1) */ - mpz_add (t, t, v); /* t = y/x^(z-1) + x*(z-1) */ - mpz_tdiv_q_ui (t, t, z); /* x'=(y/x^(z-1) + x*(z-1))/z */ - } while (mpz_cmpabs (t, u) < 0); /* |x'| < |x| */ - - mpz_clear (v); - } - - if (r) { - mpz_pow_ui (t, u, z); - mpz_sub (r, y, t); - } - if (x) - mpz_swap (x, u); - mpz_clear (u); - mpz_clear (t); -} - -int -mpz_root (mpz_t x, const mpz_t y, unsigned long z) -{ - int res; - mpz_t r; - - mpz_init (r); - mpz_rootrem (x, r, y, z); - res = r->_mp_size == 0; - mpz_clear (r); - - return res; -} - -/* Compute s = floor(sqrt(u)) and r = u - s^2. Allows r == NULL */ -void -mpz_sqrtrem (mpz_t s, mpz_t r, const mpz_t u) -{ - mpz_rootrem (s, r, u, 2); -} - -void -mpz_sqrt (mpz_t s, const mpz_t u) -{ - mpz_rootrem (s, NULL, u, 2); -} - -int -mpz_perfect_square_p (const mpz_t u) -{ - if (u->_mp_size <= 0) - return (u->_mp_size == 0); - else - return mpz_root (NULL, u, 2); -} - -int -mpn_perfect_square_p (mp_srcptr p, mp_size_t n) -{ - mpz_t t; - - assert (n > 0); - assert (p [n-1] != 0); - return mpz_root (NULL, mpz_roinit_normal_n (t, p, n), 2); -} - -mp_size_t -mpn_sqrtrem (mp_ptr sp, mp_ptr rp, mp_srcptr p, mp_size_t n) -{ - mpz_t s, r, u; - mp_size_t res; - - assert (n > 0); - assert (p [n-1] != 0); - - mpz_init (r); - mpz_init (s); - mpz_rootrem (s, r, mpz_roinit_normal_n (u, p, n), 2); - - assert (s->_mp_size == (n+1)/2); - mpn_copyd (sp, s->_mp_d, s->_mp_size); - mpz_clear (s); - res = r->_mp_size; - if (rp) - mpn_copyd (rp, r->_mp_d, res); - mpz_clear (r); - return res; -} - -/* Combinatorics */ - -void -mpz_mfac_uiui (mpz_t x, unsigned long n, unsigned long m) -{ - mpz_set_ui (x, n + (n == 0)); - if (m + 1 < 2) return; - while (n > m + 1) - mpz_mul_ui (x, x, n -= m); -} - -void -mpz_2fac_ui (mpz_t x, unsigned long n) -{ - mpz_mfac_uiui (x, n, 2); -} - -void -mpz_fac_ui (mpz_t x, unsigned long n) -{ - mpz_mfac_uiui (x, n, 1); -} - -void -mpz_bin_uiui (mpz_t r, unsigned long n, unsigned long k) -{ - mpz_t t; - - mpz_set_ui (r, k <= n); - - if (k > (n >> 1)) - k = (k <= n) ? n - k : 0; - - mpz_init (t); - mpz_fac_ui (t, k); - - for (; k > 0; --k) - mpz_mul_ui (r, r, n--); - - mpz_divexact (r, r, t); - mpz_clear (t); -} - - -/* Primality testing */ - -/* Computes Kronecker (a/b) with odd b, a!=0 and GCD(a,b) = 1 */ -/* Adapted from JACOBI_BASE_METHOD==4 in mpn/generic/jacbase.c */ -static int -gmp_jacobi_coprime (mp_limb_t a, mp_limb_t b) -{ - int c, bit = 0; - - assert (b & 1); - assert (a != 0); - /* assert (mpn_gcd_11 (a, b) == 1); */ - - /* Below, we represent a and b shifted right so that the least - significant one bit is implicit. */ - b >>= 1; - - gmp_ctz(c, a); - a >>= 1; - - do - { - a >>= c; - /* (2/b) = -1 if b = 3 or 5 mod 8 */ - bit ^= c & (b ^ (b >> 1)); - if (a < b) - { - bit ^= a & b; - a = b - a; - b -= a; - } - else - { - a -= b; - assert (a != 0); - } - - gmp_ctz(c, a); - ++c; - } - while (b > 0); - - return bit & 1 ? -1 : 1; -} - -static void -gmp_lucas_step_k_2k (mpz_t V, mpz_t Qk, const mpz_t n) -{ - mpz_mod (Qk, Qk, n); - /* V_{2k} <- V_k ^ 2 - 2Q^k */ - mpz_mul (V, V, V); - mpz_submul_ui (V, Qk, 2); - mpz_tdiv_r (V, V, n); - /* Q^{2k} = (Q^k)^2 */ - mpz_mul (Qk, Qk, Qk); -} - -/* Computes V_k, Q^k (mod n) for the Lucas' sequence */ -/* with P=1, Q=Q; k = (n>>b0)|1. */ -/* Requires an odd n > 4; b0 > 0; -2*Q must not overflow a long */ -/* Returns (U_k == 0) and sets V=V_k and Qk=Q^k. */ -static int -gmp_lucas_mod (mpz_t V, mpz_t Qk, long Q, - mp_bitcnt_t b0, const mpz_t n) -{ - mp_bitcnt_t bs; - mpz_t U; - int res; - - assert (b0 > 0); - assert (Q <= - (LONG_MIN / 2)); - assert (Q >= - (LONG_MAX / 2)); - assert (mpz_cmp_ui (n, 4) > 0); - assert (mpz_odd_p (n)); - - mpz_init_set_ui (U, 1); /* U1 = 1 */ - mpz_set_ui (V, 1); /* V1 = 1 */ - mpz_set_si (Qk, Q); - - for (bs = mpz_sizeinbase (n, 2) - 1; --bs >= b0;) - { - /* U_{2k} <- U_k * V_k */ - mpz_mul (U, U, V); - /* V_{2k} <- V_k ^ 2 - 2Q^k */ - /* Q^{2k} = (Q^k)^2 */ - gmp_lucas_step_k_2k (V, Qk, n); - - /* A step k->k+1 is performed if the bit in $n$ is 1 */ - /* mpz_tstbit(n,bs) or the bit is 0 in $n$ but */ - /* should be 1 in $n+1$ (bs == b0) */ - if (b0 == bs || mpz_tstbit (n, bs)) - { - /* Q^{k+1} <- Q^k * Q */ - mpz_mul_si (Qk, Qk, Q); - /* U_{k+1} <- (U_k + V_k) / 2 */ - mpz_swap (U, V); /* Keep in V the old value of U_k */ - mpz_add (U, U, V); - /* We have to compute U/2, so we need an even value, */ - /* equivalent (mod n) */ - if (mpz_odd_p (U)) - mpz_add (U, U, n); - mpz_tdiv_q_2exp (U, U, 1); - /* V_{k+1} <-(D*U_k + V_k) / 2 = - U_{k+1} + (D-1)/2*U_k = U_{k+1} - 2Q*U_k */ - mpz_mul_si (V, V, -2*Q); - mpz_add (V, U, V); - mpz_tdiv_r (V, V, n); - } - mpz_tdiv_r (U, U, n); - } - - res = U->_mp_size == 0; - mpz_clear (U); - return res; -} - -/* Performs strong Lucas' test on x, with parameters suggested */ -/* for the BPSW test. Qk is only passed to recycle a variable. */ -/* Requires GCD (x,6) = 1.*/ -static int -gmp_stronglucas (const mpz_t x, mpz_t Qk) -{ - mp_bitcnt_t b0; - mpz_t V, n; - mp_limb_t maxD, D; /* The absolute value is stored. */ - long Q; - mp_limb_t tl; - - /* Test on the absolute value. */ - mpz_roinit_normal_n (n, x->_mp_d, GMP_ABS (x->_mp_size)); - - assert (mpz_odd_p (n)); - /* assert (mpz_gcd_ui (NULL, n, 6) == 1); */ - if (mpz_root (Qk, n, 2)) - return 0; /* A square is composite. */ - - /* Check Ds up to square root (in case, n is prime) - or avoid overflows */ - maxD = (Qk->_mp_size == 1) ? Qk->_mp_d [0] - 1 : GMP_LIMB_MAX; - - D = 3; - /* Search a D such that (D/n) = -1 in the sequence 5,-7,9,-11,.. */ - /* For those Ds we have (D/n) = (n/|D|) */ - do - { - if (D >= maxD) - return 1 + (D != GMP_LIMB_MAX); /* (1 + ! ~ D) */ - D += 2; - tl = mpz_tdiv_ui (n, D); - if (tl == 0) - return 0; - } - while (gmp_jacobi_coprime (tl, D) == 1); - - mpz_init (V); - - /* n-(D/n) = n+1 = d*2^{b0}, with d = (n>>b0) | 1 */ - b0 = mpz_scan0 (n, 0); - - /* D= P^2 - 4Q; P = 1; Q = (1-D)/4 */ - Q = (D & 2) ? (long) (D >> 2) + 1 : -(long) (D >> 2); - - if (! gmp_lucas_mod (V, Qk, Q, b0, n)) /* If Ud != 0 */ - while (V->_mp_size != 0 && --b0 != 0) /* while Vk != 0 */ - /* V <- V ^ 2 - 2Q^k */ - /* Q^{2k} = (Q^k)^2 */ - gmp_lucas_step_k_2k (V, Qk, n); - - mpz_clear (V); - return (b0 != 0); -} - -static int -gmp_millerrabin (const mpz_t n, const mpz_t nm1, mpz_t y, - const mpz_t q, mp_bitcnt_t k) -{ - assert (k > 0); - - /* Caller must initialize y to the base. */ - mpz_powm (y, y, q, n); - - if (mpz_cmp_ui (y, 1) == 0 || mpz_cmp (y, nm1) == 0) - return 1; - - while (--k > 0) - { - mpz_powm_ui (y, y, 2, n); - if (mpz_cmp (y, nm1) == 0) - return 1; - /* y == 1 means that the previous y was a non-trivial square root - of 1 (mod n). y == 0 means that n is a power of the base. - In either case, n is not prime. */ - if (mpz_cmp_ui (y, 1) <= 0) - return 0; - } - return 0; -} - -/* This product is 0xc0cfd797, and fits in 32 bits. */ -#define GMP_PRIME_PRODUCT \ - (3UL*5UL*7UL*11UL*13UL*17UL*19UL*23UL*29UL) - -/* Bit (p+1)/2 is set, for each odd prime <= 61 */ -#define GMP_PRIME_MASK 0xc96996dcUL - -int -mpz_probab_prime_p (const mpz_t n, int reps) -{ - mpz_t nm1; - mpz_t q; - mpz_t y; - mp_bitcnt_t k; - int is_prime; - int j; - - /* Note that we use the absolute value of n only, for compatibility - with the real GMP. */ - if (mpz_even_p (n)) - return (mpz_cmpabs_ui (n, 2) == 0) ? 2 : 0; - - /* Above test excludes n == 0 */ - assert (n->_mp_size != 0); - - if (mpz_cmpabs_ui (n, 64) < 0) - return (GMP_PRIME_MASK >> (n->_mp_d[0] >> 1)) & 2; - - if (mpz_gcd_ui (NULL, n, GMP_PRIME_PRODUCT) != 1) - return 0; - - /* All prime factors are >= 31. */ - if (mpz_cmpabs_ui (n, 31*31) < 0) - return 2; - - mpz_init (nm1); - mpz_init (q); - - /* Find q and k, where q is odd and n = 1 + 2**k * q. */ - mpz_abs (nm1, n); - nm1->_mp_d[0] -= 1; - k = mpz_scan1 (nm1, 0); - mpz_tdiv_q_2exp (q, nm1, k); - - /* BPSW test */ - mpz_init_set_ui (y, 2); - is_prime = gmp_millerrabin (n, nm1, y, q, k) && gmp_stronglucas (n, y); - reps -= 24; /* skip the first 24 repetitions */ - - /* Use Miller-Rabin, with a deterministic sequence of bases, a[j] = - j^2 + j + 41 using Euler's polynomial. We potentially stop early, - if a[j] >= n - 1. Since n >= 31*31, this can happen only if reps > - 30 (a[30] == 971 > 31*31 == 961). */ - - for (j = 0; is_prime & (j < reps); j++) - { - mpz_set_ui (y, (unsigned long) j*j+j+41); - if (mpz_cmp (y, nm1) >= 0) - { - /* Don't try any further bases. This "early" break does not affect - the result for any reasonable reps value (<=5000 was tested) */ - assert (j >= 30); - break; - } - is_prime = gmp_millerrabin (n, nm1, y, q, k); - } - mpz_clear (nm1); - mpz_clear (q); - mpz_clear (y); - - return is_prime; -} - - -/* Logical operations and bit manipulation. */ - -/* Numbers are treated as if represented in two's complement (and - infinitely sign extended). For a negative values we get the two's - complement from -x = ~x + 1, where ~ is bitwise complement. - Negation transforms - - xxxx10...0 - - into - - yyyy10...0 - - where yyyy is the bitwise complement of xxxx. So least significant - bits, up to and including the first one bit, are unchanged, and - the more significant bits are all complemented. - - To change a bit from zero to one in a negative number, subtract the - corresponding power of two from the absolute value. This can never - underflow. To change a bit from one to zero, add the corresponding - power of two, and this might overflow. E.g., if x = -001111, the - two's complement is 110001. Clearing the least significant bit, we - get two's complement 110000, and -010000. */ - -int -mpz_tstbit (const mpz_t d, mp_bitcnt_t bit_index) -{ - mp_size_t limb_index; - unsigned shift; - mp_size_t ds; - mp_size_t dn; - mp_limb_t w; - int bit; - - ds = d->_mp_size; - dn = GMP_ABS (ds); - limb_index = bit_index / GMP_LIMB_BITS; - if (limb_index >= dn) - return ds < 0; - - shift = bit_index % GMP_LIMB_BITS; - w = d->_mp_d[limb_index]; - bit = (w >> shift) & 1; - - if (ds < 0) - { - /* d < 0. Check if any of the bits below is set: If so, our bit - must be complemented. */ - if (shift > 0 && (mp_limb_t) (w << (GMP_LIMB_BITS - shift)) > 0) - return bit ^ 1; - while (--limb_index >= 0) - if (d->_mp_d[limb_index] > 0) - return bit ^ 1; - } - return bit; -} - -static void -mpz_abs_add_bit (mpz_t d, mp_bitcnt_t bit_index) -{ - mp_size_t dn, limb_index; - mp_limb_t bit; - mp_ptr dp; - - dn = GMP_ABS (d->_mp_size); - - limb_index = bit_index / GMP_LIMB_BITS; - bit = (mp_limb_t) 1 << (bit_index % GMP_LIMB_BITS); - - if (limb_index >= dn) - { - mp_size_t i; - /* The bit should be set outside of the end of the number. - We have to increase the size of the number. */ - dp = MPZ_REALLOC (d, limb_index + 1); - - dp[limb_index] = bit; - for (i = dn; i < limb_index; i++) - dp[i] = 0; - dn = limb_index + 1; - } - else - { - mp_limb_t cy; - - dp = d->_mp_d; - - cy = mpn_add_1 (dp + limb_index, dp + limb_index, dn - limb_index, bit); - if (cy > 0) - { - dp = MPZ_REALLOC (d, dn + 1); - dp[dn++] = cy; - } - } - - d->_mp_size = (d->_mp_size < 0) ? - dn : dn; -} - -static void -mpz_abs_sub_bit (mpz_t d, mp_bitcnt_t bit_index) -{ - mp_size_t dn, limb_index; - mp_ptr dp; - mp_limb_t bit; - - dn = GMP_ABS (d->_mp_size); - dp = d->_mp_d; - - limb_index = bit_index / GMP_LIMB_BITS; - bit = (mp_limb_t) 1 << (bit_index % GMP_LIMB_BITS); - - assert (limb_index < dn); - - gmp_assert_nocarry (mpn_sub_1 (dp + limb_index, dp + limb_index, - dn - limb_index, bit)); - dn = mpn_normalized_size (dp, dn); - d->_mp_size = (d->_mp_size < 0) ? - dn : dn; -} - -void -mpz_setbit (mpz_t d, mp_bitcnt_t bit_index) -{ - if (!mpz_tstbit (d, bit_index)) - { - if (d->_mp_size >= 0) - mpz_abs_add_bit (d, bit_index); - else - mpz_abs_sub_bit (d, bit_index); - } -} - -void -mpz_clrbit (mpz_t d, mp_bitcnt_t bit_index) -{ - if (mpz_tstbit (d, bit_index)) - { - if (d->_mp_size >= 0) - mpz_abs_sub_bit (d, bit_index); - else - mpz_abs_add_bit (d, bit_index); - } -} - -void -mpz_combit (mpz_t d, mp_bitcnt_t bit_index) -{ - if (mpz_tstbit (d, bit_index) ^ (d->_mp_size < 0)) - mpz_abs_sub_bit (d, bit_index); - else - mpz_abs_add_bit (d, bit_index); -} - -void -mpz_com (mpz_t r, const mpz_t u) -{ - mpz_add_ui (r, u, 1); - mpz_neg (r, r); -} - -void -mpz_and (mpz_t r, const mpz_t u, const mpz_t v) -{ - mp_size_t un, vn, rn, i; - mp_ptr up, vp, rp; - - mp_limb_t ux, vx, rx; - mp_limb_t uc, vc, rc; - mp_limb_t ul, vl, rl; - - un = GMP_ABS (u->_mp_size); - vn = GMP_ABS (v->_mp_size); - if (un < vn) - { - MPZ_SRCPTR_SWAP (u, v); - MP_SIZE_T_SWAP (un, vn); - } - if (vn == 0) - { - r->_mp_size = 0; - return; - } - - uc = u->_mp_size < 0; - vc = v->_mp_size < 0; - rc = uc & vc; - - ux = -uc; - vx = -vc; - rx = -rc; - - /* If the smaller input is positive, higher limbs don't matter. */ - rn = vx ? un : vn; - - rp = MPZ_REALLOC (r, rn + (mp_size_t) rc); - - up = u->_mp_d; - vp = v->_mp_d; - - i = 0; - do - { - ul = (up[i] ^ ux) + uc; - uc = ul < uc; - - vl = (vp[i] ^ vx) + vc; - vc = vl < vc; - - rl = ( (ul & vl) ^ rx) + rc; - rc = rl < rc; - rp[i] = rl; - } - while (++i < vn); - assert (vc == 0); - - for (; i < rn; i++) - { - ul = (up[i] ^ ux) + uc; - uc = ul < uc; - - rl = ( (ul & vx) ^ rx) + rc; - rc = rl < rc; - rp[i] = rl; - } - if (rc) - rp[rn++] = rc; - else - rn = mpn_normalized_size (rp, rn); - - r->_mp_size = rx ? -rn : rn; -} - -void -mpz_ior (mpz_t r, const mpz_t u, const mpz_t v) -{ - mp_size_t un, vn, rn, i; - mp_ptr up, vp, rp; - - mp_limb_t ux, vx, rx; - mp_limb_t uc, vc, rc; - mp_limb_t ul, vl, rl; - - un = GMP_ABS (u->_mp_size); - vn = GMP_ABS (v->_mp_size); - if (un < vn) - { - MPZ_SRCPTR_SWAP (u, v); - MP_SIZE_T_SWAP (un, vn); - } - if (vn == 0) - { - mpz_set (r, u); - return; - } - - uc = u->_mp_size < 0; - vc = v->_mp_size < 0; - rc = uc | vc; - - ux = -uc; - vx = -vc; - rx = -rc; - - /* If the smaller input is negative, by sign extension higher limbs - don't matter. */ - rn = vx ? vn : un; - - rp = MPZ_REALLOC (r, rn + (mp_size_t) rc); - - up = u->_mp_d; - vp = v->_mp_d; - - i = 0; - do - { - ul = (up[i] ^ ux) + uc; - uc = ul < uc; - - vl = (vp[i] ^ vx) + vc; - vc = vl < vc; - - rl = ( (ul | vl) ^ rx) + rc; - rc = rl < rc; - rp[i] = rl; - } - while (++i < vn); - assert (vc == 0); - - for (; i < rn; i++) - { - ul = (up[i] ^ ux) + uc; - uc = ul < uc; - - rl = ( (ul | vx) ^ rx) + rc; - rc = rl < rc; - rp[i] = rl; - } - if (rc) - rp[rn++] = rc; - else - rn = mpn_normalized_size (rp, rn); - - r->_mp_size = rx ? -rn : rn; -} - -void -mpz_xor (mpz_t r, const mpz_t u, const mpz_t v) -{ - mp_size_t un, vn, i; - mp_ptr up, vp, rp; - - mp_limb_t ux, vx, rx; - mp_limb_t uc, vc, rc; - mp_limb_t ul, vl, rl; - - un = GMP_ABS (u->_mp_size); - vn = GMP_ABS (v->_mp_size); - if (un < vn) - { - MPZ_SRCPTR_SWAP (u, v); - MP_SIZE_T_SWAP (un, vn); - } - if (vn == 0) - { - mpz_set (r, u); - return; - } - - uc = u->_mp_size < 0; - vc = v->_mp_size < 0; - rc = uc ^ vc; - - ux = -uc; - vx = -vc; - rx = -rc; - - rp = MPZ_REALLOC (r, un + (mp_size_t) rc); - - up = u->_mp_d; - vp = v->_mp_d; - - i = 0; - do - { - ul = (up[i] ^ ux) + uc; - uc = ul < uc; - - vl = (vp[i] ^ vx) + vc; - vc = vl < vc; - - rl = (ul ^ vl ^ rx) + rc; - rc = rl < rc; - rp[i] = rl; - } - while (++i < vn); - assert (vc == 0); - - for (; i < un; i++) - { - ul = (up[i] ^ ux) + uc; - uc = ul < uc; - - rl = (ul ^ ux) + rc; - rc = rl < rc; - rp[i] = rl; - } - if (rc) - rp[un++] = rc; - else - un = mpn_normalized_size (rp, un); - - r->_mp_size = rx ? -un : un; -} - -static unsigned -gmp_popcount_limb (mp_limb_t x) -{ - unsigned c; - - /* Do 16 bits at a time, to avoid limb-sized constants. */ - int LOCAL_SHIFT_BITS = 16; - for (c = 0; x > 0;) - { - unsigned w = x - ((x >> 1) & 0x5555); - w = ((w >> 2) & 0x3333) + (w & 0x3333); - w = (w >> 4) + w; - w = ((w >> 8) & 0x000f) + (w & 0x000f); - c += w; - if (GMP_LIMB_BITS > LOCAL_SHIFT_BITS) - x >>= LOCAL_SHIFT_BITS; - else - x = 0; - } - return c; -} - -mp_bitcnt_t -mpn_popcount (mp_srcptr p, mp_size_t n) -{ - mp_size_t i; - mp_bitcnt_t c; - - for (c = 0, i = 0; i < n; i++) - c += gmp_popcount_limb (p[i]); - - return c; -} - -mp_bitcnt_t -mpz_popcount (const mpz_t u) -{ - mp_size_t un; - - un = u->_mp_size; - - if (un < 0) - return ~(mp_bitcnt_t) 0; - - return mpn_popcount (u->_mp_d, un); -} - -mp_bitcnt_t -mpz_hamdist (const mpz_t u, const mpz_t v) -{ - mp_size_t un, vn, i; - mp_limb_t uc, vc, ul, vl, comp; - mp_srcptr up, vp; - mp_bitcnt_t c; - - un = u->_mp_size; - vn = v->_mp_size; - - if ( (un ^ vn) < 0) - return ~(mp_bitcnt_t) 0; - - comp = - (uc = vc = (un < 0)); - if (uc) - { - assert (vn < 0); - un = -un; - vn = -vn; - } - - up = u->_mp_d; - vp = v->_mp_d; - - if (un < vn) - MPN_SRCPTR_SWAP (up, un, vp, vn); - - for (i = 0, c = 0; i < vn; i++) - { - ul = (up[i] ^ comp) + uc; - uc = ul < uc; - - vl = (vp[i] ^ comp) + vc; - vc = vl < vc; - - c += gmp_popcount_limb (ul ^ vl); - } - assert (vc == 0); - - for (; i < un; i++) - { - ul = (up[i] ^ comp) + uc; - uc = ul < uc; - - c += gmp_popcount_limb (ul ^ comp); - } - - return c; -} - -mp_bitcnt_t -mpz_scan1 (const mpz_t u, mp_bitcnt_t starting_bit) -{ - mp_ptr up; - mp_size_t us, un, i; - mp_limb_t limb, ux; - - us = u->_mp_size; - un = GMP_ABS (us); - i = starting_bit / GMP_LIMB_BITS; - - /* Past the end there's no 1 bits for u>=0, or an immediate 1 bit - for u<0. Notice this test picks up any u==0 too. */ - if (i >= un) - return (us >= 0 ? ~(mp_bitcnt_t) 0 : starting_bit); - - up = u->_mp_d; - ux = 0; - limb = up[i]; - - if (starting_bit != 0) - { - if (us < 0) - { - ux = mpn_zero_p (up, i); - limb = ~ limb + ux; - ux = - (mp_limb_t) (limb >= ux); - } - - /* Mask to 0 all bits before starting_bit, thus ignoring them. */ - limb &= GMP_LIMB_MAX << (starting_bit % GMP_LIMB_BITS); - } - - return mpn_common_scan (limb, i, up, un, ux); -} - -mp_bitcnt_t -mpz_scan0 (const mpz_t u, mp_bitcnt_t starting_bit) -{ - mp_ptr up; - mp_size_t us, un, i; - mp_limb_t limb, ux; - - us = u->_mp_size; - ux = - (mp_limb_t) (us >= 0); - un = GMP_ABS (us); - i = starting_bit / GMP_LIMB_BITS; - - /* When past end, there's an immediate 0 bit for u>=0, or no 0 bits for - u<0. Notice this test picks up all cases of u==0 too. */ - if (i >= un) - return (ux ? starting_bit : ~(mp_bitcnt_t) 0); - - up = u->_mp_d; - limb = up[i] ^ ux; - - if (ux == 0) - limb -= mpn_zero_p (up, i); /* limb = ~(~limb + zero_p) */ - - /* Mask all bits before starting_bit, thus ignoring them. */ - limb &= GMP_LIMB_MAX << (starting_bit % GMP_LIMB_BITS); - - return mpn_common_scan (limb, i, up, un, ux); -} - - -/* MPZ base conversion. */ - -size_t -mpz_sizeinbase (const mpz_t u, int base) -{ - mp_size_t un; - mp_srcptr up; - mp_ptr tp; - mp_bitcnt_t bits; - struct gmp_div_inverse bi; - size_t ndigits; - - assert (base >= 2); - assert (base <= 62); - - un = GMP_ABS (u->_mp_size); - if (un == 0) - return 1; - - up = u->_mp_d; - - bits = (un - 1) * GMP_LIMB_BITS + mpn_limb_size_in_base_2 (up[un-1]); - switch (base) - { - case 2: - return bits; - case 4: - return (bits + 1) / 2; - case 8: - return (bits + 2) / 3; - case 16: - return (bits + 3) / 4; - case 32: - return (bits + 4) / 5; - /* FIXME: Do something more clever for the common case of base - 10. */ - } - - tp = gmp_xalloc_limbs (un); - mpn_copyi (tp, up, un); - mpn_div_qr_1_invert (&bi, base); - - ndigits = 0; - do - { - ndigits++; - mpn_div_qr_1_preinv (tp, tp, un, &bi); - un -= (tp[un-1] == 0); - } - while (un > 0); - - gmp_free (tp); - return ndigits; -} - -char * -mpz_get_str (char *sp, int base, const mpz_t u) -{ - unsigned bits; - const char *digits; - mp_size_t un; - size_t i, sn; - - digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; - if (base > 1) - { - if (base <= 36) - digits = "0123456789abcdefghijklmnopqrstuvwxyz"; - else if (base > 62) - return NULL; - } - else if (base >= -1) - base = 10; - else - { - base = -base; - if (base > 36) - return NULL; - } - - sn = 1 + mpz_sizeinbase (u, base); - if (!sp) - sp = (char *) gmp_xalloc (1 + sn); - - un = GMP_ABS (u->_mp_size); - - if (un == 0) - { - sp[0] = '0'; - sp[1] = '\0'; - return sp; - } - - i = 0; - - if (u->_mp_size < 0) - sp[i++] = '-'; - - bits = mpn_base_power_of_two_p (base); - - if (bits) - /* Not modified in this case. */ - sn = i + mpn_get_str_bits ((unsigned char *) sp + i, bits, u->_mp_d, un); - else - { - struct mpn_base_info info; - mp_ptr tp; - - mpn_get_base_info (&info, base); - tp = gmp_xalloc_limbs (un); - mpn_copyi (tp, u->_mp_d, un); - - sn = i + mpn_get_str_other ((unsigned char *) sp + i, base, &info, tp, un); - gmp_free (tp); - } - - for (; i < sn; i++) - sp[i] = digits[(unsigned char) sp[i]]; - - sp[sn] = '\0'; - return sp; -} - -int -mpz_set_str (mpz_t r, const char *sp, int base) -{ - unsigned bits, value_of_a; - mp_size_t rn, alloc; - mp_ptr rp; - size_t dn; - int sign; - unsigned char *dp; - - assert (base == 0 || (base >= 2 && base <= 62)); - - while (isspace( (unsigned char) *sp)) - sp++; - - sign = (*sp == '-'); - sp += sign; - - if (base == 0) - { - if (sp[0] == '0') - { - if (sp[1] == 'x' || sp[1] == 'X') - { - base = 16; - sp += 2; - } - else if (sp[1] == 'b' || sp[1] == 'B') - { - base = 2; - sp += 2; - } - else - base = 8; - } - else - base = 10; - } - - if (!*sp) - { - r->_mp_size = 0; - return -1; - } - dp = (unsigned char *) gmp_xalloc (strlen (sp)); - - value_of_a = (base > 36) ? 36 : 10; - for (dn = 0; *sp; sp++) - { - unsigned digit; - - if (isspace ((unsigned char) *sp)) - continue; - else if (*sp >= '0' && *sp <= '9') - digit = *sp - '0'; - else if (*sp >= 'a' && *sp <= 'z') - digit = *sp - 'a' + value_of_a; - else if (*sp >= 'A' && *sp <= 'Z') - digit = *sp - 'A' + 10; - else - digit = base; /* fail */ - - if (digit >= (unsigned) base) - { - gmp_free (dp); - r->_mp_size = 0; - return -1; - } - - dp[dn++] = digit; - } - - if (!dn) - { - gmp_free (dp); - r->_mp_size = 0; - return -1; - } - bits = mpn_base_power_of_two_p (base); - - if (bits > 0) - { - alloc = (dn * bits + GMP_LIMB_BITS - 1) / GMP_LIMB_BITS; - rp = MPZ_REALLOC (r, alloc); - rn = mpn_set_str_bits (rp, dp, dn, bits); - } - else - { - struct mpn_base_info info; - mpn_get_base_info (&info, base); - alloc = (dn + info.exp - 1) / info.exp; - rp = MPZ_REALLOC (r, alloc); - rn = mpn_set_str_other (rp, dp, dn, base, &info); - /* Normalization, needed for all-zero input. */ - assert (rn > 0); - rn -= rp[rn-1] == 0; - } - assert (rn <= alloc); - gmp_free (dp); - - r->_mp_size = sign ? - rn : rn; - - return 0; -} - -int -mpz_init_set_str (mpz_t r, const char *sp, int base) -{ - mpz_init (r); - return mpz_set_str (r, sp, base); -} - -size_t -mpz_out_str (FILE *stream, int base, const mpz_t x) -{ - char *str; - size_t len; - - str = mpz_get_str (NULL, base, x); - if (!str) - return 0; - len = strlen (str); - len = fwrite (str, 1, len, stream); - gmp_free (str); - return len; -} - - -static int -gmp_detect_endian (void) -{ - static const int i = 2; - const unsigned char *p = (const unsigned char *) &i; - return 1 - *p; -} - -/* Import and export. Does not support nails. */ -void -mpz_import (mpz_t r, size_t count, int order, size_t size, int endian, - size_t nails, const void *src) -{ - const unsigned char *p; - ptrdiff_t word_step; - mp_ptr rp; - mp_size_t rn; - - /* The current (partial) limb. */ - mp_limb_t limb; - /* The number of bytes already copied to this limb (starting from - the low end). */ - size_t bytes; - /* The index where the limb should be stored, when completed. */ - mp_size_t i; - - if (nails != 0) - gmp_die ("mpz_import: Nails not supported."); - - assert (order == 1 || order == -1); - assert (endian >= -1 && endian <= 1); - - if (endian == 0) - endian = gmp_detect_endian (); - - p = (unsigned char *) src; - - word_step = (order != endian) ? 2 * size : 0; - - /* Process bytes from the least significant end, so point p at the - least significant word. */ - if (order == 1) - { - p += size * (count - 1); - word_step = - word_step; - } - - /* And at least significant byte of that word. */ - if (endian == 1) - p += (size - 1); - - rn = (size * count + sizeof(mp_limb_t) - 1) / sizeof(mp_limb_t); - rp = MPZ_REALLOC (r, rn); - - for (limb = 0, bytes = 0, i = 0; count > 0; count--, p += word_step) - { - size_t j; - for (j = 0; j < size; j++, p -= (ptrdiff_t) endian) - { - limb |= (mp_limb_t) *p << (bytes++ * CHAR_BIT); - if (bytes == sizeof(mp_limb_t)) - { - rp[i++] = limb; - bytes = 0; - limb = 0; - } - } - } - assert (i + (bytes > 0) == rn); - if (limb != 0) - rp[i++] = limb; - else - i = mpn_normalized_size (rp, i); - - r->_mp_size = i; -} - -void * -mpz_export (void *r, size_t *countp, int order, size_t size, int endian, - size_t nails, const mpz_t u) -{ - size_t count; - mp_size_t un; - - if (nails != 0) - gmp_die ("mpz_import: Nails not supported."); - - assert (order == 1 || order == -1); - assert (endian >= -1 && endian <= 1); - assert (size > 0 || u->_mp_size == 0); - - un = u->_mp_size; - count = 0; - if (un != 0) - { - size_t k; - unsigned char *p; - ptrdiff_t word_step; - /* The current (partial) limb. */ - mp_limb_t limb; - /* The number of bytes left to do in this limb. */ - size_t bytes; - /* The index where the limb was read. */ - mp_size_t i; - - un = GMP_ABS (un); - - /* Count bytes in top limb. */ - limb = u->_mp_d[un-1]; - assert (limb != 0); - - k = (GMP_LIMB_BITS <= CHAR_BIT); - if (!k) - { - do { - int LOCAL_CHAR_BIT = CHAR_BIT; - k++; limb >>= LOCAL_CHAR_BIT; - } while (limb != 0); - } - /* else limb = 0; */ - - count = (k + (un-1) * sizeof (mp_limb_t) + size - 1) / size; - - if (!r) - r = gmp_xalloc (count * size); - - if (endian == 0) - endian = gmp_detect_endian (); - - p = (unsigned char *) r; - - word_step = (order != endian) ? 2 * size : 0; - - /* Process bytes from the least significant end, so point p at the - least significant word. */ - if (order == 1) - { - p += size * (count - 1); - word_step = - word_step; - } - - /* And at least significant byte of that word. */ - if (endian == 1) - p += (size - 1); - - for (bytes = 0, i = 0, k = 0; k < count; k++, p += word_step) - { - size_t j; - for (j = 0; j < size; ++j, p -= (ptrdiff_t) endian) - { - if (sizeof (mp_limb_t) == 1) - { - if (i < un) - *p = u->_mp_d[i++]; - else - *p = 0; - } - else - { - int LOCAL_CHAR_BIT = CHAR_BIT; - if (bytes == 0) - { - if (i < un) - limb = u->_mp_d[i++]; - bytes = sizeof (mp_limb_t); - } - *p = limb; - limb >>= LOCAL_CHAR_BIT; - bytes--; - } - } - } - assert (i == un); - assert (k == count); - } - - if (countp) - *countp = count; - - return r; -} diff --git a/src/rigraph/vendor/mini-gmp/mini-gmp.h b/src/rigraph/vendor/mini-gmp/mini-gmp.h deleted file mode 100644 index d575f7d..0000000 --- a/src/rigraph/vendor/mini-gmp/mini-gmp.h +++ /dev/null @@ -1,305 +0,0 @@ -/* mini-gmp, a minimalistic implementation of a GNU GMP subset. - -Copyright 2011-2015, 2017, 2019-2020 Free Software Foundation, Inc. - -This file is part of the GNU MP Library. - -The GNU MP Library is free software; you can redistribute it and/or modify -it under the terms of either: - - * the GNU Lesser General Public License as published by the Free - Software Foundation; either version 3 of the License, or (at your - option) any later version. - -or - - * the GNU General Public License as published by the Free Software - Foundation; either version 2 of the License, or (at your option) any - later version. - -or both in parallel, as here. - -The GNU MP Library is distributed in the hope that it will be useful, but -WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY -or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -for more details. - -You should have received copies of the GNU General Public License and the -GNU Lesser General Public License along with the GNU MP Library. If not, -see https://www.gnu.org/licenses/. */ - -/* About mini-gmp: This is a minimal implementation of a subset of the - GMP interface. It is intended for inclusion into applications which - have modest bignums needs, as a fallback when the real GMP library - is not installed. - - This file defines the public interface. */ - -#ifndef __MINI_GMP_H__ -#define __MINI_GMP_H__ - -/* For size_t */ -#include - -#if defined (__cplusplus) -extern "C" { -#endif - -void mp_set_memory_functions (void *(*) (size_t), - void *(*) (void *, size_t, size_t), - void (*) (void *, size_t)); - -void mp_get_memory_functions (void *(**) (size_t), - void *(**) (void *, size_t, size_t), - void (**) (void *, size_t)); - -#ifndef MINI_GMP_LIMB_TYPE -#define MINI_GMP_LIMB_TYPE long -#endif - -typedef unsigned MINI_GMP_LIMB_TYPE mp_limb_t; -typedef long mp_size_t; -typedef unsigned long mp_bitcnt_t; - -typedef mp_limb_t *mp_ptr; -typedef const mp_limb_t *mp_srcptr; - -typedef struct -{ - int _mp_alloc; /* Number of *limbs* allocated and pointed - to by the _mp_d field. */ - int _mp_size; /* abs(_mp_size) is the number of limbs the - last field points to. If _mp_size is - negative this is a negative number. */ - mp_limb_t *_mp_d; /* Pointer to the limbs. */ -} __mpz_struct; - -typedef __mpz_struct mpz_t[1]; - -typedef __mpz_struct *mpz_ptr; -typedef const __mpz_struct *mpz_srcptr; - -extern const int mp_bits_per_limb; - -void mpn_copyi (mp_ptr, mp_srcptr, mp_size_t); -void mpn_copyd (mp_ptr, mp_srcptr, mp_size_t); -void mpn_zero (mp_ptr, mp_size_t); - -int mpn_cmp (mp_srcptr, mp_srcptr, mp_size_t); -int mpn_zero_p (mp_srcptr, mp_size_t); - -mp_limb_t mpn_add_1 (mp_ptr, mp_srcptr, mp_size_t, mp_limb_t); -mp_limb_t mpn_add_n (mp_ptr, mp_srcptr, mp_srcptr, mp_size_t); -mp_limb_t mpn_add (mp_ptr, mp_srcptr, mp_size_t, mp_srcptr, mp_size_t); - -mp_limb_t mpn_sub_1 (mp_ptr, mp_srcptr, mp_size_t, mp_limb_t); -mp_limb_t mpn_sub_n (mp_ptr, mp_srcptr, mp_srcptr, mp_size_t); -mp_limb_t mpn_sub (mp_ptr, mp_srcptr, mp_size_t, mp_srcptr, mp_size_t); - -mp_limb_t mpn_mul_1 (mp_ptr, mp_srcptr, mp_size_t, mp_limb_t); -mp_limb_t mpn_addmul_1 (mp_ptr, mp_srcptr, mp_size_t, mp_limb_t); -mp_limb_t mpn_submul_1 (mp_ptr, mp_srcptr, mp_size_t, mp_limb_t); - -mp_limb_t mpn_mul (mp_ptr, mp_srcptr, mp_size_t, mp_srcptr, mp_size_t); -void mpn_mul_n (mp_ptr, mp_srcptr, mp_srcptr, mp_size_t); -void mpn_sqr (mp_ptr, mp_srcptr, mp_size_t); -int mpn_perfect_square_p (mp_srcptr, mp_size_t); -mp_size_t mpn_sqrtrem (mp_ptr, mp_ptr, mp_srcptr, mp_size_t); - -mp_limb_t mpn_lshift (mp_ptr, mp_srcptr, mp_size_t, unsigned int); -mp_limb_t mpn_rshift (mp_ptr, mp_srcptr, mp_size_t, unsigned int); - -mp_bitcnt_t mpn_scan0 (mp_srcptr, mp_bitcnt_t); -mp_bitcnt_t mpn_scan1 (mp_srcptr, mp_bitcnt_t); - -void mpn_com (mp_ptr, mp_srcptr, mp_size_t); -mp_limb_t mpn_neg (mp_ptr, mp_srcptr, mp_size_t); - -mp_bitcnt_t mpn_popcount (mp_srcptr, mp_size_t); - -mp_limb_t mpn_invert_3by2 (mp_limb_t, mp_limb_t); -#define mpn_invert_limb(x) mpn_invert_3by2 ((x), 0) - -size_t mpn_get_str (unsigned char *, int, mp_ptr, mp_size_t); -mp_size_t mpn_set_str (mp_ptr, const unsigned char *, size_t, int); - -void mpz_init (mpz_t); -void mpz_init2 (mpz_t, mp_bitcnt_t); -void mpz_clear (mpz_t); - -#define mpz_odd_p(z) (((z)->_mp_size != 0) & (int) (z)->_mp_d[0]) -#define mpz_even_p(z) (! mpz_odd_p (z)) - -int mpz_sgn (const mpz_t); -int mpz_cmp_si (const mpz_t, long); -int mpz_cmp_ui (const mpz_t, unsigned long); -int mpz_cmp (const mpz_t, const mpz_t); -int mpz_cmpabs_ui (const mpz_t, unsigned long); -int mpz_cmpabs (const mpz_t, const mpz_t); -int mpz_cmp_d (const mpz_t, double); -int mpz_cmpabs_d (const mpz_t, double); - -void mpz_abs (mpz_t, const mpz_t); -void mpz_neg (mpz_t, const mpz_t); -void mpz_swap (mpz_t, mpz_t); - -void mpz_add_ui (mpz_t, const mpz_t, unsigned long); -void mpz_add (mpz_t, const mpz_t, const mpz_t); -void mpz_sub_ui (mpz_t, const mpz_t, unsigned long); -void mpz_ui_sub (mpz_t, unsigned long, const mpz_t); -void mpz_sub (mpz_t, const mpz_t, const mpz_t); - -void mpz_mul_si (mpz_t, const mpz_t, long int); -void mpz_mul_ui (mpz_t, const mpz_t, unsigned long int); -void mpz_mul (mpz_t, const mpz_t, const mpz_t); -void mpz_mul_2exp (mpz_t, const mpz_t, mp_bitcnt_t); -void mpz_addmul_ui (mpz_t, const mpz_t, unsigned long int); -void mpz_addmul (mpz_t, const mpz_t, const mpz_t); -void mpz_submul_ui (mpz_t, const mpz_t, unsigned long int); -void mpz_submul (mpz_t, const mpz_t, const mpz_t); - -void mpz_cdiv_qr (mpz_t, mpz_t, const mpz_t, const mpz_t); -void mpz_fdiv_qr (mpz_t, mpz_t, const mpz_t, const mpz_t); -void mpz_tdiv_qr (mpz_t, mpz_t, const mpz_t, const mpz_t); -void mpz_cdiv_q (mpz_t, const mpz_t, const mpz_t); -void mpz_fdiv_q (mpz_t, const mpz_t, const mpz_t); -void mpz_tdiv_q (mpz_t, const mpz_t, const mpz_t); -void mpz_cdiv_r (mpz_t, const mpz_t, const mpz_t); -void mpz_fdiv_r (mpz_t, const mpz_t, const mpz_t); -void mpz_tdiv_r (mpz_t, const mpz_t, const mpz_t); - -void mpz_cdiv_q_2exp (mpz_t, const mpz_t, mp_bitcnt_t); -void mpz_fdiv_q_2exp (mpz_t, const mpz_t, mp_bitcnt_t); -void mpz_tdiv_q_2exp (mpz_t, const mpz_t, mp_bitcnt_t); -void mpz_cdiv_r_2exp (mpz_t, const mpz_t, mp_bitcnt_t); -void mpz_fdiv_r_2exp (mpz_t, const mpz_t, mp_bitcnt_t); -void mpz_tdiv_r_2exp (mpz_t, const mpz_t, mp_bitcnt_t); - -void mpz_mod (mpz_t, const mpz_t, const mpz_t); - -void mpz_divexact (mpz_t, const mpz_t, const mpz_t); - -int mpz_divisible_p (const mpz_t, const mpz_t); -int mpz_congruent_p (const mpz_t, const mpz_t, const mpz_t); - -unsigned long mpz_cdiv_qr_ui (mpz_t, mpz_t, const mpz_t, unsigned long); -unsigned long mpz_fdiv_qr_ui (mpz_t, mpz_t, const mpz_t, unsigned long); -unsigned long mpz_tdiv_qr_ui (mpz_t, mpz_t, const mpz_t, unsigned long); -unsigned long mpz_cdiv_q_ui (mpz_t, const mpz_t, unsigned long); -unsigned long mpz_fdiv_q_ui (mpz_t, const mpz_t, unsigned long); -unsigned long mpz_tdiv_q_ui (mpz_t, const mpz_t, unsigned long); -unsigned long mpz_cdiv_r_ui (mpz_t, const mpz_t, unsigned long); -unsigned long mpz_fdiv_r_ui (mpz_t, const mpz_t, unsigned long); -unsigned long mpz_tdiv_r_ui (mpz_t, const mpz_t, unsigned long); -unsigned long mpz_cdiv_ui (const mpz_t, unsigned long); -unsigned long mpz_fdiv_ui (const mpz_t, unsigned long); -unsigned long mpz_tdiv_ui (const mpz_t, unsigned long); - -unsigned long mpz_mod_ui (mpz_t, const mpz_t, unsigned long); - -void mpz_divexact_ui (mpz_t, const mpz_t, unsigned long); - -int mpz_divisible_ui_p (const mpz_t, unsigned long); - -unsigned long mpz_gcd_ui (mpz_t, const mpz_t, unsigned long); -void mpz_gcd (mpz_t, const mpz_t, const mpz_t); -void mpz_gcdext (mpz_t, mpz_t, mpz_t, const mpz_t, const mpz_t); -void mpz_lcm_ui (mpz_t, const mpz_t, unsigned long); -void mpz_lcm (mpz_t, const mpz_t, const mpz_t); -int mpz_invert (mpz_t, const mpz_t, const mpz_t); - -void mpz_sqrtrem (mpz_t, mpz_t, const mpz_t); -void mpz_sqrt (mpz_t, const mpz_t); -int mpz_perfect_square_p (const mpz_t); - -void mpz_pow_ui (mpz_t, const mpz_t, unsigned long); -void mpz_ui_pow_ui (mpz_t, unsigned long, unsigned long); -void mpz_powm (mpz_t, const mpz_t, const mpz_t, const mpz_t); -void mpz_powm_ui (mpz_t, const mpz_t, unsigned long, const mpz_t); - -void mpz_rootrem (mpz_t, mpz_t, const mpz_t, unsigned long); -int mpz_root (mpz_t, const mpz_t, unsigned long); - -void mpz_fac_ui (mpz_t, unsigned long); -void mpz_2fac_ui (mpz_t, unsigned long); -void mpz_mfac_uiui (mpz_t, unsigned long, unsigned long); -void mpz_bin_uiui (mpz_t, unsigned long, unsigned long); - -int mpz_probab_prime_p (const mpz_t, int); - -int mpz_tstbit (const mpz_t, mp_bitcnt_t); -void mpz_setbit (mpz_t, mp_bitcnt_t); -void mpz_clrbit (mpz_t, mp_bitcnt_t); -void mpz_combit (mpz_t, mp_bitcnt_t); - -void mpz_com (mpz_t, const mpz_t); -void mpz_and (mpz_t, const mpz_t, const mpz_t); -void mpz_ior (mpz_t, const mpz_t, const mpz_t); -void mpz_xor (mpz_t, const mpz_t, const mpz_t); - -mp_bitcnt_t mpz_popcount (const mpz_t); -mp_bitcnt_t mpz_hamdist (const mpz_t, const mpz_t); -mp_bitcnt_t mpz_scan0 (const mpz_t, mp_bitcnt_t); -mp_bitcnt_t mpz_scan1 (const mpz_t, mp_bitcnt_t); - -int mpz_fits_slong_p (const mpz_t); -int mpz_fits_ulong_p (const mpz_t); -long int mpz_get_si (const mpz_t); -unsigned long int mpz_get_ui (const mpz_t); -double mpz_get_d (const mpz_t); -size_t mpz_size (const mpz_t); -mp_limb_t mpz_getlimbn (const mpz_t, mp_size_t); - -void mpz_realloc2 (mpz_t, mp_bitcnt_t); -mp_srcptr mpz_limbs_read (mpz_srcptr); -mp_ptr mpz_limbs_modify (mpz_t, mp_size_t); -mp_ptr mpz_limbs_write (mpz_t, mp_size_t); -void mpz_limbs_finish (mpz_t, mp_size_t); -mpz_srcptr mpz_roinit_n (mpz_t, mp_srcptr, mp_size_t); - -#define MPZ_ROINIT_N(xp, xs) {{0, (xs),(xp) }} - -void mpz_set_si (mpz_t, signed long int); -void mpz_set_ui (mpz_t, unsigned long int); -void mpz_set (mpz_t, const mpz_t); -void mpz_set_d (mpz_t, double); - -void mpz_init_set_si (mpz_t, signed long int); -void mpz_init_set_ui (mpz_t, unsigned long int); -void mpz_init_set (mpz_t, const mpz_t); -void mpz_init_set_d (mpz_t, double); - -size_t mpz_sizeinbase (const mpz_t, int); -char *mpz_get_str (char *, int, const mpz_t); -int mpz_set_str (mpz_t, const char *, int); -int mpz_init_set_str (mpz_t, const char *, int); - -/* This long list taken from gmp.h. */ -/* For reference, "defined(EOF)" cannot be used here. In g++ 2.95.4, - defines EOF but not FILE. */ -#if defined (FILE) \ - || defined (H_STDIO) \ - || defined (_H_STDIO) /* AIX */ \ - || defined (_STDIO_H) /* glibc, Sun, SCO */ \ - || defined (_STDIO_H_) /* BSD, OSF */ \ - || defined (__STDIO_H) /* Borland */ \ - || defined (__STDIO_H__) /* IRIX */ \ - || defined (_STDIO_INCLUDED) /* HPUX */ \ - || defined (__dj_include_stdio_h_) /* DJGPP */ \ - || defined (_FILE_DEFINED) /* Microsoft */ \ - || defined (__STDIO__) /* Apple MPW MrC */ \ - || defined (_MSL_STDIO_H) /* Metrowerks */ \ - || defined (_STDIO_H_INCLUDED) /* QNX4 */ \ - || defined (_ISO_STDIO_ISO_H) /* Sun C++ */ \ - || defined (__STDIO_LOADED) /* VMS */ \ - || defined (__DEFINED_FILE) /* musl */ -size_t mpz_out_str (FILE *, int, const mpz_t); -#endif - -void mpz_import (mpz_t, size_t, int, size_t, int, size_t, const void *); -void *mpz_export (void *, size_t *, int, size_t, int, size_t, const mpz_t); - -#if defined (__cplusplus) -} -#endif -#endif /* __MINI_GMP_H__ */ diff --git a/src/rigraph/vendor/plfit/arithmetic_ansi.h b/src/rigraph/vendor/plfit/arithmetic_ansi.h deleted file mode 100644 index c58c98a..0000000 --- a/src/rigraph/vendor/plfit/arithmetic_ansi.h +++ /dev/null @@ -1,133 +0,0 @@ -/* - * 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 -#include - -#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/src/rigraph/vendor/plfit/arithmetic_sse_double.h b/src/rigraph/vendor/plfit/arithmetic_sse_double.h deleted file mode 100644 index a94d89d..0000000 --- a/src/rigraph/vendor/plfit/arithmetic_sse_double.h +++ /dev/null @@ -1,294 +0,0 @@ -/* - * 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 - -#if !defined(__APPLE__) -#include -#endif - -#include - -#if 1400 <= _MSC_VER -#include -#endif/*1400 <= _MSC_VER*/ - -#if HAVE_EMMINTRIN_H -#include -#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/src/rigraph/vendor/plfit/arithmetic_sse_float.h b/src/rigraph/vendor/plfit/arithmetic_sse_float.h deleted file mode 100644 index b88f0e2..0000000 --- a/src/rigraph/vendor/plfit/arithmetic_sse_float.h +++ /dev/null @@ -1,291 +0,0 @@ -/* - * 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 - -#if !defined(__APPLE__) -#include -#endif - -#include - -#if 1400 <= _MSC_VER -#include -#endif/*_MSC_VER*/ - -#if HAVE_XMMINTRIN_H -#include -#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/src/rigraph/vendor/plfit/gss.c b/src/rigraph/vendor/plfit/gss.c deleted file mode 100644 index f503484..0000000 --- a/src/rigraph/vendor/plfit/gss.c +++ /dev/null @@ -1,153 +0,0 @@ -/* 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 2 of the License, or (at - * your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public 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 -#include -#include -#include "plfit_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/src/rigraph/vendor/plfit/gss.h b/src/rigraph/vendor/plfit/gss.h deleted file mode 100644 index b96b213..0000000 --- a/src/rigraph/vendor/plfit/gss.h +++ /dev/null @@ -1,146 +0,0 @@ -/* 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 2 of the License, or (at - * your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public 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/src/rigraph/vendor/plfit/hzeta.c b/src/rigraph/vendor/plfit/hzeta.c deleted file mode 100644 index eefd70c..0000000 --- a/src/rigraph/vendor/plfit/hzeta.c +++ /dev/null @@ -1,651 +0,0 @@ -/* vim:set ts=4 sw=2 sts=2 et: */ - -/* This file was imported from a private scientific library - * based on GSL coined Home Scientific Libray (HSL) by its author - * Jerome Benoit; this very material is itself inspired from the - * material written by G. Jungan and distributed by GSL. - * Ultimately, some modifications were done in order to render the - * imported material independent from the rest of GSL. - */ - -/* `hsl/specfunc/hzeta.c' C source file -// HSL - Home Scientific Library -// Copyright (C) 2017-2018 Jerome Benoit -// -// HSL is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public 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 material in this file is mainly inspired by the material written by -// G. Jungan and distributed under GPLv2 by the GNU Scientific Library (GSL) -// ( https://www.gnu.org/software/gsl/ [specfunc/zeta.c]), itself inspired by -// the material written by Moshier and distributed in the Cephes Mathematical -// Library ( http://www.moshier.net/ [zeta.c]). -// -// More specifically, hsl_sf_hzeta_e is a slightly modifed clone of -// gsl_sf_hzeta_e as found in GSL 2.4; the remaining is `inspired by'. -// [Sooner or later a _Working_Note_ may be deposited at ResearchGate -// ( https://www.researchgate.net/profile/Jerome_Benoit )] -*/ - -/* Author: Jerome G. Benoit < jgmbenoit _at_ rezozer _dot_ net > */ - -#ifdef _MSC_VER -#define _USE_MATH_DEFINES -#endif - -#include -#include -#include "hzeta.h" -#include "plfit_error.h" -#include "platform.h" /* because of NAN */ - -/* imported 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 - -/* imported from gsl_math.h */ - -#ifndef M_LOG2E -#define M_LOG2E 1.44269504088896340735992468100 /* log_2 (e) */ -#endif - -/* imported from gsl_sf_result.h */ - -struct gsl_sf_result_struct { - double val; - double err; -}; -typedef struct gsl_sf_result_struct gsl_sf_result; - -/* imported and adapted from hsl/specfunc/specfunc_def.h */ - -#define HSL_SF_EVAL_RESULT(FnE) \ - gsl_sf_result result; \ - FnE ; \ - return (result.val); - -#define HSL_SF_EVAL_TUPLE_RESULT(FnET) \ - gsl_sf_result result0; \ - gsl_sf_result result1; \ - FnET ; \ - *tuple1=result1.val; \ - *tuple0=result0.val; \ - return (result0.val); - -/* */ - - -#define HSL_SF_HZETA_EULERMACLAURIN_SERIES_SHIFT 10 -#define HSL_SF_HZETA_EULERMACLAURIN_SERIES_ORDER 32 - -#define HSL_SF_LNHZETA_EULERMACLAURIN_SERIES_SHIFT_MAX 256 - -// B_{2j}/(2j) -static -double hsl_sf_hzeta_eulermaclaurin_series_coeffs[HSL_SF_HZETA_EULERMACLAURIN_SERIES_ORDER+1]={ - +1.0, - +1.0/12.0, - -1.0/720.0, - +1.0/30240.0, - -1.0/1209600.0, - +1.0/47900160.0, - -691.0/1307674368000.0, - +1.0/74724249600.0, - -3.38968029632258286683019539125e-13, - +8.58606205627784456413590545043e-15, - -2.17486869855806187304151642387e-16, - +5.50900282836022951520265260890e-18, - -1.39544646858125233407076862641e-19, - +3.53470703962946747169322997780e-21, - -8.95351742703754685040261131811e-23, - +2.26795245233768306031095073887e-24, - -5.74479066887220244526388198761e-26, - +1.45517247561486490186626486727e-27, - -3.68599494066531017818178247991e-29, - +9.33673425709504467203255515279e-31, - -2.36502241570062993455963519637e-32, - +5.99067176248213430465991239682e-34, - -1.51745488446829026171081313586e-35, - +3.84375812545418823222944529099e-37, - -9.73635307264669103526762127925e-39, - +2.46624704420068095710640028029e-40, - -6.24707674182074369314875679472e-42, - +1.58240302446449142975108170683e-43, - -4.00827368594893596853001219052e-45, - +1.01530758555695563116307139454e-46, - -2.57180415824187174992481940976e-48, - +6.51445603523381493155843485864e-50, - -1.65013099068965245550609878048e-51 - }; // hsl_sf_hzeta_eulermaclaurin_series_coeffs - -// 4\zeta(2j)/(2\pi)^(2j) -static -double hsl_sf_hzeta_eulermaclaurin_series_majorantratios[HSL_SF_HZETA_EULERMACLAURIN_SERIES_ORDER+1]={ - -2.0, - +1.0/6.0, - +1.0/360.0, - +1.0/15120.0, - +1.0/604800.0, - +1.0/23950080.0, - +691.0/653837184000.0, - +1.0/37362124800.0, - +3617.0/5335311421440000.0, - +1.71721241125556891282718109009e-14, - +4.34973739711612374608303284773e-16, - +1.10180056567204590304053052178e-17, - +2.79089293716250466814153725281e-19, - +7.06941407925893494338645995561e-21, - +1.79070348540750937008052226362e-22, - +4.53590490467536612062190147774e-24, - +1.14895813377444048905277639752e-25, - +2.91034495122972980373252973454e-27, - +7.37198988133062035636356495982e-29, - +1.86734685141900893440651103056e-30, - +4.73004483140125986911927039274e-32, - +1.19813435249642686093198247936e-33, - +3.03490976893658052342162627173e-35, - +7.68751625090837646445889058198e-37, - +1.94727061452933820705352425585e-38, - +4.93249408840136191421280056051e-40, - +1.24941534836414873862975135893e-41, - +3.16480604892898285950216341362e-43, - +8.01654737189787193706002438098e-45, - +2.03061517111391126232614278906e-46, - +5.14360831648374349984963881946e-48, - +1.30289120704676298631168697172e-49, - +3.30026198137930491101219756091e-51 - }; // hsl_sf_hzeta_eulermaclaurin_series_majorantratios - - -extern -int hsl_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; // max_bits=\lceil{s}\rceil with \zeta(s,2)=\zeta(s)-1=GSL_DBL_EPSILON - const double ln_term0=-s*log(q); - if (ln_term0 < GSL_LOG_DBL_MIN+1.0) { - PLFIT_ERROR("underflow", PLFIT_UNDRFLOW); - } - else if (GSL_LOG_DBL_MAX-1.0 < ln_term0) { - PLFIT_ERROR("overflow", PLFIT_OVERFLOW); - } -#if 1 - else if (((max_bits < s) && (q < 1.0)) || ((0.5*max_bits < s) && (q < 0.25))) { - result->val=pow(q,-s); - result->err=2.0*GSL_DBL_EPSILON*fabs(result->val); - return (PLFIT_SUCCESS); - } - else if ((0.5*max_bits < s) && (q < 1.0)) { - const double a0=pow(q,-s); - const double p1=pow(q/(1.0+q),s); - const double p2=pow(q/(2.0+q),s); - const double ans=a0*(1.0+p1+p2); - result->val=ans; - result->err=GSL_DBL_EPSILON*(2.0+0.5*s)*fabs(result->val); - return (PLFIT_SUCCESS); - } -#endif - else { // Euler-Maclaurin summation formula - const double qshift=HSL_SF_HZETA_EULERMACLAURIN_SERIES_SHIFT+q; - const double inv_qshift=1.0/qshift; - const double sqr_inv_qshift=inv_qshift*inv_qshift; - const double inv_sm1=1.0/(s-1.0); - const double pmax=pow(qshift,-s); - double terms[HSL_SF_HZETA_EULERMACLAURIN_SERIES_SHIFT+HSL_SF_HZETA_EULERMACLAURIN_SERIES_ORDER+1]={NAN}; - double delta=NAN; - double tscp=s; - double scp=tscp; - double pcp=pmax*inv_qshift; - double ratio=scp*pcp; - size_t n=0; - size_t j=0; - double ans=0.0; - double mjr=NAN; - - for(j=0;jval=+ans; - result->err=2.0*((HSL_SF_HZETA_EULERMACLAURIN_SERIES_SHIFT+1.0)*GSL_DBL_EPSILON*fabs(ans)+mjr); - return (PLFIT_SUCCESS); - } - } - -} - -extern -double hsl_sf_hzeta(const double s, const double q) { - HSL_SF_EVAL_RESULT(hsl_sf_hzeta_e(s,q,&result)); } - -extern -int hsl_sf_hzeta_deriv_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 ln_hz_term0=-s*log(q); - if (ln_hz_term0 < GSL_LOG_DBL_MIN+1.0) { - PLFIT_ERROR("underflow", PLFIT_UNDRFLOW); - } - else if (GSL_LOG_DBL_MAX-1.0 < ln_hz_term0) { - PLFIT_ERROR("overflow", PLFIT_OVERFLOW); - } - else { // Euler-Maclaurin summation formula - const double qshift=HSL_SF_HZETA_EULERMACLAURIN_SERIES_SHIFT+q; - const double inv_qshift=1.0/qshift; - const double sqr_inv_qshift=inv_qshift*inv_qshift; - const double inv_sm1=1.0/(s-1.0); - const double pmax=pow(qshift,-s); - const double lmax=log(qshift); - double terms[HSL_SF_HZETA_EULERMACLAURIN_SERIES_SHIFT+HSL_SF_HZETA_EULERMACLAURIN_SERIES_ORDER+1]={NAN}; - double delta=NAN; - double tscp=s; - double scp=tscp; - double pcp=pmax*inv_qshift; - double lcp=lmax-1.0/s; - double ratio=scp*pcp*lcp; - double qs=NAN; - size_t n=0; - size_t j=0; - double ans=0.0; - double mjr=NAN; - - for(j=0,qs=q;jval=-ans; - result->err=2.0*((HSL_SF_HZETA_EULERMACLAURIN_SERIES_SHIFT+1.0)*GSL_DBL_EPSILON*fabs(ans)+mjr); - return (PLFIT_SUCCESS); - } - } - -} - -extern -double hsl_sf_hzeta_deriv(const double s, const double q) { - HSL_SF_EVAL_RESULT(hsl_sf_hzeta_deriv_e(s,q,&result)); } - -extern -int hsl_sf_hzeta_deriv2_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 ln_hz_term0=-s*log(q); - if (ln_hz_term0 < GSL_LOG_DBL_MIN+1.0) { - PLFIT_ERROR("underflow", PLFIT_UNDRFLOW); - } - else if (GSL_LOG_DBL_MAX-1.0 < ln_hz_term0) { - PLFIT_ERROR("overflow", PLFIT_OVERFLOW); - } - else { // Euler-Maclaurin summation formula - const double qshift=HSL_SF_HZETA_EULERMACLAURIN_SERIES_SHIFT+q; - const double inv_qshift=1.0/qshift; - const double sqr_inv_qshift=inv_qshift*inv_qshift; - const double inv_sm1=1.0/(s-1.0); - const double pmax=pow(qshift,-s); - const double lmax=log(qshift); - const double lmax_p_inv_sm1=lmax+inv_sm1; - const double sqr_inv_sm1=inv_sm1*inv_sm1; - const double sqr_lmax=lmax*lmax; - const double sqr_lmax_p_inv_sm1=lmax_p_inv_sm1*lmax_p_inv_sm1; - double terms[HSL_SF_HZETA_EULERMACLAURIN_SERIES_SHIFT+HSL_SF_HZETA_EULERMACLAURIN_SERIES_ORDER+1]={NAN}; - double delta=NAN; - double tscp=s; - double slcp=NAN; - double plcp=NAN; - double scp=tscp; - double pcp=pmax*inv_qshift; - double lcp=1.0/s-lmax; - double sqr_lcp=lmax*(lmax-2.0/s); - double ratio=scp*pcp*sqr_lcp; - double qs=NAN; - double lqs=NAN; - size_t n=0; - size_t j=0; - double ans=0.0; - double mjr=NAN; - - for(j=0,qs=q;jval=+ans; - result->err=2.0*((HSL_SF_HZETA_EULERMACLAURIN_SERIES_SHIFT+1.0)*GSL_DBL_EPSILON*fabs(ans)+mjr); - return (PLFIT_SUCCESS); - } - } - -} - -extern -double hsl_sf_hzeta_deriv2(const double s, const double q) { - HSL_SF_EVAL_RESULT(hsl_sf_hzeta_deriv2_e(s,q,&result)); } - -static inline -double hsl_sf_hZeta0_zed(const double s, const double q) { -#if 1 - const long double ld_q=(long double)(q); - const long double ld_s=(long double)(s); - const long double ld_log1prq=log1pl(1.0L/ld_q); - const long double ld_epsilon=expm1l(-ld_s*ld_log1prq); - const long double ld_z=ld_s+(ld_q+0.5L*ld_s+0.5L)*ld_epsilon; - const double z=(double)(ld_z); -#else - double z=s+(q+0.5*s+0.5)*expm1(-s*log1p(1.0/q)); -#endif - return (z); } - -// Z_{0}(s,a) = a^s \left(\frac{1}{2}+\frac{a}{s-1}\right)^{-1} \zeta(s,a) - 1 -// Z_{0}(s,a) = O\left(\frac{(s-1)s}{6a^{2}}\right) -static -int hsl_sf_hZeta0(const double s, const double q, double * value, double * abserror) { - const double criterion=ceil(10.0*s-q); - const size_t shift=(criterion<0.0)?0: - (criterionval=log1p(ln_hZeta0_value); - result->err=(2.0*GSL_DBL_EPSILON*ln_hz_coeff+hZeta0_abserror)/(1.0+ln_hZeta0_value); - } - - if (result_deriv) { - const double ld_hz_coeff2=1.0+inv_sm1*M_LOG2E; - const double ld_hz_coeff1=1.0+inv_qsm1*ld_hz_coeff2; - double hZeta1_value=NAN; - double hZeta1_abserror=NAN; - hsl_sf_hZeta1(s,2.0,M_LN2,&hZeta1_value,&hZeta1_abserror,NULL); - hZeta0_value*=hz_coeff1; - hZeta0_value+=hz_coeff0; - hZeta1_value+=1.0; - hZeta1_value*=-M_LN2*ld_hz_coeff1; - result_deriv->val=hZeta1_value/hZeta0_value; - result_deriv->err=2.0*GSL_DBL_EPSILON*fabs(result_deriv->val)+(hZeta0_abserror+hZeta1_abserror); - } - } - else { - const double ln_q=log(q); - double hZeta0_value=NAN; - double hZeta0_abserror=NAN; - hsl_sf_hZeta0(s,q,&hZeta0_value,&hZeta0_abserror); - if (result) { - const double ln_hz_term0=-s*ln_q; - const double ln_hz_term1=log(0.5+q/(s-1.0)); - result->val=ln_hz_term0+ln_hz_term1+log1p(hZeta0_value); - result->err=2.0*GSL_DBL_EPSILON*(fabs(ln_hz_term0)+fabs(ln_hz_term1))+hZeta0_abserror/(1.0+hZeta0_value); - } - if (result_deriv) { - double hZeta1_value=NAN; - double hZeta1_abserror=NAN; - double ld_hz_coeff1=NAN; - hsl_sf_hZeta1(s,q,ln_q,&hZeta1_value,&hZeta1_abserror,&ld_hz_coeff1); - result_deriv->val=-ln_q*ld_hz_coeff1*(1.0+hZeta1_value)/(1.0+hZeta0_value); - result_deriv->err=2.0*GSL_DBL_EPSILON*fabs(result_deriv->val)+(hZeta0_abserror+hZeta1_abserror); - } - } - - return (PLFIT_SUCCESS); } - -extern -double hsl_sf_lnhzeta_deriv_tuple(const double s, const double q, double * tuple0, double * tuple1) { - HSL_SF_EVAL_TUPLE_RESULT(hsl_sf_lnhzeta_deriv_tuple_e(s,q,&result0,&result1)); } - -extern -int hsl_sf_lnhzeta_e(const double s, const double q, gsl_sf_result * result) { - return (hsl_sf_lnhzeta_deriv_tuple_e(s,q,result,NULL)); } - -extern -double hsl_sf_lnhzeta(const double s, const double q) { - HSL_SF_EVAL_RESULT(hsl_sf_lnhzeta_e(s,q,&result)); } - -extern -int hsl_sf_lnhzeta_deriv_e(const double s, const double q, gsl_sf_result * result) { - return (hsl_sf_lnhzeta_deriv_tuple_e(s,q,NULL,result)); } - -extern -double hsl_sf_lnhzeta_deriv(const double s, const double q) { - HSL_SF_EVAL_RESULT(hsl_sf_lnhzeta_deriv_e(s,q,&result)); } - -// -// End of file `hsl/specfunc/hzeta.c'. diff --git a/src/rigraph/vendor/plfit/hzeta.h b/src/rigraph/vendor/plfit/hzeta.h deleted file mode 100644 index 31d646e..0000000 --- a/src/rigraph/vendor/plfit/hzeta.h +++ /dev/null @@ -1,96 +0,0 @@ -/* This file was imported from a private scientific library - * based on GSL coined Home Scientific Libray (HSL) by its author - * Jerome Benoit; this very material is itself inspired from the - * material written by G. Jungan and distributed by GSL. - * Ultimately, some modifications were done in order to render the - * imported material independent from the rest of GSL. - */ - -/* `hsl/hsl_sf_zeta.h' C header file -// HSL - Home Scientific Library -// Copyright (C) 2005-2018 Jerome Benoit -// -// HSL is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -*/ - -/* For futher details, see its source conterpart src/hzeta.c */ - -/* Author: Jerome G. Benoit < jgmbenoit _at_ rezozer _dot_ net > */ - -#ifndef __HZETA_H__ -#define __HZETA_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 hsl_sf_hzeta(const double s, const double q); - -/* First Derivative of Hurwitz Zeta Function - * zeta'(s,q) = - Sum[ Ln(k+q)/(k+q)^(s), {k,0,Infinity} ] - * - * s > 1.0, q > 0.0 - */ -double hsl_sf_hzeta_deriv(const double s, const double q); - -/* Second Derivative of Hurwitz Zeta Function - * zeta''(s,q) = + Sum[ Ln(k+q)^2/(k+q)^(s), {k,0,Infinity} ] - * - * s > 1.0, q > 0.0 - */ -double hsl_sf_hzeta_deriv2(const double s, const double q); - -/* Logarithm of Hurwitz Zeta Function - * lnzeta(s,q) = ln(zeta(s,q)) - * - * s > 1.0, q > 0.0 (and q >> 1) - */ -double hsl_sf_lnhzeta(const double s, const double q); - -/* Logarithmic Derivative of Hurwitz Zeta Function - * lnzeta'(s,q) = zeta'(s,q)/zeta(s,q) - * - * s > 1.0, q > 0.0 (and q >> 1) - */ -double hsl_sf_lnhzeta_deriv(const double s, const double q); - -/* Logarithm and Logarithmic Derivative of Hurwitz Zeta Function: - * nonredundant computation version: - * - lnzeta(s,q) and lnzeta'(s,q) are stored in *deriv0 and *deriv1, respectively; - * - the return value and the value stored in *deriv0 are the same; - * - deriv0 and deriv1 must be effective pointers, that is, not the NULL pointer. - * - * s > 1.0, q > 0.0 (and q >> 1) - */ -double hsl_sf_lnhzeta_deriv_tuple(const double s, const double q, double * deriv0, double * deriv1); - - -__END_DECLS - -#endif // __HZETA_H__ diff --git a/src/rigraph/vendor/plfit/kolmogorov.c b/src/rigraph/vendor/plfit/kolmogorov.c deleted file mode 100644 index 432878f..0000000 --- a/src/rigraph/vendor/plfit/kolmogorov.c +++ /dev/null @@ -1,66 +0,0 @@ -/* 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 2 of the License, or (at - * your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public 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 -#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((double) 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/src/rigraph/vendor/plfit/kolmogorov.h b/src/rigraph/vendor/plfit/kolmogorov.h deleted file mode 100644 index 358e697..0000000 --- a/src/rigraph/vendor/plfit/kolmogorov.h +++ /dev/null @@ -1,43 +0,0 @@ -/* 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 2 of the License, or (at - * your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public 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 - -__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/src/rigraph/vendor/plfit/lbfgs.c b/src/rigraph/vendor/plfit/lbfgs.c deleted file mode 100644 index 3fcd2c3..0000000 --- a/src/rigraph/vendor/plfit/lbfgs.c +++ /dev/null @@ -1,1378 +0,0 @@ -/* - * 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. - Mathematics of Computation, Vol. 35, No. 151, pp. 773--782, 1980. - - Dong C. Liu and Jorge Nocedal. - On the limited memory BFGS method for large scale optimization. - Mathematical Programming 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. - Numerical Methods for Unconstrained Optimization and Nonlinear - Equations, Englewood Cliffs, 1983. - - 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. - -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 Proceedings of the 24th International Conference on Machine - Learning (ICML 2007), 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 -#endif - -#include -#include -#include - -#include "lbfgs.h" -#include "platform.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, ¶m); - } else { - ls = linesearch(n, x, &fx, g, d, &step, xp, pg, w, &cd, ¶m); - 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) { - ret = cd.proc_progress(cd.instance, x, g, fx, xnorm, gnorm, step, cd.n, k, ls); - if (ret) { - 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); - } - } -} - - - -/** - * 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/src/rigraph/vendor/plfit/lbfgs.h b/src/rigraph/vendor/plfit/lbfgs.h deleted file mode 100644 index f26ae87..0000000 --- a/src/rigraph/vendor/plfit/lbfgs.h +++ /dev/null @@ -1,736 +0,0 @@ -/* - * 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, - -
-    minimize F(x), x = (x1, x2, ..., xN),
-
- -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: -- Optimization with L1-norm (Orthant-Wise Limited-memory Quasi-Newton - (OWL-QN) method): - 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). -- Clean C code: - 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. -- Callback interface: - 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. -- Thread safe: - The library is thread-safe, which is the secondary gain from the callback - interface. -- Cross platform. The source code can be compiled on Microsoft Visual - Studio 2005, GNU C Compiler (gcc), etc. -- Configurable precision: A user can choose single-precision (float) - or double-precision (double) accuracy by changing ::LBFGS_FLOAT macro. -- SSE/SSE2 optimization: - 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: -- CRFsuite: A fast implementation of Conditional Random Fields (CRFs) -- Classias: A collection of machine-learning algorithms for classification -- mlegp: an R package for maximum likelihood estimates for Gaussian processes -- imaging2: the imaging2 class library -- Algorithm::LBFGS - Perl extension for L-BFGS -- YAP-LBFGS (an interface to call libLBFGS from YAP Prolog) - -@section download Download - -- Source code - -libLBFGS is distributed under the term of the -MIT license. - -@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, the OW-LQN method must be used with the - backtracking algorithm (::LBFGS_LINESEARCH_BACKTRACKING), 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. - Mathematics of Computation, Vol. 35, No. 151, pp. 773--782, 1980. - - Dong C. Liu and Jorge Nocedal. - On the limited memory BFGS method for large scale optimization. - Mathematical Programming 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. - Numerical Methods for Unconstrained Optimization and Nonlinear - Equations, Englewood Cliffs, 1983. - - 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. - -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 Proceedings of the 24th International Conference on Machine - Learning (ICML 2007), 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 - -- L-BFGS by Jorge Nocedal. -- Orthant-Wise Limited-memory Quasi-Newton Optimizer for L1-regularized Objectives by Galen Andrew. -- C port (via f2c) by Taku Kudo. -- C#/C++/Delphi/VisualBasic6 port in ALGLIB. -- Computational Crystallography Toolbox includes - scitbx::lbfgs. -*/ - -#endif/*__LBFGS_H__*/ diff --git a/src/rigraph/vendor/plfit/mt.c b/src/rigraph/vendor/plfit/mt.c deleted file mode 100644 index 615c6bc..0000000 --- a/src/rigraph/vendor/plfit/mt.c +++ /dev/null @@ -1,93 +0,0 @@ -/* mt.c - * - * Mersenne Twister random number generator, based on the implementation of - * Michael Brundage (which has been placed in the public domain). - * - * Author: Tamas Nepusz (original by Michael Brundage) - * - * See the following URL for the original implementation: - * http://www.qbrundage.com/michaelb/pubs/essays/random_number_generation.html - * - * This file has been placed in the public domain. - */ - -#include - -#include "igraph_random.h" - -#include "plfit_mt.h" - -static uint16_t get_random_uint16() { - return RNG_INT31() & 0xFFFF; -} - -void plfit_mt_init(plfit_mt_rng_t* rng) { - plfit_mt_init_from_rng(rng, 0); -} - -void plfit_mt_init_from_rng(plfit_mt_rng_t* rng, plfit_mt_rng_t* seeder) { - int i; - - if (seeder == 0) { - for (i = 0; i < PLFIT_MT_LEN; i++) { - /* RAND_MAX is guaranteed to be at least 32767, so we can use two - * calls to rand() to produce a random 32-bit number */ - rng->mt_buffer[i] = (get_random_uint16() << 16) + get_random_uint16(); - } - } else { - for (i = 0; i < PLFIT_MT_LEN; i++) { - rng->mt_buffer[i] = plfit_mt_random(seeder); - } - } - - rng->mt_index = 0; -} - -#define MT_IA 397 -#define MT_IB (PLFIT_MT_LEN - MT_IA) -#define UPPER_MASK 0x80000000 -#define LOWER_MASK 0x7FFFFFFF -#define MATRIX_A 0x9908B0DF -#define TWIST(b,i,j) ((b)[i] & UPPER_MASK) | ((b)[j] & LOWER_MASK) -#define MAGIC(s) (((s)&1)*MATRIX_A) - -uint32_t plfit_mt_random(plfit_mt_rng_t* rng) { - uint32_t * b = rng->mt_buffer; - int idx = rng->mt_index; - uint32_t s; - int i; - - if (idx == PLFIT_MT_LEN * sizeof(uint32_t)) { - idx = 0; - i = 0; - for (; i < MT_IB; i++) { - s = TWIST(b, i, i+1); - b[i] = b[i + MT_IA] ^ (s >> 1) ^ MAGIC(s); - } - for (; i < PLFIT_MT_LEN-1; i++) { - s = TWIST(b, i, i+1); - b[i] = b[i - MT_IB] ^ (s >> 1) ^ MAGIC(s); - } - - s = TWIST(b, PLFIT_MT_LEN-1, 0); - b[PLFIT_MT_LEN-1] = b[MT_IA-1] ^ (s >> 1) ^ MAGIC(s); - } - - rng->mt_index = idx + sizeof(uint32_t); - return *(uint32_t *)((unsigned char *)b + idx); - /* - Matsumoto and Nishimura additionally confound the bits returned to the caller - but this doesn't increase the randomness, and slows down the generator by - as much as 25%. So I omit these operations here. - - r ^= (r >> 11); - r ^= (r << 7) & 0x9D2C5680; - r ^= (r << 15) & 0xEFC60000; - r ^= (r >> 18); - */ -} - - -double plfit_mt_uniform_01(plfit_mt_rng_t* rng) { - return ((double)plfit_mt_random(rng)) / PLFIT_MT_RAND_MAX; -} diff --git a/src/rigraph/vendor/plfit/options.c b/src/rigraph/vendor/plfit/options.c deleted file mode 100644 index 1665560..0000000 --- a/src/rigraph/vendor/plfit/options.c +++ /dev/null @@ -1,52 +0,0 @@ -/* 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 2 of the License, or (at - * your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public 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 "plfit_error.h" -#include "plfit.h" - -const plfit_continuous_options_t plfit_continuous_default_options = { - /* .finite_size_correction = */ 0, - /* .xmin_method = */ PLFIT_DEFAULT_CONTINUOUS_METHOD, - /* .p_value_method = */ PLFIT_DEFAULT_P_VALUE_METHOD, - /* .p_value_precision = */ 0.01, - /* .rng = */ 0 -}; - -const plfit_discrete_options_t plfit_discrete_default_options = { - /* .finite_size_correction = */ 0, - /* .alpha_method = */ PLFIT_DEFAULT_DISCRETE_METHOD, - /* .alpha = */ { - /* .min = */ 1.01, - /* .max = */ 5, - /* .step = */ 0.01 - }, - /* .p_value_method = */ PLFIT_DEFAULT_P_VALUE_METHOD, - /* .p_value_precision = */ 0.01, - /* .rng = */ 0 -}; - -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/src/rigraph/vendor/plfit/platform.c b/src/rigraph/vendor/plfit/platform.c deleted file mode 100644 index e9560e4..0000000 --- a/src/rigraph/vendor/plfit/platform.c +++ /dev/null @@ -1,36 +0,0 @@ -/* platform.c - * - * Copyright (C) 2014 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 2 of the License, or (at - * your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public 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 "platform.h" - -#ifdef _MSC_VER - -inline double _plfit_fmin(double a, double b) { - return (a < b) ? a : b; -} - -inline double _plfit_round(double x) { - return floor(x+0.5); -} - -#endif - -/* Dummy function to prevent a warning when compiling with Clang - the file - * would contain no symbols */ -void _plfit_i_unused() {} diff --git a/src/rigraph/vendor/plfit/platform.h b/src/rigraph/vendor/plfit/platform.h deleted file mode 100644 index 6512955..0000000 --- a/src/rigraph/vendor/plfit/platform.h +++ /dev/null @@ -1,69 +0,0 @@ -/* 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 2 of the License, or (at - * your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public 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 - -__BEGIN_DECLS - -#ifdef _MSC_VER -#include - -#define snprintf _snprintf -#define inline __inline - -#ifndef isfinite -# define isfinite(x) _finite(x) -#endif - -extern double _plfit_fmin(double a, double b); -extern double _plfit_round(double x); - -#define fmin _plfit_fmin -#define round _plfit_round - -#endif /* _MSC_VER */ - -#ifndef isnan -# define isnan(x) ((x) != (x)) -#endif - -#ifndef INFINITY -# define INFINITY (1.0/0.0) -#endif - -#ifndef NAN -# define NAN ((double)0.0 / (double)DBL_MIN) -#endif - -__END_DECLS - -#endif /* __PLATFORM_H__ */ diff --git a/src/rigraph/vendor/plfit/plfit.c b/src/rigraph/vendor/plfit/plfit.c deleted file mode 100644 index 266a9b0..0000000 --- a/src/rigraph/vendor/plfit/plfit.c +++ /dev/null @@ -1,1342 +0,0 @@ -/* vim:set ts=4 sw=4 sts=4 et: */ -/* 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 2 of the License, or (at - * your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public 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 -#include -#include -#include -#include -#include -#include "plfit_error.h" -#include "gss.h" -#include "lbfgs.h" -#include "platform.h" -#include "plfit.h" -#include "kolmogorov.h" -#include "plfit_sampling.h" -#include "hzeta.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 plfit_i_resample_continuous(double* xs_head, size_t num_smaller, - size_t n, double alpha, double xmin, size_t num_samples, plfit_mt_rng_t* rng, - double* result); -static int plfit_i_resample_discrete(double* xs_head, size_t num_smaller, - size_t n, double alpha, double xmin, size_t num_samples, plfit_mt_rng_t* rng, - double* result); - -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); -} - -static int plfit_i_copy_and_sort(double* xs, size_t n, double** result) { - *result = (double*)malloc(sizeof(double) * n); - if (*result == 0) { - PLFIT_ERROR("cannot create sorted copy of input data", PLFIT_ENOMEM); - } - - memcpy(*result, xs, sizeof(double) * n); - qsort(*result, n, sizeof(double), double_comparator); - - return PLFIT_SUCCESS; -} - -/** - * Given an unsorted array of doubles, counts how many elements there are that - * are smaller than a given value. - * - * \param begin pointer to the beginning of the array - * \param end pointer to the first element after the end of the array - * \param xmin the threshold value - * - * \return the nubmer of elements in the array that are smaller than the given - * value. - */ -static size_t count_smaller(double* begin, double* end, double xmin) { - double* p; - size_t counter = 0; - - for (p = begin; p < end; p++) { - if (*p < xmin) { - counter++; - } - } - - return counter; -} - -/** - * Given an unsorted array of doubles, return another array that contains the - * elements that are smaller than a given value - * - * \param begin pointer to the beginning of the array - * \param end pointer to the first element after the end of the array - * \param xmin the threshold value - * \param result_length if not \c NULL, the number of unique elements in the - * given array is returned here - * - * \return pointer to the head of the new array or 0 if there is not enough - * memory - */ -static double* extract_smaller(double* begin, double* end, double xmin, - size_t* result_length) { - size_t counter = count_smaller(begin, end, xmin); - double *p, *result; - - result = calloc(counter, sizeof(double)); - if (result == 0) - return 0; - - for (p = result; begin < end; begin++) { - if (*begin < xmin) { - *p = *begin; - p++; - } - } - - if (result_length) { - *result_length = counter; - } - - return result; -} - -/** - * 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. It is left unchanged if - * the function returns with an error. - * - * \return pointer to the head of the new array or 0 if there is not enough - * memory - */ -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; - if (result_length != 0) { - *result_length = 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 **********/ - -static 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; -} - -static 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; -} - -static 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; -} - -static 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; -} - -static int plfit_i_calculate_p_value_continuous(double* xs, size_t n, - const plfit_continuous_options_t *options, plfit_bool_t xmin_fixed, - plfit_result_t *result) { - long int num_trials; - long int successes = 0; - double *xs_head; - size_t num_smaller; - plfit_continuous_options_t options_no_p_value = *options; - int retval = PLFIT_SUCCESS; - - if (options->p_value_method == PLFIT_P_VALUE_SKIP) { - result->p = NAN; - return PLFIT_SUCCESS; - } - - if (options->p_value_method == PLFIT_P_VALUE_APPROXIMATE) { - num_smaller = count_smaller(xs, xs + n, result->xmin); - result->p = plfit_ks_test_one_sample_p(result->D, n - num_smaller); - return PLFIT_SUCCESS; - } - - options_no_p_value.p_value_method = PLFIT_P_VALUE_SKIP; - num_trials = (long int)(0.25 / options->p_value_precision / options->p_value_precision); - if (num_trials <= 0) { - PLFIT_ERROR("invalid p-value precision", PLFIT_EINVAL); - } - - /* Extract the head of xs that contains elements smaller than xmin */ - xs_head = extract_smaller(xs, xs+n, result->xmin, &num_smaller); - if (xs_head == 0) - PLFIT_ERROR("cannot calculate exact p-value", PLFIT_ENOMEM); - -#ifdef _OPENMP -#pragma omp parallel -#endif - { - /* Parallel section starts here. If we are compiling using OpenMP, each - * thread will use its own RNG that is seeded from the master RNG. If - * we are compiling without OpenMP, there is only one thread and it uses - * the master RNG. This section must be critical to ensure that only one - * thread is using the master RNG at the same time. */ -#ifdef _OPENMP - plfit_mt_rng_t private_rng; -#endif - plfit_mt_rng_t *p_rng; - double *ys; - long int i; - plfit_result_t result_synthetic; - -#ifdef _OPENMP -#pragma omp critical - { - p_rng = &private_rng; - plfit_mt_init_from_rng(p_rng, options->rng); - } -#else - p_rng = options->rng; -#endif - - /* Allocate memory to sample into */ - ys = calloc(n, sizeof(double)); - if (ys == 0) { - retval = PLFIT_ENOMEM; - } else { - /* The main for loop starts here. */ -#ifdef _OPENMP -#pragma omp for reduction(+:successes) -#endif - for (i = 0; i < num_trials; i++) { - plfit_i_resample_continuous(xs_head, num_smaller, n, result->alpha, - result->xmin, n, p_rng, ys); - if (xmin_fixed) { - plfit_estimate_alpha_continuous(ys, n, result->xmin, - &options_no_p_value, &result_synthetic); - } else { - plfit_continuous(ys, n, &options_no_p_value, &result_synthetic); - } - if (result_synthetic.D > result->D) - successes++; - } - free(ys); - } - - /* End of parallelized part */ - } - - free(xs_head); - - if (retval == PLFIT_SUCCESS) { - result->p = successes / ((double)num_trials); - } else { - PLFIT_ERROR("cannot calculate exact p-value", retval); - } - - return retval; -} - -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_sorted(double* xs, size_t n, double xmin, - const plfit_continuous_options_t* options, plfit_result_t *result) { - double *begin, *end; - - if (!options) - options = &plfit_continuous_default_options; - - begin = xs; - end = xs + n; - while (begin < end && *begin < xmin) - begin++; - - PLFIT_CHECK(plfit_i_estimate_alpha_continuous_sorted(begin, end-begin, - xmin, &result->alpha)); - PLFIT_CHECK(plfit_i_ks_test_continuous(begin, end, result->alpha, - xmin, &result->D)); - - if (options->finite_size_correction) - plfit_i_perform_finite_size_correction(result, end-begin); - result->xmin = xmin; - - PLFIT_CHECK(plfit_log_likelihood_continuous(begin, end-begin, result->alpha, - result->xmin, &result->L)); - PLFIT_CHECK(plfit_i_calculate_p_value_continuous(xs, n, options, 1, result)); - - 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; - - PLFIT_CHECK(plfit_i_copy_and_sort(xs, n, &xs_copy)); - PLFIT_CHECK(plfit_estimate_alpha_continuous_sorted(xs_copy, n, xmin, - options, result)); - free(xs_copy); - - 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 **probes; /**< Pointers to the elements of the array that will be probed */ - size_t num_probes; /**< Number of probes */ - plfit_result_t last; /**< Result of the last evaluation */ -} plfit_continuous_xmin_opt_data_t; - -static 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->probes[(long int)x]; - - data->last.xmin = *begin; - -#ifdef PLFIT_DEBUG - printf("Trying with probes[%ld] = %.4f\n", (long int)x, *begin); -#endif - - plfit_i_estimate_alpha_continuous_sorted(begin, 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; -} - -static 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; -} - -static int plfit_i_continuous_xmin_opt_linear_scan( - plfit_continuous_xmin_opt_data_t* opt_data, plfit_result_t* best_result, - size_t* best_n) { - /* this must be signed because OpenMP with Windows MSVC needs signed for - * loop index variables. ssize_t will not work because that is a POSIX - * extension */ - ptrdiff_t i = 0; /* initialize to work around incorrect warning issued by Clang 9.0 */ - plfit_result_t global_best_result; - size_t global_best_n; - - /* Prepare some variables */ - global_best_n = 0; - global_best_result.D = DBL_MAX; - global_best_result.xmin = 0; - global_best_result.alpha = 0; - - /* Due to the OpenMP parallelization, we do things as follows. Each - * OpenMP thread will search for the best D-score on its own and store - * the result in a private local_best_result variable. The end of the - * parallel block contains a critical section that threads will enter - * one by one and compare their private local_best_result with a - * global_best that is shared among the threads. - */ -#ifdef _OPENMP -#pragma omp parallel shared(global_best_result, global_best_n) private(i) firstprivate(opt_data) -#endif - { - /* These variables are private since they are declared within the - * parallel block */ - plfit_result_t local_best_result; - plfit_continuous_xmin_opt_data_t local_opt_data = *opt_data; - size_t local_best_n; - - /* Initialize the local_best_result and local_best_n variables */ - local_best_n = 0; - local_best_result.D = DBL_MAX; - local_best_result.xmin = 0; - local_best_result.alpha = 0; - local_best_result.p = NAN; - local_best_result.L = NAN; - - /* The range of the for loop below is divided among the threads. - * nowait means that there will be no implicit barrier at the end - * of the loop so threads that get there earlier can enter the - * critical section without waiting for the others */ -#ifdef _OPENMP -#pragma omp for nowait schedule(dynamic,10) -#endif - for (i = 0; i < local_opt_data.num_probes-1; i++) { - plfit_i_continuous_xmin_opt_evaluate(&local_opt_data, i); - if (local_opt_data.last.D < local_best_result.D) { -#ifdef PLFIT_DEBUG - printf("Found new local best at %g with D=%g\n", - local_opt_data.last.xmin, local_opt_data.last.D); -#endif - local_best_result = local_opt_data.last; - local_best_n = local_opt_data.end - local_opt_data.probes[i] + 1; - } - } - - /* Critical section that finds the global best result from the - * local ones collected by each thread */ -#ifdef _OPENMP -#pragma omp critical -#endif - if (local_best_result.D < global_best_result.D) { - global_best_result = local_best_result; - global_best_n = local_best_n; -#ifdef PLFIT_DEBUG - printf("Found new global best at %g with D=%g\n", global_best_result.xmin, - global_best_result.D); -#endif - } - } - - *best_result = global_best_result; - *best_n = global_best_n; - -#ifdef PLFIT_DEBUG - printf("Returning global best: %g\n", best_result->xmin); -#endif - - return PLFIT_SUCCESS; -} - -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 = { - /* alpha = */ NAN, - /* xmin = */ NAN, - /* L = */ NAN, - /* D = */ NAN, - /* p = */ NAN - }; - - int success; - size_t i, best_n, num_uniques = 0; - double x, *px, **uniques; - - DATA_POINTS_CHECK; - - /* Sane defaults */ - best_n = n; - if (!options) - options = &plfit_continuous_default_options; - - /* Make a copy of xs and sort it */ - PLFIT_CHECK(plfit_i_copy_and_sort(xs, n, &opt_data.begin)); - 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. */ - uniques = unique_element_pointers(opt_data.begin, opt_data.end, &num_uniques); - if (uniques == 0) - PLFIT_ERROR("cannot fit continuous power-law", PLFIT_ENOMEM); - - /* We will now determine the best xmin that yields the lowest D-score. The - * 'success' variable will denote whether the search procedure we tried was - * successful. If it is false after having exhausted all options, we fall - * back to a linear search. */ - success = 0; - switch (options->xmin_method) { - case PLFIT_GSS_OR_LINEAR: - /* Try golden section search first. */ - if (num_uniques > 5) { - opt_data.probes = uniques; - opt_data.num_probes = num_uniques; - gss_parameter_init(&gss_param); - success = (gss(0, opt_data.num_probes-5, &x, 0, - plfit_i_continuous_xmin_opt_evaluate, - plfit_i_continuous_xmin_opt_progress, &opt_data, &gss_param) == 0); - if (success) { - px = opt_data.probes[(int)x]; - best_n = opt_data.end-px+1; - best_result = opt_data.last; - } - } - break; - - case PLFIT_STRATIFIED_SAMPLING: - if (num_uniques >= 50) { - /* Try stratified sampling to narrow down the interval where the minimum - * is likely to reside. We check 10% of the unique items, distributed - * evenly, find the one with the lowest D-score, and then check the - * area around it more thoroughly. */ - const size_t subdivision_length = 10; - size_t num_strata = num_uniques / subdivision_length; - double **strata = calloc(num_strata, sizeof(double*)); - int error_code; - - for (i = 0; i < num_strata; i++) { - strata[i] = uniques[i * subdivision_length]; - } - - opt_data.probes = strata; - opt_data.num_probes = num_strata; - error_code = plfit_i_continuous_xmin_opt_linear_scan(&opt_data, &best_result, &best_n); - if (error_code != PLFIT_SUCCESS) { - free(strata); - return error_code; - } - - opt_data.num_probes = 0; - for (i = 0; i < num_strata; i++) { - if (*strata[i] == best_result.xmin) { - /* Okay, scan more thoroughly from strata[i-1] to strata[i+1], - * which is from uniques[(i-1)*subdivision_length] to - * uniques[(i+1)*subdivision_length */ - opt_data.probes = uniques + (i > 0 ? (i-1)*subdivision_length : 0); - opt_data.num_probes = 0; - if (i != 0) - opt_data.num_probes += subdivision_length; - if (i != num_strata-1) - opt_data.num_probes += subdivision_length; - break; - } - } - - free(strata); - if (opt_data.num_probes > 0) { - /* Do a strict linear scan in the subrange determined above */ - PLFIT_CHECK( - plfit_i_continuous_xmin_opt_linear_scan( - &opt_data, &best_result, &best_n - ) - ); - success = 1; - } else { - /* This should not happen, but we handle it anyway */ - success = 0; - } - } - break; - - default: - /* Just use the linear search */ - break; - } - - if (!success) { - /* More advanced search methods failed or were skipped; try linear search */ - opt_data.probes = uniques; - opt_data.num_probes = num_uniques; - PLFIT_CHECK(plfit_i_continuous_xmin_opt_linear_scan(&opt_data, &best_result, &best_n)); - success = 1; - } - - /* Get rid of the uniques array, we don't need it any more */ - free(uniques); - - /* Sort out the result */ - *result = best_result; - if (options->finite_size_correction) - plfit_i_perform_finite_size_correction(result, best_n); - - PLFIT_CHECK(plfit_log_likelihood_continuous(opt_data.begin + n - best_n, best_n, - result->alpha, result->xmin, &result->L)); - PLFIT_CHECK(plfit_i_calculate_p_value_continuous(opt_data.begin, n, options, 0, result)); - - /* 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; - -static double plfit_i_logsum_discrete(double* begin, double* end, double xmin) { - double logsum = 0.0; - for (; begin != end; begin++) - logsum += log(*begin); - return logsum; -} - -static 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; -} - -static 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 */ - double lnhzeta_x=NAN; - double lnhzeta_deriv_x=NAN; - - 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; - result = x[0] * data->logsum + data->m * hsl_sf_lnhzeta(x[0], data->xmin); - } else { - hsl_sf_lnhzeta_deriv_tuple(x[0], data->xmin, &lnhzeta_x, &lnhzeta_deriv_x); - g[0] = data->logsum + data->m * lnhzeta_deriv_x; - result = x[0] * data->logsum + data->m * lnhzeta_x; - } - -#ifdef PLFIT_DEBUG - printf(" - Gradient: %.4f\n", g[0]); - printf(" - Result: %.4f\n", result); -#endif - - return result; -} - -static 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; -} - -static 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 * hsl_sf_lnhzeta(curr_alpha, xmin); - if (L > L_max) { - L_max = L; - best_alpha = curr_alpha; - } - } - - *alpha = best_alpha; - - return PLFIT_SUCCESS; -} - -static 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(¶m); - 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, ¶m); - - if (ret < 0 && - ret != LBFGSERR_ROUNDING_ERROR && - ret != LBFGSERR_MAXIMUMLINESEARCH && - ret != LBFGSERR_MINIMUMSTEP && - 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; -} - -static 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); - } -} - -static 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, lnhzeta, x; - int m = 0; - - n = xs_end - xs; - lnhzeta = hsl_sf_lnhzeta(alpha, xmin); - - while (xs < xs_end) { - double d; - - x = *xs; - - /* Re the next line: this used to be the following: - * - * fabs( 1 - hzeta(alpha, x) / hzeta(alpha, xmin) - m / n) - * - * However, using the Hurwitz zeta directly sometimes yields - * underflows (see Github pull request #17 and related issues). - * hzeta(alpha, x) / hzeta(alpha, xmin) can be replaced with - * exp(lnhzeta(alpha, x) - lnhzeta(alpha, xmin)), but then - * we have 1 - exp(something), which is better to calculate - * with a dedicated expm1() function. - */ - d = fabs( expm1( hsl_sf_lnhzeta(alpha, x) - lnhzeta ) + m / n); - - if (d > result) - result = d; - - do { - xs++; m++; - } while (xs < xs_end && *xs == x); - } - - *D = result; - - return PLFIT_SUCCESS; -} - -static int plfit_i_calculate_p_value_discrete(double* xs, size_t n, - const plfit_discrete_options_t* options, plfit_bool_t xmin_fixed, - plfit_result_t *result) { - long int num_trials; - long int successes = 0; - double *xs_head; - size_t num_smaller; - plfit_discrete_options_t options_no_p_value = *options; - int retval = PLFIT_SUCCESS; - - if (options->p_value_method == PLFIT_P_VALUE_SKIP) { - /* skipping p-value calculation */ - result->p = NAN; - return PLFIT_SUCCESS; - } - - if (options->p_value_method == PLFIT_P_VALUE_APPROXIMATE) { - /* p-value approximation; most likely an upper bound */ - num_smaller = count_smaller(xs, xs + n, result->xmin); - result->p = plfit_ks_test_one_sample_p(result->D, n - num_smaller); - return PLFIT_SUCCESS; - } - - options_no_p_value.p_value_method = PLFIT_P_VALUE_SKIP; - num_trials = (long int)(0.25 / options->p_value_precision / options->p_value_precision); - if (num_trials <= 0) { - PLFIT_ERROR("invalid p-value precision", PLFIT_EINVAL); - } - - /* Extract the head of xs that contains elements smaller than xmin */ - xs_head = extract_smaller(xs, xs+n, result->xmin, &num_smaller); - if (xs_head == 0) - PLFIT_ERROR("cannot calculate exact p-value", PLFIT_ENOMEM); - -#ifdef _OPENMP -#pragma omp parallel -#endif - { - /* Parallel section starts here. If we are compiling using OpenMP, each - * thread will use its own RNG that is seeded from the master RNG. If - * we are compiling without OpenMP, there is only one thread and it uses - * the master RNG. This section must be critical to ensure that only one - * thread is using the master RNG at the same time. */ -#ifdef _OPENMP - plfit_mt_rng_t private_rng; -#endif - plfit_mt_rng_t *p_rng; - double *ys; - long int i; - plfit_result_t result_synthetic; - -#ifdef _OPENMP -#pragma omp critical - { - p_rng = &private_rng; - plfit_mt_init_from_rng(p_rng, options->rng); - } -#else - p_rng = options->rng; -#endif - - /* Allocate memory to sample into */ - ys = calloc(n, sizeof(double)); - if (ys == 0) { - retval = PLFIT_ENOMEM; - } else { - /* The main for loop starts here. */ -#ifdef _OPENMP -#pragma omp for reduction(+:successes) -#endif - for (i = 0; i < num_trials; i++) { - plfit_i_resample_discrete(xs_head, num_smaller, n, result->alpha, - result->xmin, n, p_rng, ys); - if (xmin_fixed) { - plfit_estimate_alpha_discrete(ys, n, result->xmin, - &options_no_p_value, &result_synthetic); - } else { - plfit_discrete(ys, n, &options_no_p_value, &result_synthetic); - } - if (result_synthetic.D > result->D) - successes++; - } - - free(ys); - } - - /* End of parallelized part */ - } - - free(xs_head); - - if (retval == PLFIT_SUCCESS) { - result->p = successes / ((double)num_trials); - } else { - PLFIT_ERROR("cannot calculate exact p-value", retval); - } - - return retval; -} - -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 * hsl_sf_lnhzeta(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, *begin, *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); - } - } - - PLFIT_CHECK(plfit_i_copy_and_sort(xs, n, &xs_copy)); - - begin = xs_copy; end = xs_copy + n; - while (begin < end && *begin < xmin) - begin++; - - PLFIT_CHECK(plfit_i_estimate_alpha_discrete(begin, end-begin, xmin, &result->alpha, - options, /* sorted = */ 1)); - PLFIT_CHECK(plfit_i_ks_test_discrete(begin, end, result->alpha, xmin, &result->D)); - - result->xmin = xmin; - if (options->finite_size_correction) - plfit_i_perform_finite_size_correction(result, end-begin); - - PLFIT_CHECK(plfit_log_likelihood_discrete(begin, end-begin, result->alpha, - result->xmin, &result->L)); - PLFIT_CHECK(plfit_i_calculate_p_value_discrete(xs, n, options, 1, result)); - - 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; - int 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); - } - } - - PLFIT_CHECK(plfit_i_copy_and_sort(xs, n, &xs_copy)); - - best_result.D = DBL_MAX; - best_result.xmin = 1; - best_result.alpha = 1; - best_n = 0; - - /* Skip initial values from xs_copy until we get to a positive element or - * until we reach the end of the array */ - px = xs_copy; end = px + n; end_xmin = end - 1; - while (px < end && *px < 1) { - px++; - } - - /* Make sure there are at least three distinct values if possible */ - m = px - xs_copy; - prev_x = *end_xmin; - while (end_xmin > px && *end_xmin == prev_x) { - end_xmin--; - } - prev_x = *end_xmin; - while (end_xmin > px && *end_xmin == prev_x) { - end_xmin--; - } - - prev_x = 0; - end_xmin++; - while (px < end_xmin) { - while (px < end_xmin && *px == prev_x) { - px++; m++; - } - - PLFIT_CHECK( - plfit_i_estimate_alpha_discrete( - px, n-m, *px, &curr_alpha, options, /* sorted = */ 1 - ) - ); - PLFIT_CHECK( - 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); - - PLFIT_CHECK(plfit_log_likelihood_discrete(xs_copy+(n-best_n), best_n, - result->alpha, result->xmin, &result->L)); - PLFIT_CHECK(plfit_i_calculate_p_value_discrete(xs_copy, n, options, 0, result)); - - free(xs_copy); - - return PLFIT_SUCCESS; -} - -/***** resampling routines to generate synthetic replicates ****/ - -static int plfit_i_resample_continuous(double* xs_head, size_t num_smaller, - size_t n, double alpha, double xmin, size_t num_samples, plfit_mt_rng_t* rng, - double* result) -{ - size_t num_orig_samples, i; - - /* Calculate how many samples have to be drawn from xs_head */ - num_orig_samples = (size_t) plfit_rbinom(num_samples, num_smaller / (double)n, rng); - - /* Draw the samples from xs_head */ - for (i = 0; i < num_orig_samples; i++, result++) { - *result = xs_head[(size_t)plfit_runif(0, num_smaller, rng)]; - } - - /* Draw the remaining samples from the fitted distribution */ - PLFIT_CHECK(plfit_rpareto_array(xmin, alpha-1, num_samples-num_orig_samples, rng, - result)); - - return PLFIT_SUCCESS; -} - -int plfit_resample_continuous(double* xs, size_t n, double alpha, double xmin, - size_t num_samples, plfit_mt_rng_t* rng, double* result) { - double *xs_head; - size_t num_smaller = 0; - int retval; - - /* Extract the head of xs that contains elements smaller than xmin */ - xs_head = extract_smaller(xs, xs+n, xmin, &num_smaller); - if (xs_head == 0) - PLFIT_ERROR("cannot resample continuous dataset", PLFIT_ENOMEM); - - retval = plfit_i_resample_continuous(xs_head, num_smaller, n, alpha, xmin, - num_samples, rng, result); - - /* Free xs_head; we don't need it any more */ - free(xs_head); - - return retval; -} - -static int plfit_i_resample_discrete(double* xs_head, size_t num_smaller, size_t n, - double alpha, double xmin, size_t num_samples, plfit_mt_rng_t* rng, - double* result) -{ - size_t num_orig_samples, i; - - /* Calculate how many samples have to be drawn from xs_head */ - num_orig_samples = (size_t) plfit_rbinom(num_samples, num_smaller / (double)n, rng); - - /* Draw the samples from xs_head */ - for (i = 0; i < num_orig_samples; i++, result++) { - *result = xs_head[(size_t)plfit_runif(0, num_smaller, rng)]; - } - - /* Draw the remaining samples from the fitted distribution */ - PLFIT_CHECK(plfit_rzeta_array((long int)xmin, alpha, - num_samples-num_orig_samples, rng, result)); - - return PLFIT_SUCCESS; -} - -int plfit_resample_discrete(double* xs, size_t n, double alpha, double xmin, - size_t num_samples, plfit_mt_rng_t* rng, double* result) { - double *xs_head; - size_t num_smaller = 0; - int retval; - - /* Extract the head of xs that contains elements smaller than xmin */ - xs_head = extract_smaller(xs, xs+n, xmin, &num_smaller); - if (xs_head == 0) - PLFIT_ERROR("cannot resample discrete dataset", PLFIT_ENOMEM); - - retval = plfit_i_resample_discrete(xs_head, num_smaller, n, alpha, xmin, - num_samples, rng, result); - - /* Free xs_head; we don't need it any more */ - free(xs_head); - - return retval; -} - -/******** calculating the p-value of a fitted model only *******/ - -int plfit_calculate_p_value_continuous(double* xs, size_t n, - const plfit_continuous_options_t* options, plfit_bool_t xmin_fixed, - plfit_result_t *result) { - double* xs_copy; - - PLFIT_CHECK(plfit_i_copy_and_sort(xs, n, &xs_copy)); - PLFIT_CHECK(plfit_i_calculate_p_value_continuous(xs_copy, n, options, - xmin_fixed, result)); - free(xs_copy); - - return PLFIT_SUCCESS; -} - -int plfit_calculate_p_value_discrete(double* xs, size_t n, - const plfit_discrete_options_t* options, plfit_bool_t xmin_fixed, - plfit_result_t *result) { - double* xs_copy; - - PLFIT_CHECK(plfit_i_copy_and_sort(xs, n, &xs_copy)); - PLFIT_CHECK(plfit_i_calculate_p_value_discrete(xs_copy, n, options, - xmin_fixed, result)); - free(xs_copy); - - return PLFIT_SUCCESS; -} diff --git a/src/rigraph/vendor/plfit/plfit.h b/src/rigraph/vendor/plfit/plfit.h deleted file mode 100644 index afc0348..0000000 --- a/src/rigraph/vendor/plfit/plfit.h +++ /dev/null @@ -1,140 +0,0 @@ -/* vim:set ts=4 sw=4 sts=4 et: */ -/* 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 2 of the License, or (at - * your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public 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 -#include "plfit_mt.h" -#include "plfit_version.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 - -typedef unsigned short int plfit_bool_t; - -typedef enum { - PLFIT_LINEAR_ONLY, - PLFIT_STRATIFIED_SAMPLING, - PLFIT_GSS_OR_LINEAR, - PLFIT_DEFAULT_CONTINUOUS_METHOD = PLFIT_STRATIFIED_SAMPLING -} 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 enum { - PLFIT_P_VALUE_SKIP, - PLFIT_P_VALUE_APPROXIMATE, - PLFIT_P_VALUE_EXACT, - PLFIT_DEFAULT_P_VALUE_METHOD = PLFIT_P_VALUE_EXACT -} plfit_p_value_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_p_value_method_t p_value_method; - double p_value_precision; - plfit_mt_rng_t* rng; -} 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_p_value_method_t p_value_method; - double p_value_precision; - plfit_mt_rng_t* rng; -} 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_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); - -/***** resampling routines to generate synthetic replicates ****/ - -int plfit_resample_continuous(double* xs, size_t n, double alpha, double xmin, - size_t num_samples, plfit_mt_rng_t* rng, double* result); -int plfit_resample_discrete(double* xs, size_t n, double alpha, double xmin, - size_t num_samples, plfit_mt_rng_t* rng, double* result); - -/******** calculating the p-value of a fitted model only *******/ - -int plfit_calculate_p_value_continuous(double* xs, size_t n, - const plfit_continuous_options_t* options, plfit_bool_t xmin_fixed, - plfit_result_t *result); -int plfit_calculate_p_value_discrete(double* xs, size_t n, - const plfit_discrete_options_t* options, plfit_bool_t xmin_fixed, - plfit_result_t *result); - -/************* calculating descriptive statistics **************/ - -int plfit_moments(double* data, size_t n, double* mean, double* variance, - double* skewness, double* kurtosis); - -__END_DECLS - -#endif /* __PLFIT_H__ */ diff --git a/src/rigraph/vendor/plfit/plfit_error.c b/src/rigraph/vendor/plfit/plfit_error.c deleted file mode 100644 index d93d1c0..0000000 --- a/src/rigraph/vendor/plfit/plfit_error.c +++ /dev/null @@ -1,66 +0,0 @@ -/* 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 2 of the License, or (at - * your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public 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 -#include -#include "plfit_error.h" -#include "platform.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_printignore; -#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_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/src/rigraph/vendor/plfit/plfit_error.h b/src/rigraph/vendor/plfit/plfit_error.h deleted file mode 100644 index e542997..0000000 --- a/src/rigraph/vendor/plfit/plfit_error.h +++ /dev/null @@ -1,86 +0,0 @@ -/* plfit_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 2 of the License, or (at - * your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public 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/src/rigraph/vendor/plfit/plfit_mt.h b/src/rigraph/vendor/plfit/plfit_mt.h deleted file mode 100644 index c7ed0dc..0000000 --- a/src/rigraph/vendor/plfit/plfit_mt.h +++ /dev/null @@ -1,101 +0,0 @@ -/* plfit_mt.h - * - * Mersenne Twister random number generator, based on the implementation of - * Michael Brundage (which has been placed in the public domain). - * - * Author: Tamas Nepusz (original by Michael Brundage) - * - * See the following URL for the original implementation: - * http://www.qbrundage.com/michaelb/pubs/essays/random_number_generation.html - * - * This file has been placed in the public domain. - */ - -#ifndef __PLFIT_MT_H__ -#define __PLFIT_MT_H__ - -/* VS 2010, i.e. _MSC_VER == 1600, already has stdint.h */ -#if defined(_MSC_VER) && _MSC_VER < 1600 -# define uint32_t unsigned __int32 -#else -# include -#endif - -#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_MT_LEN 624 - -/** - * \def PLFIT_MT_RAND_MAX - * - * The maximum random number that \c plfit_mt_random() can generate. - */ -#define PLFIT_MT_RAND_MAX 0xFFFFFFFF - -/** - * Struct that stores the internal state of a Mersenne Twister random number - * generator. - */ -typedef struct { - int mt_index; - uint32_t mt_buffer[PLFIT_MT_LEN]; -} plfit_mt_rng_t; - -/** - * \brief Initializes a Mersenne Twister random number generator. - * - * The random number generator is seeded with random 32-bit numbers obtained - * from the \em built-in random number generator using consecutive calls to - * \c rand(). - * - * \param rng the random number generator to initialize - */ -void plfit_mt_init(plfit_mt_rng_t* rng); - -/** - * \brief Initializes a Mersenne Twister random number generator, seeding it - * from another one. - * - * The random number generator is seeded with random 32-bit numbers obtained - * from another, initialized Mersenne Twister random number generator. - * - * \param rng the random number generator to initialize - * \param seeder the random number generator that will seed the one being - * initialized. When null, the random number generator will - * be initialized from the built-in RNG as if \ref plfit_mt_init() - * was called. - */ -void plfit_mt_init_from_rng(plfit_mt_rng_t* rng, plfit_mt_rng_t* seeder); - -/** - * \brief Returns the next 32-bit random number from the given Mersenne Twister - * random number generator. - * - * \param rng the random number generator to use - * \return the next 32-bit random number from the generator - */ -uint32_t plfit_mt_random(plfit_mt_rng_t* rng); - -/** - * \brief Returns a uniformly distributed double from the interval [0;1) - * based on the next value of the given Mersenne Twister random number - * generator. - * - * \param rng the random number generator to use - * \return a uniformly distributed random number from the interval [0;1) - */ -double plfit_mt_uniform_01(plfit_mt_rng_t* rng); - -__END_DECLS - -#endif diff --git a/src/rigraph/vendor/plfit/plfit_sampling.h b/src/rigraph/vendor/plfit/plfit_sampling.h deleted file mode 100644 index f63407c..0000000 --- a/src/rigraph/vendor/plfit/plfit_sampling.h +++ /dev/null @@ -1,177 +0,0 @@ -/* plfit_sampling.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 2 of the License, or (at - * your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public 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 __SAMPLING_H__ -#define __SAMPLING_H__ - -#include -#include "plfit_mt.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 - -/** - * Draws a sample from a binomial distribution with the given count and - * probability values. - * - * This function is borrowed from R; see the corresponding license in - * \c rbinom.c. The return value is always an integer. - * - * The function is \em not thread-safe. - * - * \param n the number of trials - * \param p the success probability of each trial - * \param rng the Mersenne Twister random number generator to use - * \return the value drawn from the given binomial distribution. - */ -double plfit_rbinom(double n, double p, plfit_mt_rng_t* rng); - -/** - * Draws a sample from a Pareto distribution with the given minimum value and - * power-law exponent. - * - * \param xmin the minimum value of the distribution. Must be positive. - * \param alpha the exponent. Must be positive - * \param rng the Mersenne Twister random number generator to use - * - * \return the sample or NaN if one of the parameters is invalid - */ -extern double plfit_rpareto(double xmin, double alpha, plfit_mt_rng_t* rng); - -/** - * Draws a given number of samples from a Pareto distribution with the given - * minimum value and power-law exponent. - * - * \param xmin the minimum value of the distribution. Must be positive. - * \param alpha the exponent. Must be positive - * \param n the number of samples to draw - * \param rng the Mersenne Twister random number generator to use - * \param result the array where the result should be written. It must - * have enough space to store n items - * - * \return \c PLFIT_EINVAL if one of the parameters is invalid, zero otherwise - */ -int plfit_rpareto_array(double xmin, double alpha, size_t n, plfit_mt_rng_t* rng, - double* result); - -/** - * Draws a sample from a zeta distribution with the given minimum value and - * power-law exponent. - * - * \param xmin the minimum value of the distribution. Must be positive. - * \param alpha the exponent. Must be positive - * \param rng the Mersenne Twister random number generator to use - * - * \return the sample or NaN if one of the parameters is invalid - */ -extern double plfit_rzeta(long int xmin, double alpha, plfit_mt_rng_t* rng); - -/** - * Draws a given number of samples from a zeta distribution with the given - * minimum value and power-law exponent. - * - * \param xmin the minimum value of the distribution. Must be positive. - * \param alpha the exponent. Must be positive - * \param n the number of samples to draw - * \param rng the Mersenne Twister random number generator to use - * \param result the array where the result should be written. It must - * have enough space to store n items - * - * \return \c PLFIT_EINVAL if one of the parameters is invalid, zero otherwise - */ -int plfit_rzeta_array(long int xmin, double alpha, size_t n, plfit_mt_rng_t* rng, - double* result); - -/** - * Draws a sample from a uniform distribution with the given lower and - * upper bounds. - * - * The lower bound is inclusive, the uppoer bound is not. - * - * \param lo the lower bound - * \param hi the upper bound - * \param rng the Mersenne Twister random number generator to use - * \return the value drawn from the given uniform distribution. - */ -extern double plfit_runif(double lo, double hi, plfit_mt_rng_t* rng); - -/** - * Draws a sample from a uniform distribution over the [0; 1) interval. - * - * The interval is closed from the left and open from the right. - * - * \param rng the Mersenne Twister random number generator to use - * \return the value drawn from the given uniform distribution. - */ -extern double plfit_runif_01(plfit_mt_rng_t* rng); - -/** - * Random sampler using Walker's alias method. - */ -typedef struct { - long int num_bins; /**< Number of bins */ - long int* indexes; /**< Index of the "other" element in each bin */ - double* probs; /**< Probability of drawing the "own" element from a bin */ -} plfit_walker_alias_sampler_t; - -/** - * \brief Initializes the sampler with item probabilities. - * - * \param sampler the sampler to initialize - * \param ps pointer to an array containing a value proportional to the - * sampling probability of each item in the set being sampled. - * \param n the number of items in the array - * \return error code - */ -int plfit_walker_alias_sampler_init(plfit_walker_alias_sampler_t* sampler, - double* ps, size_t n); - -/** - * \brief Destroys an initialized sampler and frees the allocated memory. - * - * \param sampler the sampler to destroy - */ -void plfit_walker_alias_sampler_destroy(plfit_walker_alias_sampler_t* sampler); - -/** - * \brief Draws a given number of samples from the sampler and writes them - * to a given array. - * - * \param sampler the sampler to use - * \param xs pointer to an array where the sampled items should be - * written - * \param n the number of samples to draw - * \param rng the Mersenne Twister random number generator to use - * \return error code - */ -int plfit_walker_alias_sampler_sample(const plfit_walker_alias_sampler_t* sampler, - long int* xs, size_t n, plfit_mt_rng_t* rng); - -__END_DECLS - -#endif diff --git a/src/rigraph/vendor/plfit/plfit_version.h b/src/rigraph/vendor/plfit/plfit_version.h deleted file mode 100644 index 1212dd1..0000000 --- a/src/rigraph/vendor/plfit/plfit_version.h +++ /dev/null @@ -1,28 +0,0 @@ -/* plfit_version.h - * - * Copyright (C) 2021 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 2 of the License, or (at - * your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public 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_VERSION_H -#define PLFIT_VERSION_H - -#define PLFIT_VERSION_MAJOR 0 -#define PLFIT_VERSION_MINOR 9 -#define PLFIT_VERSION_PATCH 3 -#define PLFIT_VERSION_STRING "0.9.3" - -#endif diff --git a/src/rigraph/vendor/plfit/rbinom.c b/src/rigraph/vendor/plfit/rbinom.c deleted file mode 100644 index 771f972..0000000 --- a/src/rigraph/vendor/plfit/rbinom.c +++ /dev/null @@ -1,208 +0,0 @@ -/* - * Mathlib : A C Library of Special Functions - * Copyright (C) 1998 Ross Ihaka - * Copyright (C) 2000-2002 The R Core Team - * Copyright (C) 2007 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 - * - * #include - * double rbinom(double nin, double pp) - * - * DESCRIPTION - * - * Random variates from the binomial distribution. - * - * REFERENCE - * - * Kachitvichyanukul, V. and Schmeiser, B. W. (1988). - * Binomial random variate generation. - * Communications of the ACM 31, 216-222. - * (Algorithm BTPEC). - */ - -/* - * Modifications for this file were performed by Tamas Nepusz to make it fit - * better with plfit. The license of the original file applies to the - * modifications as well. - */ - -#include -#include -#include -#include "plfit_sampling.h" -#include "platform.h" - -#define repeat for(;;) - -double plfit_rbinom(double nin, double pp, plfit_mt_rng_t* rng) -{ - /* FIXME: These should become THREAD_specific globals : */ - - static double c, fm, npq, p1, p2, p3, p4, qn; - static double xl, xll, xlr, xm, xr; - - static double psave = -1.0; - static int nsave = -1; - static 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 (!isfinite(nin)) return NAN; - r = floor(nin + 0.5); - if (r != nin) return NAN; - if (!isfinite(pp) || - /* n=0, p=0, p=1 are not errors */ - r < 0 || pp < 0. || pp > 1.) return NAN; - - if (r == 0 || pp == 0.) return 0; - if (pp == 1.) return r; - - n = (int) r; - - 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 = (int) 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 = plfit_runif_01(rng) * p4; - v = plfit_runif_01(rng); - /* triangular region */ - if (u <= p1) { - ix = (int)(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 = (int) x; - } else { - if (u > p3) { /* right tail */ - ix = (int)(xr - log(v) / xlr); - if (ix > n) - continue; - v = v * (u - p3) * xlr; - } else {/* left tail */ - ix = (int)(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 = plfit_runif_01(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; -} diff --git a/src/rigraph/vendor/plfit/sampling.c b/src/rigraph/vendor/plfit/sampling.c deleted file mode 100644 index 4becf13..0000000 --- a/src/rigraph/vendor/plfit/sampling.c +++ /dev/null @@ -1,312 +0,0 @@ -/* sampling.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 2 of the License, or (at - * your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public 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 -#include - -#include "igraph_random.h" - -#include "plfit_error.h" -#include "plfit_sampling.h" -#include "platform.h" - -inline double plfit_runif(double lo, double hi, plfit_mt_rng_t* rng) { - if (rng == 0) { - return RNG_UNIF(lo, hi); - } - return lo + plfit_mt_uniform_01(rng) * (hi-lo); -} - -inline double plfit_runif_01(plfit_mt_rng_t* rng) { - if (rng == 0) { - return RNG_UNIF01(); - } - return plfit_mt_uniform_01(rng); -} - -inline double plfit_rpareto(double xmin, double alpha, plfit_mt_rng_t* rng) { - if (alpha <= 0 || xmin <= 0) - return NAN; - - /* 1-u is used in the base here because we want to avoid the case of - * sampling zero */ - return pow(1-plfit_runif_01(rng), -1.0 / alpha) * xmin; -} - -int plfit_rpareto_array(double xmin, double alpha, size_t n, plfit_mt_rng_t* rng, - double* result) { - double gamma; - - if (alpha <= 0 || xmin <= 0) - return PLFIT_EINVAL; - - if (result == 0 || n == 0) - return PLFIT_SUCCESS; - - gamma = -1.0 / alpha; - while (n > 0) { - /* 1-u is used in the base here because we want to avoid the case of - * sampling zero */ - *result = pow(1-plfit_runif_01(rng), gamma) * xmin; - result++; n--; - } - - return PLFIT_SUCCESS; -} - -inline double plfit_rzeta(long int xmin, double alpha, plfit_mt_rng_t* rng) { - double u, v, t; - long int x; - double alpha_minus_1 = alpha-1; - double minus_1_over_alpha_minus_1 = -1.0 / (alpha-1); - double b; - double one_over_b_minus_1; - - if (alpha <= 0 || xmin < 1) - return NAN; - - xmin = (long int) round(xmin); - - /* Rejection sampling for the win. We use Y=floor(U^{-1/alpha} * xmin) as the - * envelope distribution, similarly to Chapter X.6 of Luc Devroye's book - * (where xmin is assumed to be 1): http://luc.devroye.org/chapter_ten.pdf - * - * Some notes that should help me recover what I was doing: - * - * p_i = 1/zeta(alpha, xmin) * i^-alpha - * q_i = (xmin/i)^{alpha-1} - (xmin/(i+1))^{alpha-1} - * = (i/xmin)^{1-alpha} - ((i+1)/xmin)^{1-alpha} - * = [i^{1-alpha} - (i+1)^{1-alpha}] / xmin^{1-alpha} - * - * p_i / q_i attains its maximum at xmin=i, so the rejection constant is: - * - * c = p_xmin / q_xmin - * - * We have to accept the sample if V <= (p_i / q_i) * (q_xmin / p_xmin) = - * (i/xmin)^-alpha * [xmin^{1-alpha} - (xmin+1)^{1-alpha}] / [i^{1-alpha} - (i+1)^{1-alpha}] = - * [xmin - xmin^alpha / (xmin+1)^{alpha-1}] / [i - i^alpha / (i+1)^{alpha-1}] = - * xmin/i * [1-(xmin/(xmin+1))^{alpha-1}]/[1-(i/(i+1))^{alpha-1}] - * - * In other words (and substituting i with X, which is the same), - * - * V * (X/xmin) <= [1 - (1+1/xmin)^{1-alpha}] / [1 - (1+1/i)^{1-alpha}] - * - * Let b := (1+1/xmin)^{alpha-1} and let T := (1+1/i)^{alpha-1}. Then: - * - * V * (X/xmin) <= [(b-1)/b] / [(T-1)/T] - * V * (X/xmin) * (T-1) / (b-1) <= T / b - * - * which is the same as in Devroye's book, except for the X/xmin term, and - * the definition of b. - */ - b = pow(1 + 1.0/xmin, alpha_minus_1); - one_over_b_minus_1 = 1.0/(b-1); - do { - do { - u = plfit_runif_01(rng); - v = plfit_runif_01(rng); - /* 1-u is used in the base here because we want to avoid the case of - * having zero in x */ - x = (long int) floor(pow(1-u, minus_1_over_alpha_minus_1) * xmin); - } while (x < xmin); - t = pow((x+1.0)/x, alpha_minus_1); - } while (v*x*(t-1)*one_over_b_minus_1*b > t*xmin); - - return x; -} - -int plfit_rzeta_array(long int xmin, double alpha, size_t n, plfit_mt_rng_t* rng, - double* result) { - double u, v, t; - long int x; - double alpha_minus_1 = alpha-1; - double minus_1_over_alpha_minus_1 = -1.0 / (alpha-1); - double b, one_over_b_minus_1; - - if (alpha <= 0 || xmin < 1) - return PLFIT_EINVAL; - - if (result == 0 || n == 0) - return PLFIT_SUCCESS; - - /* See the comments in plfit_rzeta for an explanation of the algorithm - * below. */ - xmin = (long int) round(xmin); - b = pow(1 + 1.0/xmin, alpha_minus_1); - one_over_b_minus_1 = 1.0/(b-1); - - while (n > 0) { - do { - do { - u = plfit_runif_01(rng); - v = plfit_runif_01(rng); - /* 1-u is used in the base here because we want to avoid the case of - * having zero in x */ - x = (long int) floor(pow(1-u, minus_1_over_alpha_minus_1) * xmin); - } while (x < xmin); /* handles overflow as well */ - t = pow((x+1.0)/x, alpha_minus_1); - } while (v*x*(t-1)*one_over_b_minus_1*b > t*xmin); - *result = x; - if (x < 0) return PLFIT_EINVAL; - result++; n--; - } - - return PLFIT_SUCCESS; -} - -int plfit_walker_alias_sampler_init(plfit_walker_alias_sampler_t* sampler, - double* ps, size_t n) { - double *p, *p2, *ps_end; - double sum; - long int *short_sticks, *long_sticks; - long int num_short_sticks, num_long_sticks; - long int i; - - if (n > LONG_MAX) { - return PLFIT_EINVAL; - } - - sampler->num_bins = (long int) n; - - ps_end = ps + n; - - /* Initialize indexes and probs */ - sampler->indexes = (long int*)calloc(n, sizeof(long int)); - if (sampler->indexes == 0) { - return PLFIT_ENOMEM; - } - sampler->probs = (double*)calloc(n, sizeof(double)); - if (sampler->probs == 0) { - free(sampler->indexes); - return PLFIT_ENOMEM; - } - - /* Normalize the probability vector; count how many short and long sticks - * are there initially */ - for (sum = 0.0, p = ps; p != ps_end; p++) { - sum += *p; - } - sum = n / sum; - - num_short_sticks = num_long_sticks = 0; - for (p = ps, p2 = sampler->probs; p != ps_end; p++, p2++) { - *p2 = *p * sum; - if (*p2 < 1) { - num_short_sticks++; - } else if (*p2 > 1) { - num_long_sticks++; - } - } - - /* Allocate space for short & long stick indexes */ - long_sticks = (long int*)calloc(num_long_sticks, sizeof(long int)); - if (long_sticks == 0) { - free(sampler->probs); - free(sampler->indexes); - return PLFIT_ENOMEM; - } - short_sticks = (long int*)calloc(num_long_sticks, sizeof(long int)); - if (short_sticks == 0) { - free(sampler->probs); - free(sampler->indexes); - free(long_sticks); - return PLFIT_ENOMEM; - } - - /* Initialize short_sticks and long_sticks */ - num_short_sticks = num_long_sticks = 0; - for (i = 0, p = sampler->probs; i < n; i++, p++) { - if (*p < 1) { - short_sticks[num_short_sticks++] = i; - } else if (*p > 1) { - long_sticks[num_long_sticks++] = i; - } - } - - /* Prepare the index table */ - while (num_short_sticks && num_long_sticks) { - long int short_index, long_index; - short_index = short_sticks[--num_short_sticks]; - long_index = long_sticks[num_long_sticks-1]; - sampler->indexes[short_index] = long_index; - sampler->probs[long_index] = /* numerical stability */ - (sampler->probs[long_index] + sampler->probs[short_index]) - 1; - if (sampler->probs[long_index] < 1) { - short_sticks[num_short_sticks++] = long_index; - num_long_sticks--; - } - } - - /* Fix numerical stability issues */ - while (num_long_sticks) { - i = long_sticks[--num_long_sticks]; - sampler->probs[i] = 1; - } - while (num_short_sticks) { - i = short_sticks[--num_short_sticks]; - sampler->probs[i] = 1; - } - - free(short_sticks); - free(long_sticks); - - return PLFIT_SUCCESS; -} - - -void plfit_walker_alias_sampler_destroy(plfit_walker_alias_sampler_t* sampler) { - if (sampler->indexes) { - free(sampler->indexes); - sampler->indexes = 0; - } - if (sampler->probs) { - free(sampler->probs); - sampler->probs = 0; - } -} - - -int plfit_walker_alias_sampler_sample(const plfit_walker_alias_sampler_t* sampler, - long int *xs, size_t n, plfit_mt_rng_t* rng) { - double u; - long int j; - long int *x; - - x = xs; - - if (rng == 0) { - /* Using built-in RNG */ - while (n > 0) { - u = RNG_UNIF01(); - j = RNG_INTEGER(0, sampler->num_bins - 1); - *x = (u < sampler->probs[j]) ? j : sampler->indexes[j]; - n--; x++; - } - } else { - /* Using Mersenne Twister */ - while (n > 0) { - u = plfit_mt_uniform_01(rng); - j = plfit_mt_random(rng) % sampler->num_bins; - *x = (u < sampler->probs[j]) ? j : sampler->indexes[j]; - n--; x++; - } - } - - return PLFIT_SUCCESS; -} diff --git a/src/rigraph/vendor/simpleraytracer/Color.cpp b/src/rigraph/vendor/simpleraytracer/Color.cpp deleted file mode 100644 index 3b31cf0..0000000 --- a/src/rigraph/vendor/simpleraytracer/Color.cpp +++ /dev/null @@ -1,96 +0,0 @@ -#include "Color.h" -#include "unit_limiter.h" - -namespace igraph { - -Color::Color() -{ -} - -Color::Color(double vRed, double vGreen, double vBlue, - double vTransparent) -{ - Red(vRed); - Green(vGreen); - Blue(vBlue); - Transparent(vTransparent); -} - -Color::~Color() -{ -} - -// returns multiplication of a scalar with this vector -Color Color::operator* (double vRhs) const -{ - return Color(mRed*vRhs, mGreen*vRhs, mBlue*vRhs, mTransparent); -} - -// returns the addition of this color with another color -Color Color::operator+ (const Color& vRhs) const -{ - double trans=Transparent() > vRhs.Transparent() ? Transparent() : - vRhs.Transparent(); - return Color(Red()+vRhs.Red(),Green()+vRhs.Green(),Blue()+vRhs.Blue(), - trans); -} - -void Color::Red(double vRed) -{ - mRed = unit_limiter(vRed); -} -double Color::Red() const -{ - return mRed; -} -void Color::Green(double vGreen) -{ - mGreen = unit_limiter(vGreen); - -} -double Color::Green() const -{ - return mGreen; -} -void Color::Blue(double vBlue) -{ - mBlue = unit_limiter(vBlue); -} -double Color::Blue() const -{ - return mBlue; -} - -void Color::Transparent(double vTransparent) -{ - mTransparent = unit_limiter(vTransparent); -} - -double Color::Transparent() const -{ - return mTransparent; -} - -unsigned char Color::RedByte() const -{ - return ByteValue(mRed); -} -unsigned char Color::GreenByte() const -{ - return ByteValue(mGreen); -} -unsigned char Color::BlueByte() const -{ - return ByteValue(mBlue); -} -unsigned char Color::TransparentByte() const -{ - return ByteValue(mTransparent); -} - -unsigned char Color::ByteValue(double vZeroToOne) const -{ - return (unsigned char)(vZeroToOne*255.0); -} - -} // namespace igraph diff --git a/src/rigraph/vendor/simpleraytracer/Color.h b/src/rigraph/vendor/simpleraytracer/Color.h deleted file mode 100644 index 8a10430..0000000 --- a/src/rigraph/vendor/simpleraytracer/Color.h +++ /dev/null @@ -1,40 +0,0 @@ -/** Color.h - */ - -#ifndef COLOR_H -#define COLOR_H - -namespace igraph { - -class Color -{ -public: - Color(); - Color(double vRed, double vGreen, double vBlue, - double vTransparent=1.0); - ~Color(); - - Color operator* (double vRhs) const; // returns multiplication of a scalar with a vector - Color operator+ (const Color& vRhs) const; // returns the addition of this color with another color - - void Red(double vRed); - double Red() const; - void Green(double vGreen); - double Green() const; - void Blue(double vBlue); - double Blue() const; - void Transparent(double vTransparent); - double Transparent() const; - - unsigned char RedByte() const; - unsigned char GreenByte() const; - unsigned char BlueByte() const; - unsigned char TransparentByte() const; -private: - unsigned char ByteValue(double vZeroToOne) const; - double mRed, mGreen, mBlue, mTransparent; -}; - -} // namespace igraph - -#endif diff --git a/src/rigraph/vendor/simpleraytracer/Light.cpp b/src/rigraph/vendor/simpleraytracer/Light.cpp deleted file mode 100644 index d74cf0c..0000000 --- a/src/rigraph/vendor/simpleraytracer/Light.cpp +++ /dev/null @@ -1,46 +0,0 @@ -#include "Light.h" -#include "unit_limiter.h" - -namespace igraph { - -Light::Light() : mLightPoint(0,0,0) -{ - mIntensity = 0.1; -} - -Light::Light(const Point& rLightPoint) : mLightPoint(rLightPoint) -{ - mIntensity = 0.1; -} - -Light::~Light() -{} - -const Point& Light::LightPoint() const -{ - return mLightPoint; -} -void Light::LightPoint(const Point& rLightPoint) -{ - mLightPoint = rLightPoint; -} -double Light::Intensity() const -{ - return mIntensity; -} -void Light::Intensity(double vIntensity) -{ - mIntensity = unit_limiter(vIntensity); -} - -const Color& Light::LightColor() const -{ - return mLightColor; -} - -void Light::LightColor(const Color& rLightColor) -{ - mLightColor = rLightColor; -} - -} // namespace igraph diff --git a/src/rigraph/vendor/simpleraytracer/Light.h b/src/rigraph/vendor/simpleraytracer/Light.h deleted file mode 100644 index 369b926..0000000 --- a/src/rigraph/vendor/simpleraytracer/Light.h +++ /dev/null @@ -1,39 +0,0 @@ -#ifndef LIGHT_H -#define LIGHT_H - -#include "Point.h" -#include "Color.h" - -#include -using namespace std; - -namespace igraph { - -class Light -{ -public: - Light(); // creates a light at the origin - Light(const Point& rLightPoint); - ~Light(); - - const Point& LightPoint() const; - void LightPoint(const Point& rLightPoint); - - double Intensity() const; - void Intensity(double vIntensity); - - const Color& LightColor() const; - void LightColor(const Color& rLightColor); - -private: - Point mLightPoint; - double mIntensity; // 0 to 1 - Color mLightColor; -}; - -typedef list LightList; -typedef list::iterator LightListIterator; - -} // namespace igraph - -#endif diff --git a/src/rigraph/vendor/simpleraytracer/Point.cpp b/src/rigraph/vendor/simpleraytracer/Point.cpp deleted file mode 100644 index 8ccbc27..0000000 --- a/src/rigraph/vendor/simpleraytracer/Point.cpp +++ /dev/null @@ -1,106 +0,0 @@ -#include "Point.h" -#include - -namespace igraph { - -Point::Point() -{ - X(0.0); - Y(0.0); - Z(0.0); - Name(0); -} - -Point::Point(double vX, double vY, double vZ, int vName) -{ - X(vX); - Y(vY); - Z(vZ); - Name(vName); -} - -Point::Point(double vX, double vY, double vZ) -{ - X(vX); - Y(vY); - Z(vZ); - Name(0); -} - -Point::~Point() -{} - -double Point::X() const -{ - return mX; -} - -void Point::X(double vX) -{ - mX = vX; -} - -double Point::Y() const -{ - return mY; -} - -void Point::Y(double vY) -{ - mY = vY; -} - -double Point::Z() const -{ - return mZ; -} - -void Point::Z(double vZ) -{ - mZ = vZ; -} - -int Point::Name() const -{ - return mName; -} - -void Point::Name(int vName) -{ - mName = vName; -} - -double Point::Distance(const Point& rPoint) const -{ - return sqrt( (rPoint.X() - mX)*(rPoint.X() - mX) + (rPoint.Y() - mY)*(rPoint.Y() - mY) + (rPoint.Z() - mZ)*(rPoint.Z() - mZ) ); -} - -bool Point::operator==(const Point& vRhs) const -{ - bool result = true; -/* - if ( mX + .001 <= vRhs.X() ) - result = false; - if ( mX - .001 >= vRhs.X() ) - result = false; - if ( mY + .001 <= vRhs.Y() ) - result = false; - if ( mY - .001 >= vRhs.Y() ) - result = false; - if ( mZ + .001 <= vRhs.Z() ) - result = false; - if ( mZ - .001 >= vRhs.Z() ) - result = false; -*/ - if ( mX != vRhs.X() ) - result = false; - if ( mY != vRhs.Y() ) - result = false; - if ( mZ != vRhs.Z() ) - result = false; - - - return result; -} - -} // namespace igraph diff --git a/src/rigraph/vendor/simpleraytracer/Point.h b/src/rigraph/vendor/simpleraytracer/Point.h deleted file mode 100644 index 647bc47..0000000 --- a/src/rigraph/vendor/simpleraytracer/Point.h +++ /dev/null @@ -1,45 +0,0 @@ -/** - this is a simple generic class representing a 3d point with a name. - it also defines the PointList type, which is a linked list of Points -*/ - -#ifndef POINT_H -#define POINT_H - -#include -using namespace std; - -namespace igraph { - -class Point -{ -public: - Point(); // creates a point at the origin with name 0 - Point(double vX, double vY, double vZ, int vName); - Point(double vX, double vY, double vZ); - ~Point(); - - double X() const; - void X(double vX); - double Y() const; - void Y(double vY); - double Z() const; - void Z(double vZ); - - int Name() const; - void Name(int vName); - double Distance(const Point& rPoint) const; - - bool operator==(const Point& vRhs) const; - -private: - double mX, mY, mZ; - int mName; -}; - -typedef list PointList; -typedef list::iterator PointListIterator; - -} // namespace igraph - -#endif diff --git a/src/rigraph/vendor/simpleraytracer/RIgraphRay.cpp b/src/rigraph/vendor/simpleraytracer/RIgraphRay.cpp deleted file mode 100644 index 8bc9ddb..0000000 --- a/src/rigraph/vendor/simpleraytracer/RIgraphRay.cpp +++ /dev/null @@ -1,93 +0,0 @@ -/* -*- mode: C -*- */ -/* - IGraph library R interface. - Copyright (C) 2005-2012 Gabor Csardi - 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.h" -#include "igraph_error.h" - -#include "RayTracer.h" -#include "Sphere.h" - -#include "config.h" - -#include -#include -#include - -using namespace igraph; - -extern "C" { - -SEXP R_igraph_getsphere(SEXP pos, SEXP radius, SEXP color, SEXP bgcolor, - SEXP lightpos, SEXP lightcolor, SEXP width, - SEXP height) { - - /* All error checking is done at the R level */ - - int i; - double *spos=REAL(pos); - double *scolor=REAL(color); - int no_lights=GET_LENGTH(lightpos); - RayTracer* p_ray_tracer; - Sphere * sphere; - int swidth=INTEGER(width)[0]; - int sheight=INTEGER(height)[0]; - int nopixels=swidth * sheight; - SEXP result, dim; - Image image; - - p_ray_tracer = new RayTracer(); - p_ray_tracer->EyePoint(Point(0,0,0)); - - for (i=0; iIntensity(1); - light->LightColor(Color(lcol[0], lcol[1], lcol[2])); - p_ray_tracer->AddLight(light); - } - - sphere = new Sphere(Point(spos[0], spos[1], spos[2]), REAL(radius)[0]); - sphere->ShapeColor(Color(scolor[0], scolor[1], scolor[2])); - p_ray_tracer->AddShape(sphere); - - PROTECT(result=NEW_NUMERIC(nopixels * 4)); - PROTECT(dim=NEW_INTEGER(3)); - INTEGER(dim)[0]=swidth; INTEGER(dim)[1]=sheight; INTEGER(dim)[2]=4; - SET_DIM(result, dim); - - image.width=swidth; - image.height=sheight; - image.red=REAL(result); - image.green=image.red + nopixels; - image.blue=image.green + nopixels; - image.trans=image.blue + nopixels; - - p_ray_tracer->RayTrace(image); - delete p_ray_tracer; - - UNPROTECT(2); - return result; -} - -} // extern C diff --git a/src/rigraph/vendor/simpleraytracer/Ray.cpp b/src/rigraph/vendor/simpleraytracer/Ray.cpp deleted file mode 100644 index 7f6744a..0000000 --- a/src/rigraph/vendor/simpleraytracer/Ray.cpp +++ /dev/null @@ -1,44 +0,0 @@ -#include "Ray.h" - -namespace igraph { - -Ray::Ray() -{} - -Ray::~Ray() -{} - -Ray::Ray(const Point& rOrigin, const Vector& rDirection) -{ - Direction(rDirection); - Origin(rOrigin); - -} - -Ray::Ray(const Point& rOrigin, const Point& rEndPoint) -{ - Direction(Vector(rOrigin,rEndPoint)); - Origin(rOrigin); -} - -const Point& Ray::Origin() const -{ - return mOrigin; -} - -void Ray::Origin(Point vOrigin) -{ - mOrigin = vOrigin; -} - -const Vector& Ray::Direction() const -{ - return mDirection; -} - -void Ray::Direction(Vector vDirection) -{ - mDirection = vDirection; -} - -} // namespace igraph diff --git a/src/rigraph/vendor/simpleraytracer/Ray.h b/src/rigraph/vendor/simpleraytracer/Ray.h deleted file mode 100644 index cd14ca1..0000000 --- a/src/rigraph/vendor/simpleraytracer/Ray.h +++ /dev/null @@ -1,33 +0,0 @@ -/** Ray.h - */ - -#ifndef RAY_H -#define RAY_H - -#include "RayVector.h" -#include "Point.h" - -namespace igraph { - -class Ray -{ -public: - Ray(); - Ray(const Point& rOrigin, const Vector& rDirection); - Ray(const Point& rOrigin, const Point& rEndPoint); - ~Ray(); - - void Origin(Point vPoint); - const Point& Origin() const; - - const Vector& Direction() const; - void Direction(Vector vDirection); - -private: - Vector mDirection; - Point mOrigin; -}; - -} // namespace igraph - -#endif diff --git a/src/rigraph/vendor/simpleraytracer/RayTracer.cpp b/src/rigraph/vendor/simpleraytracer/RayTracer.cpp deleted file mode 100644 index 27e3cc5..0000000 --- a/src/rigraph/vendor/simpleraytracer/RayTracer.cpp +++ /dev/null @@ -1,266 +0,0 @@ -#include "RayTracer.h" -#include "unit_limiter.h" -#include -#include - -namespace igraph { - -RayTracer::RayTracer() : mBackgroundColor(0,0,0,0), mAmbientColor(0,0,0), mEyePoint(0,0,0), mSpecularColor(1,1,1) -{ - // begin settings - mAmbientIntensity = .7; - mRecursionLimit = 700; - mAntiAliasDetail = 1; - // end settings - - mRecursions = 0; - - mpShapes = new ShapeList; - mpLights = new LightList; -} - -RayTracer::~RayTracer() -{ - ShapeListIterator iter1 = mpShapes->begin(); - while ( iter1 != mpShapes->end() ) - { - delete *iter1; - iter1++; - } - delete mpShapes; - - LightListIterator iter2 = mpLights->begin(); - while ( iter2 != mpLights->end() ) - { - delete *iter2; - iter2++; - } - - delete mpLights; - -} - -void RayTracer::RayTrace(Image &result) -{ - int mWidth=result.width; - int mHeight=result.height; - Ray eye_ray(mEyePoint,Vector(0,0,1)); - Color draw_color; - double i_inc, j_inc, anti_alias_i_inc, anti_alias_j_inc; // amount to increment the ray in each direction - double i, j, anti_alias_i, anti_alias_j; // the i and j values of the ray - int pixel_x, pixel_y, anti_alias_pixel_x, anti_alias_pixel_y; // the pixels being drawn - - double average_red_byte, average_green_byte, average_blue_byte, average_trans_byte; - int anti_alias_count; // the number of anti aliases (used in averaging) - int idx=0; - - i_inc = 2.0/(double)mWidth; - j_inc = 2.0/(double)mHeight; - anti_alias_i_inc = 1.0/(double)mAntiAliasDetail; - anti_alias_j_inc = 1.0/(double)mAntiAliasDetail; - - pixel_y = 0; - j = 1.0; - for (; pixel_y < mHeight; j -= j_inc, pixel_y++) - { - pixel_x = 0; - i = -1.0; - for (; pixel_x < mWidth; i += i_inc, pixel_x++) - { - anti_alias_pixel_y = 0; - anti_alias_j = 0.0; - average_red_byte = 0; - average_green_byte = 0; - average_blue_byte = 0; - average_trans_byte = 0; - anti_alias_count = 0; - for (; anti_alias_pixel_y < mAntiAliasDetail; anti_alias_j += anti_alias_j_inc, anti_alias_pixel_y++) - { - anti_alias_pixel_x = 0; - anti_alias_i = 0.0; - - for (; anti_alias_pixel_x < mAntiAliasDetail; anti_alias_i += anti_alias_i_inc, anti_alias_pixel_x++) - { - anti_alias_count++; - eye_ray.Direction( Vector(i+(anti_alias_i*i_inc),j+(anti_alias_j*j_inc),1.0) ); - draw_color = Render(eye_ray); - - average_red_byte = average_red_byte + ((double)draw_color.RedByte() - average_red_byte)/(double)anti_alias_count; - average_green_byte = average_green_byte + ((double)draw_color.GreenByte() - average_green_byte)/(double)anti_alias_count; - average_blue_byte = average_blue_byte + ((double)draw_color.BlueByte() - average_blue_byte)/(double)anti_alias_count; - average_trans_byte = average_trans_byte + ((double)draw_color.TransparentByte() - average_trans_byte)/(double)anti_alias_count; - } - } - - result.red [idx] = average_red_byte/255; - result.green[idx] = average_green_byte/255; - result.blue [idx] = average_blue_byte/255; - result.trans[idx] = average_trans_byte/255; - idx++; - } - } -} - -Color RayTracer::Render(const Ray& rRay, bool vIsReflecting, const Shape* pReflectingFrom ) -{ - mRecursions++; - Shape* closest_shape; - Point intersect_point; - Color result; - if (vIsReflecting) - closest_shape = QueryScene(rRay, intersect_point, vIsReflecting, pReflectingFrom); - else - closest_shape = QueryScene(rRay, intersect_point); - - if (closest_shape == NULL && !vIsReflecting) - { - mRecursions = 0; - return mBackgroundColor; - } - if (closest_shape == NULL && vIsReflecting) - { - mRecursions = 0; - return mAmbientColor*mAmbientIntensity; - } - if ( mRecursions > mRecursionLimit ) - { - mRecursions = 0; - return Color(0,0,0); // mAmbientColor*mAmbientIntensity; - } - result = closest_shape->ShapeColor()*Shade(closest_shape, intersect_point); - - Ray backwards_ray(intersect_point,rRay.Direction()*-1); - if ( closest_shape->DiffuseReflectivity() > 0.0 ) - result = result + (Render( closest_shape->Reflect(intersect_point,backwards_ray), true, closest_shape )*closest_shape->DiffuseReflectivity()); - - return (result + mSpecularColor); -} - -double RayTracer::Shade(const Shape* pShapeToShade, const Point& rPointOnShapeToShade) -{ - double intensity = mAmbientIntensity * pShapeToShade->AmbientReflectivity(); // the ambient intensity of the scene - Ray light_ray; // the ray that goes from the intersection point to the light sources - double dot_product; - Shape* closest_shape; // the shape closest from the intersection point to the light source - Point light_intersect; // the intersection point of the ray that goes from the intersection point to the light source - light_ray.Origin(rPointOnShapeToShade); // lightRay. org= object. intersect; - Ray light_ray_from_light; - - LightListIterator iter = mpLights->begin(); - mSpecularColor.Red(0); - mSpecularColor.Green(0); - mSpecularColor.Blue(0); - - while ( iter != mpLights->end() ) // foreach light in LightList do - { - - light_ray.Direction(Vector(rPointOnShapeToShade,(*iter)->LightPoint())); // lightRay. dir= light. dir - light_ray_from_light.Origin((*iter)->LightPoint()); - light_ray_from_light.Direction(Vector((*iter)->LightPoint(),rPointOnShapeToShade)); - - closest_shape = QueryScene(light_ray_from_light, light_intersect); - if ( closest_shape == NULL || (closest_shape == pShapeToShade && light_ray.Direction().Dot(pShapeToShade->Normal(rPointOnShapeToShade, light_ray_from_light.Origin() )) >= 0.0 ) ) //if (QueryScene( lightRay)= NIL) - { - Vector normal_vector = pShapeToShade->Normal(rPointOnShapeToShade, Point() ); - dot_product = normal_vector.Dot(light_ray.Direction().Normalize()); - dot_product *= (*iter)->Intensity(); - - if (dot_product < 0.0) - { - if (pShapeToShade->Type() == "Triangle") - dot_product = dot_product*-1.0; - else - dot_product = 0.0; - } - intensity = unit_limiter( intensity + dot_product ); - - if ( light_ray.Direction().Dot(pShapeToShade->Normal(rPointOnShapeToShade, light_ray_from_light.Origin() )) >= 0.0 ) - { - double specular = Specular(pShapeToShade, rPointOnShapeToShade, *iter); - mSpecularColor = mSpecularColor + Color(specular,specular,specular); - } - } - - iter++; - } - - return intensity; -} - -double RayTracer::Specular(const Shape* pShapeToShade, const Point& rPointOnShapeToShade, const Light* pLight) -{ - Ray reflected = pShapeToShade->Reflect(rPointOnShapeToShade,Ray(rPointOnShapeToShade, pLight->LightPoint())); - - Vector eye_vector(rPointOnShapeToShade, mEyePoint); - Vector reflected_vector = reflected.Direction().Normalize(); - eye_vector.NormalizeThis(); - double dot_product = eye_vector.Dot(reflected_vector); - - int n = pShapeToShade->SpecularSize(); - double specular_intensity = dot_product/(n - n*dot_product+ dot_product); - return unit_limiter(specular_intensity*pLight->Intensity()); -} - -Shape* RayTracer::QueryScene(const Ray& rRay, Point& rIntersectionPoint, bool vIsReflecting, const Shape* pReflectingFrom) -{ - Shape* closest_shape = NULL; - Point intersect_point; - double closest_distance; - double intersect_distance; - bool found_intersection = false; - - ShapeListIterator iter = mpShapes->begin(); - while ( iter != mpShapes->end() ) - { - if ( (*iter)->Intersect( rRay, intersect_point ) ) - { - intersect_distance = intersect_point.Distance(rRay.Origin()); - if ( !found_intersection && (*iter) != pReflectingFrom) - { - found_intersection = true; - rIntersectionPoint = intersect_point; - closest_shape = *iter; - closest_distance = intersect_distance; - } - else if ( intersect_distance < closest_distance && (*iter) != pReflectingFrom ) - { - rIntersectionPoint = intersect_point; - closest_shape = *iter; - closest_distance = intersect_distance; - } - } - iter++; - } - - return closest_shape; -} - -void RayTracer::AddShape(Shape* pShape) -{ - // should check if a shape with the same name already exists - mpShapes->push_back(pShape); -} -void RayTracer::AddLight(Light* pLight) -{ - // should check if a shape with the same name already exists - mpLights->push_back(pLight); -} - -void RayTracer::BackgroundColor(const Color& rBackgroundColor) -{ - mBackgroundColor = rBackgroundColor; -} -void RayTracer::EyePoint(const Point& rEyePoint) -{ - mEyePoint = rEyePoint; -} -void RayTracer::AmbientColor(const Color& rAmbientColor) -{ - mAmbientColor = rAmbientColor; -} -void RayTracer::AmbientIntensity(double vAmbientIntensity) -{ - mAmbientIntensity = unit_limiter(vAmbientIntensity); -} - -} // namespace igraph diff --git a/src/rigraph/vendor/simpleraytracer/RayTracer.h b/src/rigraph/vendor/simpleraytracer/RayTracer.h deleted file mode 100644 index cc4830d..0000000 --- a/src/rigraph/vendor/simpleraytracer/RayTracer.h +++ /dev/null @@ -1,63 +0,0 @@ -/** RayTraceCanvas.h - */ - -#ifndef RAY_TRACER_H -#define RAY_TRACER_H - - -#include -#include "Point.h" -#include "Shape.h" -#include "Color.h" -#include "Light.h" - -namespace igraph { - -class Image -{ - public: - int width, height; - double *red, *green, *blue, *trans; -}; - -class RayTracer -{ - -public: - RayTracer(); - ~RayTracer(); - - void RayTrace(Image &result); - - void AddShape(Shape* pShape); - void AddLight(Light* pLight); - - void BackgroundColor(const Color& rBackgroundColor); - void EyePoint(const Point& rEyePoint); - void AmbientColor(const Color& rAmbient); - void AmbientIntensity(double vAmbientIntensity); - -private: - - Color Render(const Ray& rRay, bool vIsReflecting = false, const Shape* pReflectingFrom = 0 ); // vEyeRay should be true if the ray we are tracing is a ray from the eye, otherwise it should be false - Shape* QueryScene(const Ray& rRay, Point& rIntersectionPoint, bool vIsReflecting = false, const Shape* pReflectingFrom = 0); - double Shade(const Shape* pShapeToShade, const Point& rPointOnShapeToShade); - double Specular(const Shape* pShapeToShade, const Point& rPointOnShapeToShade, const Light* pLight); - - Color mBackgroundColor; - Color mAmbientColor; - Point mEyePoint; - Color mSpecularColor; - double mAmbientIntensity; - - ShapeList* mpShapes; - LightList* mpLights; - - int mRecursions; - int mRecursionLimit; - int mAntiAliasDetail; -}; - -} // namespace igraph - -#endif diff --git a/src/rigraph/vendor/simpleraytracer/RayVector.cpp b/src/rigraph/vendor/simpleraytracer/RayVector.cpp deleted file mode 100644 index 76f21aa..0000000 --- a/src/rigraph/vendor/simpleraytracer/RayVector.cpp +++ /dev/null @@ -1,128 +0,0 @@ -#include "RayVector.h" -#include - -namespace igraph { - -Vector::Vector() -{ - mI = mJ = mK = 0.0; -} - -Vector::Vector(const Point& vStartPoint, const Point& vEndPoint) -{ - mI = vEndPoint.X() - vStartPoint.X(); - mJ = vEndPoint.Y() - vStartPoint.Y(); - mK = vEndPoint.Z() - vStartPoint.Z(); -} - -Vector::Vector(double vI, double vJ, double vK) -{ - mI = vI; - mJ = vJ; - mK = vK; -} - -Vector::~Vector() -{} - -// returns a unit vector of this vector -Vector Vector::Normalize() const -{ - double magnitude = Magnitude(); - return Vector(mI/magnitude, mJ/magnitude, mK/magnitude); -} - -void Vector::NormalizeThis() -{ - *this = Normalize(); -} - -void Vector::ReverseDirection() -{ - *this = *this * -1.0; -} - -bool Vector::IsSameDirection(const Vector& rVector) const -{ - return ( this->Normalize().Dot(rVector.Normalize()) > 0.0 ); -} - - -void Vector::I(double vI) -{ - mI = vI; -} - -double Vector::I() const -{ - return mI; -} - -void Vector::J(double vJ) -{ - mJ = vJ; -} - -double Vector::J() const -{ - return mJ; -} -void Vector::K(double vK) -{ - mK = vK; -} - -double Vector::K() const -{ - return mK; -} - -// returns the dot product of this and rVector -double Vector::Dot(const Vector& rVector) const -{ - return mI*rVector.I() + mJ*rVector.J() + mK*rVector.K(); -} - -// returns the cross product of this and vVector -Vector Vector::Cross(const Vector& rVector) const -{ - return Vector(mJ*rVector.K() - rVector.J()*mK, -1.0*(mI*rVector.K() - rVector.I()*mK), mI*rVector.J() - rVector.I()*mJ); -} - -// returns the sum of this vector with another vector -Vector Vector::operator+ (Vector vRhs) const -{ - return Vector(mI + vRhs.I(), mJ + vRhs.J(), mK + vRhs.K()); -} - -// returns the sume of a vector and a Point -Point Vector::operator+ (Point vRhs) const -{ - return Point(mI + vRhs.X(), mJ + vRhs.Y(), mK + vRhs.Z()); -} - -// returns the difference of two vectors -Vector Vector::operator- (Vector vRhs) const -{ - return Vector(mI-vRhs.I(), mJ-vRhs.J(), mK-vRhs.K()); -} - -// returns multiplication of a scalar with this vector -Vector Vector::operator* (double vRhs) const -{ - return Vector(mI*vRhs, mJ*vRhs, mK*vRhs); -} - -// converts this vector to a point -Point Vector::ToPoint() const -{ - return Point(mI,mJ,mK); -} - -// returns the magnitude -double Vector::Magnitude() const -{ - return sqrt(mI*mI + mJ*mJ + mK*mK); -} - -} // namespace igraph diff --git a/src/rigraph/vendor/simpleraytracer/RayVector.h b/src/rigraph/vendor/simpleraytracer/RayVector.h deleted file mode 100644 index 7157ba9..0000000 --- a/src/rigraph/vendor/simpleraytracer/RayVector.h +++ /dev/null @@ -1,49 +0,0 @@ -/** Vector.h - */ - -#ifndef VECTOR_H -#define VECTOR_H - -#include "Point.h" - -namespace igraph { - -class Vector -{ -public: - Vector(); - Vector(const Point& vStartPoint, const Point& vEndPoint); - Vector(double vI, double vJ, double vK); - ~Vector(); - - Vector Normalize() const; // returns a unit vector of this vector - void NormalizeThis(); - void ReverseDirection(); - bool IsSameDirection(const Vector& rVector) const; - - void I(double vI); - double I() const; - void J(double vJ); - double J() const; - void K(double vK); - double K() const; - - double Dot(const Vector& rVector) const; // returns the dot product of this and rVector - Vector Cross(const Vector& rVector) const; // returns the cross product of this and rVector - Vector operator+ (Vector vRhs) const; // returns the sum of two vectors - Vector operator- (Vector vRhs) const; // returns the difference of two vectors - Point operator+ (Point vRhs) const; // returns the sum of a vector and a Point - Vector operator* (double vRhs) const; // returns multiplication of a scalar with a vector - Point ToPoint() const; // converts a vector to a point - - double Magnitude() const; - -private: - - - double mI, mJ, mK; -}; - -} // namespace igraph - -#endif diff --git a/src/rigraph/vendor/simpleraytracer/Shape.cpp b/src/rigraph/vendor/simpleraytracer/Shape.cpp deleted file mode 100644 index 3ca2b14..0000000 --- a/src/rigraph/vendor/simpleraytracer/Shape.cpp +++ /dev/null @@ -1,106 +0,0 @@ -#include "Shape.h" -#include "unit_limiter.h" - -namespace igraph { - -Shape::Shape() -{ - mName = 0; - mAmbientReflectivity = .6; - mSpecularReflectivity = 0; - mDiffuseReflectivity = 0; - mSpecularSize = 64; -} - -Shape::~Shape() -{} - -int Shape::Name() const -{ - return mName; -} - -void Shape::Name(int vName) -{ - mName = vName; -} - -const Color& Shape::ShapeColor() const -{ - return mShapeColor; -} - -void Shape::ShapeColor(const Color& rColor) -{ - mShapeColor = rColor; -} - -double Shape::AmbientReflectivity() const -{ - return mAmbientReflectivity; -} -double Shape::SpecularReflectivity() const -{ - return mSpecularReflectivity; -} -double Shape::DiffuseReflectivity() const -{ - return mDiffuseReflectivity; -} - -void Shape::AmbientReflectivity(double rReflectivity) -{ - mAmbientReflectivity = unit_limiter(rReflectivity); -} -void Shape::SpecularReflectivity(double rReflectivity) -{ - mSpecularReflectivity = unit_limiter(rReflectivity); -} -void Shape::DiffuseReflectivity(double rReflectivity) -{ - mDiffuseReflectivity = unit_limiter(rReflectivity); -} - -Ray Shape::Reflect(const Point& rReflectFrom, const Ray& rIncidentRay) const -{ - Ray result; // the reflected ray - Vector result_direction; // the reflected direction vector - Vector incident_unit = rIncidentRay.Direction().Normalize(); - Vector normal = this->Normal(rReflectFrom, rIncidentRay.Origin() ); - if ( !normal.IsSameDirection(incident_unit) ) - normal.ReverseDirection(); // we want the normal in the same direction of the incident ray. - - result.Origin(rReflectFrom); - result.Direction( normal*2.0*normal.Dot(incident_unit) - incident_unit ); -/* - if ( normal.Dot(rIncidentRay.Direction().Normalize()) < 0.0 ) - normal.ReverseDirection(); - - result.Origin(rReflectFrom); - result.Direction((normal*2.0) - rIncidentRay.Direction().Normalize()); -*/ - - return result; -} - -const string& Shape::Type() const -{ - return mType; -} - -void Shape::Type(const string& rType) -{ - mType = rType; -} - -int Shape::SpecularSize() const -{ - return mSpecularSize; -} - -void Shape::SpecularSize(int vSpecularSize) -{ - mSpecularSize = vSpecularSize; -} - -} // namespace igraph diff --git a/src/rigraph/vendor/simpleraytracer/Shape.h b/src/rigraph/vendor/simpleraytracer/Shape.h deleted file mode 100644 index fc9c7c1..0000000 --- a/src/rigraph/vendor/simpleraytracer/Shape.h +++ /dev/null @@ -1,65 +0,0 @@ -/** Shape.h - */ - -#ifndef SHAPE_H -#define SHAPE_H - -#include -#include "Color.h" -#include "Ray.h" -#include "Point.h" - -#include -using namespace std; - -namespace igraph { - -class Shape -{ -public: - Shape(); - virtual ~Shape(); - - virtual bool Intersect(const Ray& rRay, Point& rIntersectPoint) const = 0; - virtual Vector Normal(const Point& rSurfacePoint, const Point& rOffSurface) const = 0; - // returns a normalized vector that is the normal of this shape from the surface point - // it also takes the rOffSurface point into account, for example: - // if rSurfacePoint is on top of a triangle, then the normal returned will be going up. - - Ray Reflect(const Point& rReflectFrom, const Ray& rRay) const; - - void Name(int vName); - int Name() const; - - const Color& ShapeColor() const; - void ShapeColor(const Color& rColor); - - double SpecularReflectivity() const; - void SpecularReflectivity(double rReflectivity); - double DiffuseReflectivity() const; - void DiffuseReflectivity(double rReflectivity); - double AmbientReflectivity() const; - void AmbientReflectivity(double rReflectivity); - - int SpecularSize() const; - void SpecularSize(int vSpecularSize); - - const string& Type() const; - void Type(const string& rType); - -private: - int mName; - string mType; - Color mShapeColor; - double mSpecularReflectivity; // from 0 to 1 - int mSpecularSize; // 1 to 64 - double mDiffuseReflectivity; // from 0 to 1 - double mAmbientReflectivity; // from 0 to 1 -}; - -typedef list ShapeList; -typedef list::iterator ShapeListIterator; - -} // namespace igraph - -#endif diff --git a/src/rigraph/vendor/simpleraytracer/Sphere.cpp b/src/rigraph/vendor/simpleraytracer/Sphere.cpp deleted file mode 100644 index c449b80..0000000 --- a/src/rigraph/vendor/simpleraytracer/Sphere.cpp +++ /dev/null @@ -1,70 +0,0 @@ -#include "Sphere.h" -#include - -namespace igraph { - -Sphere::Sphere() -{} - -Sphere::Sphere(Point vCenter, double vRadius) -{ - Type("Sphere"); - mCenter = vCenter; - mRadius = vRadius; -} - -Sphere::~Sphere() -{ -} - -bool Sphere::Intersect(const Ray& vRay, Point& vIntersectPoint) const -{ - Vector V; - Vector EO(vRay.Origin(), mCenter); - double v; - double disc; - double d; - Vector E(Point(0,0,0), vRay.Origin()); // E = vector from origin to ray origin - Vector P; - mCenter.Distance(vRay.Origin()); //c = distance from eye to center of sphere - V = vRay.Direction(); - V.NormalizeThis(); - v = EO.Dot(V); - double v2 = V.Dot(EO.Normalize()); - if (v2 >= 0.0) - { - disc = mRadius*mRadius - (EO.Dot(EO) - v*v); - if (disc <= 0) - return false; - else - { - d = sqrt(disc); - P = E + V*(v-d); - vIntersectPoint = P.ToPoint(); - return true; - } - } - else - return false; -} - -Vector Sphere::Normal(const Point& rSurfacePoint, const Point& rOffSurface) const -{ - // currently does not take rOffSurface point into account, - // it should check if this point is inside the sphere, if it is - // return a normal facing the center. - - Vector radius_vector (mCenter, rSurfacePoint); - return (radius_vector.Normalize()); -} - -double Sphere::Radius() const -{ - return mRadius; -} -const Point& Sphere::Center() const -{ - return mCenter; -} - -} // namespace igraph diff --git a/src/rigraph/vendor/simpleraytracer/Sphere.h b/src/rigraph/vendor/simpleraytracer/Sphere.h deleted file mode 100644 index faff6da..0000000 --- a/src/rigraph/vendor/simpleraytracer/Sphere.h +++ /dev/null @@ -1,31 +0,0 @@ -/** Sphere.h -*/ - -#ifndef SPHERE_H -#define SPHERE_H - -#include "Shape.h" - -namespace igraph { - -class Sphere : public Shape -{ -public: - Sphere(); - Sphere(Point vCenter, double vRadius); - ~Sphere(); - - virtual bool Intersect(const Ray& vRay, Point& vIntersectPoint) const; - virtual Vector Normal(const Point& rSurfacePoint, const Point& rOffSurface) const; - - double Radius() const; - const Point& Center() const; - -private: - Point mCenter; - double mRadius; -}; - -} // namespace igraph - -#endif diff --git a/src/rigraph/vendor/simpleraytracer/Triangle.cpp b/src/rigraph/vendor/simpleraytracer/Triangle.cpp deleted file mode 100644 index 7946c26..0000000 --- a/src/rigraph/vendor/simpleraytracer/Triangle.cpp +++ /dev/null @@ -1,90 +0,0 @@ -#include "Triangle.h" -#include - -namespace igraph { - -Triangle::Triangle() -{} - -Triangle::Triangle(const Point& rPoint1, const Point& rPoint2, const Point& rPoint3) -{ - Type("Triangle"); - mPoint1 = rPoint1; - mPoint2 = rPoint2; - mPoint3 = rPoint3; -} - -Triangle::~Triangle() -{ -} - -bool Triangle::Intersect(const Ray& vRay, Point& rIntersectPoint) const -{ - - Vector pointb_minus_pointa (mPoint1, mPoint2); - Vector pointb_minus_pointc (mPoint1, mPoint3); -/* - Vector plane_normal = pointb_minus_pointa.Cross(pointb_minus_pointc); - - // get the plane normal facing the right way: - Vector plane_normal_normalized = plane_normal.Normalize(); - Vector triangle_to_ray_origin = Vector(mPoint1, vRay.Origin() ); - triangle_to_ray_origin.NormalizeThis(); - if ( plane_normal_normalized.Dot(triangle_to_ray_origin) < 0.0 ) - { - plane_normal = plane_normal * -1.0; - plane_normal_normalized = plane_normal_normalized * -1.0; - } - - // check that the ray is actually facing the triangle - Vector ray_direction_normalized = vRay.Direction().Normalize(); - if ( plane_normal_normalized.Dot(ray_direction_normalized) > 0.0 ) - return false; -*/ - Vector plane_normal = this->Normal(mPoint1, vRay.Origin()); - Vector ray_direction_normalized = vRay.Direction().Normalize(); - if ( plane_normal.IsSameDirection(ray_direction_normalized) ) - return false; - - Vector b_minus_u (vRay.Origin(), mPoint2); - - double t = plane_normal.Dot(b_minus_u) / plane_normal.Dot(vRay.Direction()); - Point p = (vRay.Direction() * t) + vRay.Origin(); - - Vector p_minus_a (mPoint1, p); - Vector p_minus_b (mPoint2, p); - Vector p_minus_c (mPoint3, p); - Vector pointc_minus_pointb (mPoint2, mPoint3); - Vector pointa_minus_pointc (mPoint3, mPoint1); - - double test1 = (pointb_minus_pointa.Cross(p_minus_a)).Dot(plane_normal); - double test2 = (pointc_minus_pointb.Cross(p_minus_b)).Dot(plane_normal); - double test3 = (pointa_minus_pointc.Cross(p_minus_c)).Dot(plane_normal); - - if ((test1 > 0 && test2 > 0 && test3 > 0) || (test1 < 0 && test2 < 0 && test3 < 0)) - { - rIntersectPoint = p; - return true; - } - else - return false; -} - -Vector Triangle::Normal(const Point& rSurfacePoint, const Point& rOffSurface) const -{ - Vector pointb_minus_pointa (mPoint1, mPoint2); - Vector pointb_minus_pointc (mPoint1, mPoint3); - Vector plane_normal = pointb_minus_pointa.Cross(pointb_minus_pointc).Normalize(); - - // get the plane normal facing the right way: - Vector triangle_to_off_surface_point = Vector(mPoint1, rOffSurface ); - triangle_to_off_surface_point.NormalizeThis(); - if ( !plane_normal.IsSameDirection(triangle_to_off_surface_point) ) - { - plane_normal.ReverseDirection(); - } - - return plane_normal; -} - -} // namespace igraph diff --git a/src/rigraph/vendor/simpleraytracer/Triangle.h b/src/rigraph/vendor/simpleraytracer/Triangle.h deleted file mode 100644 index 198d241..0000000 --- a/src/rigraph/vendor/simpleraytracer/Triangle.h +++ /dev/null @@ -1,27 +0,0 @@ -/** Triangle.h -*/ - -#ifndef TRIANGLE_H -#define TRIANGLE_H - -#include "Shape.h" - -namespace igraph { - -class Triangle : public Shape -{ -public: - Triangle(); - Triangle(const Point& rPoint1, const Point& rPoint2, const Point& rPoint3); - ~Triangle(); - - virtual bool Intersect(const Ray& vRay, Point& vIntersectPoint) const; - virtual Vector Normal(const Point& rSurfacePoint, const Point& rOffSurface) const; - -private: - Point mPoint1, mPoint2, mPoint3; -}; - -} // namespace igraph - -#endif diff --git a/src/rigraph/vendor/simpleraytracer/unit_limiter.cpp b/src/rigraph/vendor/simpleraytracer/unit_limiter.cpp deleted file mode 100644 index 75a59da..0000000 --- a/src/rigraph/vendor/simpleraytracer/unit_limiter.cpp +++ /dev/null @@ -1,15 +0,0 @@ -#include "unit_limiter.h" - -namespace igraph { - -double unit_limiter(double vUnitDouble) -{ - double result = vUnitDouble; - if (result < 0.0) - result = 0.0; - else if (result > 1.0) - result = 1.0; - return result; -} - -} // namespace igraph diff --git a/src/rigraph/vendor/simpleraytracer/unit_limiter.h b/src/rigraph/vendor/simpleraytracer/unit_limiter.h deleted file mode 100644 index 9c6f61b..0000000 --- a/src/rigraph/vendor/simpleraytracer/unit_limiter.h +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef ZERO_TO_ONE_H -#define ZERO_TO_ONE_H - -namespace igraph { - -double unit_limiter(double vUnitDouble); - -} // namespace igraph - -#endif diff --git a/src/rigraph/vendor/uuid/COPYING b/src/rigraph/vendor/uuid/COPYING deleted file mode 100644 index e7a8054..0000000 --- a/src/rigraph/vendor/uuid/COPYING +++ /dev/null @@ -1,28 +0,0 @@ -This library is free software; you can redistribute it and/or -modify it under the terms of the Modified BSD License: - -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, and the entire permission notice in its entirety, - including the disclaimer of warranties. -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. The name of the author may not be used to endorse or promote - products derived from this software without specific prior - written permission. - -THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED -WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF -WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR 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 NOT ADVISED OF THE POSSIBILITY OF SUCH -DAMAGE. diff --git a/src/rigraph/vendor/uuid/Makevars.in b/src/rigraph/vendor/uuid/Makevars.in deleted file mode 100644 index 0ab302b..0000000 --- a/src/rigraph/vendor/uuid/Makevars.in +++ /dev/null @@ -1,2 +0,0 @@ -PKG_CPPFLAGS=@CPPFLAGS@ -PKG_LIBS=@LIBS@ diff --git a/src/rigraph/vendor/uuid/Makevars.win b/src/rigraph/vendor/uuid/Makevars.win deleted file mode 100644 index 248e1ce..0000000 --- a/src/rigraph/vendor/uuid/Makevars.win +++ /dev/null @@ -1 +0,0 @@ -PKG_CPPFLAGS=-Iwin32 diff --git a/src/rigraph/vendor/uuid/R.c b/src/rigraph/vendor/uuid/R.c deleted file mode 100644 index b956b8d..0000000 --- a/src/rigraph/vendor/uuid/R.c +++ /dev/null @@ -1,25 +0,0 @@ -#include "uuid.h" - -#include -#include "igraph_random.h" - -SEXP UUID_gen(SEXP sTime) { - - RNG_BEGIN(); - - uuid_t u; - char c[40]; - int use_time = asInteger(sTime); - if (use_time == TRUE) - uuid_generate_time(u); - else if (use_time == FALSE) - uuid_generate_random(u); - else - uuid_generate(u); - uuid_unparse_lower(u, c); - - RNG_END(); - - return mkString(c); -} - diff --git a/src/rigraph/vendor/uuid/clear.c b/src/rigraph/vendor/uuid/clear.c deleted file mode 100644 index ad1f066..0000000 --- a/src/rigraph/vendor/uuid/clear.c +++ /dev/null @@ -1,43 +0,0 @@ -/* - * clear.c -- Clear a UUID - * - * Copyright (C) 1996, 1997 Theodore Ts'o. - * - * %Begin-Header% - * 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, and the entire permission notice in its entirety, - * including the disclaimer of warranties. - * 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. The name of the author may not be used to endorse or promote - * products derived from this software without specific prior - * written permission. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF - * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR 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 NOT ADVISED OF THE POSSIBILITY OF SUCH - * DAMAGE. - * %End-Header% - */ - -#include - -#include "uuidP.h" - -void uuid_clear(uuid_t uu) -{ - memset(uu, 0, 16); -} - diff --git a/src/rigraph/vendor/uuid/compare.c b/src/rigraph/vendor/uuid/compare.c deleted file mode 100644 index 8f3437a..0000000 --- a/src/rigraph/vendor/uuid/compare.c +++ /dev/null @@ -1,55 +0,0 @@ -/* - * compare.c --- compare whether or not two UUIDs are the same - * - * Returns 0 if the two UUIDs are different, and 1 if they are the same. - * - * Copyright (C) 1996, 1997 Theodore Ts'o. - * - * %Begin-Header% - * 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, and the entire permission notice in its entirety, - * including the disclaimer of warranties. - * 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. The name of the author may not be used to endorse or promote - * products derived from this software without specific prior - * written permission. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF - * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR 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 NOT ADVISED OF THE POSSIBILITY OF SUCH - * DAMAGE. - * %End-Header% - */ - -#include "uuidP.h" -#include - -#define UUCMP(u1,u2) if (u1 != u2) return((u1 < u2) ? -1 : 1); - -int uuid_compare(const uuid_t uu1, const uuid_t uu2) -{ - struct uuid uuid1, uuid2; - - uuid_unpack(uu1, &uuid1); - uuid_unpack(uu2, &uuid2); - - UUCMP(uuid1.time_low, uuid2.time_low); - UUCMP(uuid1.time_mid, uuid2.time_mid); - UUCMP(uuid1.time_hi_and_version, uuid2.time_hi_and_version); - UUCMP(uuid1.clock_seq, uuid2.clock_seq); - return memcmp(uuid1.node, uuid2.node, 6); -} - diff --git a/src/rigraph/vendor/uuid/config.h.in b/src/rigraph/vendor/uuid/config.h.in deleted file mode 100644 index b6f72b3..0000000 --- a/src/rigraph/vendor/uuid/config.h.in +++ /dev/null @@ -1,82 +0,0 @@ -/* src/config.h.in. Generated from configure.ac by autoheader. */ - -/* Define to 1 if you have the header file. */ -#undef HAVE_INTTYPES_H - -/* Define to 1 if you have the `jrand48' function. */ -#undef HAVE_JRAND48 - -/* Define to 1 if you have the header file. */ -#undef HAVE_MEMORY_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_NETINET_IN_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_NET_IF_DL_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_NET_IF_H - -/* Define if struct sockaddr contains sa_len */ -#undef HAVE_SA_LEN - -/* Define to 1 if you have the header file. */ -#undef HAVE_STDINT_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_STDLIB_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_STRINGS_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_STRING_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_SYS_FILE_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_SYS_IOCTL_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_SYS_SOCKET_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_SYS_SOCKIO_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_SYS_STAT_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_SYS_SYSCALL_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_SYS_TIME_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_SYS_TYPES_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_SYS_UN_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_UNISTD_H - -/* Define to the address where bug reports for this package should be sent. */ -#undef PACKAGE_BUGREPORT - -/* Define to the full name of this package. */ -#undef PACKAGE_NAME - -/* Define to the full name and version of this package. */ -#undef PACKAGE_STRING - -/* Define to the one symbol short name of this package. */ -#undef PACKAGE_TARNAME - -/* Define to the version of this package. */ -#undef PACKAGE_VERSION - -/* Define to 1 if you have the ANSI C header files. */ -#undef STDC_HEADERS diff --git a/src/rigraph/vendor/uuid/copy.c b/src/rigraph/vendor/uuid/copy.c deleted file mode 100644 index ead33aa..0000000 --- a/src/rigraph/vendor/uuid/copy.c +++ /dev/null @@ -1,45 +0,0 @@ -/* - * copy.c --- copy UUIDs - * - * Copyright (C) 1996, 1997 Theodore Ts'o. - * - * %Begin-Header% - * 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, and the entire permission notice in its entirety, - * including the disclaimer of warranties. - * 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. The name of the author may not be used to endorse or promote - * products derived from this software without specific prior - * written permission. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF - * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR 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 NOT ADVISED OF THE POSSIBILITY OF SUCH - * DAMAGE. - * %End-Header% - */ - -#include "uuidP.h" - -void uuid_copy(uuid_t dst, const uuid_t src) -{ - unsigned char *cp1; - const unsigned char *cp2; - int i; - - for (i=0, cp1 = dst, cp2 = src; i < 16; i++) - *cp1++ = *cp2++; -} diff --git a/src/rigraph/vendor/uuid/gen_uuid.c b/src/rigraph/vendor/uuid/gen_uuid.c deleted file mode 100644 index 4bc6143..0000000 --- a/src/rigraph/vendor/uuid/gen_uuid.c +++ /dev/null @@ -1,536 +0,0 @@ -/* - * gen_uuid.c --- generate a DCE-compatible uuid - * - * Copyright (C) 1996, 1997, 1998, 1999 Theodore Ts'o. - * - * %Begin-Header% - * 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, and the entire permission notice in its entirety, - * including the disclaimer of warranties. - * 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. The name of the author may not be used to endorse or promote - * products derived from this software without specific prior - * written permission. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF - * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR 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 NOT ADVISED OF THE POSSIBILITY OF SUCH - * DAMAGE. - * %End-Header% - */ - -/* - * Force inclusion of SVID stuff since we need it if we're compiling in - * gcc-wall wall mode - */ -#define _DEFAULT_SOURCE - -#include "config.h" - -#ifdef _WIN32 -#define _WIN32_WINNT 0x0500 -#include -#define UUID MYUUID -#endif -#include -#ifdef HAVE_UNISTD_H -#include -#endif -#ifdef HAVE_STDLIB_H -#include -#endif -#include -#include -#include -#include -#include -#ifdef HAVE_SYS_TIME_H -#include -#endif -#include -#ifdef HAVE_SYS_FILE_H -#include -#endif -#ifdef HAVE_SYS_IOCTL_H -#include -#endif -#ifdef HAVE_SYS_SOCKET_H -#include -#endif -#ifdef HAVE_SYS_UN_H -#include -#endif -#ifdef HAVE_SYS_SOCKIO_H -#include -#endif -#ifdef HAVE_NET_IF_H -#include -#endif -#ifdef HAVE_NETINET_IN_H -#include -#endif -#ifdef HAVE_NET_IF_DL_H -#include -#endif -#if defined(__linux__) && defined(HAVE_SYS_SYSCALL_H) -#include -#endif - -#include "uuidP.h" -#include "uuidd.h" - -#ifdef USING_R -#include "igraph_random.h" -#define srand(x) ; -#define rand() RNG_INTEGER(0, RAND_MAX) -#endif - -#ifdef HAVE_TLS -#define THREAD_LOCAL static __thread -#else -#define THREAD_LOCAL static -#endif - -#ifdef _WIN32 -#if 0 /* MinGW has gettimeofday so we don't need this */ -static int gettimeofday (struct timeval *tv, void *dummy) -{ - FILETIME ftime; - uint64_t n; - - GetSystemTimeAsFileTime (&ftime); - n = (((uint64_t) ftime.dwHighDateTime << 32) - + (uint64_t) ftime.dwLowDateTime); - if (n) { - n /= 10; - n -= ((369 * 365 + 89) * (uint64_t) 86400) * 1000000; - } - - tv->tv_sec = n / 1000000; - tv->tv_usec = n % 1000000; -} -#endif - -#ifdef __MINGW32__ -int gettimeofday (struct timeval *tv, void *dummy); -#endif -#ifdef __MINGW64__ -int gettimeofday (struct timeval *tv, void *dummy); -#endif - -static int getuid (void) -{ - return 1; -} -#endif - -/* - * Get the ethernet hardware address, if we can find it... - * - * XXX for a windows version, probably should use GetAdaptersInfo: - * http://www.codeguru.com/cpp/i-n/network/networkinformation/article.php/c5451 - * commenting out get_node_id just to get gen_uuid to compile under windows - * is not the right way to go! - */ -static int get_node_id(unsigned char *node_id) -{ -#ifdef HAVE_NET_IF_H - int sd; - struct ifreq ifr, *ifrp; - struct ifconf ifc; - char buf[1024]; - int n, i; - unsigned char *a; -#ifdef HAVE_NET_IF_DL_H - struct sockaddr_dl *sdlp; -#endif - -/* - * BSD 4.4 defines the size of an ifreq to be - * max(sizeof(ifreq), sizeof(ifreq.ifr_name)+ifreq.ifr_addr.sa_len - * However, under earlier systems, sa_len isn't present, so the size is - * just sizeof(struct ifreq) - */ -#ifdef HAVE_SA_LEN -#define max(x, y) (((x) > (y)) ? (x) : (y)) -#define ifreq_size(i) max(sizeof(struct ifreq),\ - sizeof((i).ifr_name)+(i).ifr_addr.sa_len) -#else -#define ifreq_size(i) sizeof(struct ifreq) -#endif /* HAVE_SA_LEN */ - - sd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); - if (sd < 0) { - return -1; - } - memset(buf, 0, sizeof(buf)); - ifc.ifc_len = sizeof(buf); - ifc.ifc_buf = buf; - if (ioctl (sd, SIOCGIFCONF, (char *)&ifc) < 0) { - close(sd); - return -1; - } - n = ifc.ifc_len; - for (i = 0; i < n; i+= ifreq_size(*ifrp) ) { - ifrp = (struct ifreq *)((char *) ifc.ifc_buf+i); - strncpy(ifr.ifr_name, ifrp->ifr_name, IFNAMSIZ); -#if defined(SIOCGIFHWADDR) && (!defined(__sun__)) - if (ioctl(sd, SIOCGIFHWADDR, &ifr) < 0) - continue; - a = (unsigned char *) &ifr.ifr_hwaddr.sa_data; -#else -#ifdef SIOCGENADDR - if (ioctl(sd, SIOCGENADDR, &ifr) < 0) - continue; - a = (unsigned char *) ifr.ifr_enaddr; -#else -#ifdef HAVE_NET_IF_DL_H - sdlp = (struct sockaddr_dl *) &ifrp->ifr_addr; - if ((sdlp->sdl_family != AF_LINK) || (sdlp->sdl_alen != 6)) - continue; - a = (unsigned char *) &sdlp->sdl_data[sdlp->sdl_nlen]; -#else - /* - * XXX we don't have a way of getting the hardware - * address - */ - close(sd); - return 0; -#endif /* HAVE_NET_IF_DL_H */ -#endif /* SIOCGENADDR */ -#endif /* SIOCGIFHWADDR */ - if (!a[0] && !a[1] && !a[2] && !a[3] && !a[4] && !a[5]) - continue; - if (node_id) { - memcpy(node_id, a, 6); - close(sd); - return 1; - } - } - close(sd); -#endif - return 0; -} - -#if defined(__linux__) && defined(__NR_gettid) && defined(HAVE_JRAND48) -#define DO_JRAND_MIX -static unsigned short ul_jrand_seed[3]; -#endif - -static int random_get_fd(void) -{ - int i, fd = -1; - struct timeval tv; - - gettimeofday(&tv, NULL); -#ifndef _WIN32 - fd = open("/dev/urandom", O_RDONLY); - if (fd == -1) - fd = open("/dev/random", O_RDONLY | O_NONBLOCK); - if (fd >= 0) { - i = fcntl(fd, F_GETFD); - if (i >= 0) - fcntl(fd, F_SETFD, i | FD_CLOEXEC); - } -#endif - srand((getpid() << 16) ^ getuid() ^ tv.tv_sec ^ tv.tv_usec); - -#ifdef DO_JRAND_MIX - ul_jrand_seed[0] = getpid() ^ (tv.tv_sec & 0xFFFF); - ul_jrand_seed[1] = getppid() ^ (tv.tv_usec & 0xFFFF); - ul_jrand_seed[2] = (tv.tv_sec ^ tv.tv_usec) >> 16; -#endif - /* Crank the random number generator a few times */ - gettimeofday(&tv, NULL); - for (i = (tv.tv_sec ^ tv.tv_usec) & 0x1F; i > 0; i--) - rand(); - return fd; -} - -/* - * Generate a stream of random nbytes into buf. - * Use /dev/urandom if possible, and if not, - * use glibc pseudo-random functions. - */ -static void random_get_bytes(void *buf, size_t nbytes) -{ - size_t i, n = nbytes; - int fd = random_get_fd(); - int lose_counter = 0; - unsigned char *cp = (unsigned char *) buf; - - if (fd >= 0) { - while (n > 0) { - ssize_t x = read(fd, cp, n); - if (x <= 0) { - if (lose_counter++ > 16) - break; - continue; - } - n -= x; - cp += x; - lose_counter = 0; - } - - close(fd); - } - - /* - * We do this all the time, but this is the only source of - * randomness if /dev/random/urandom is out to lunch. - */ - for (cp = buf, i = 0; i < nbytes; i++) - *cp++ ^= (rand() >> 7) & 0xFF; - -#ifdef DO_JRAND_MIX - { - unsigned short tmp_seed[3]; - - memcpy(tmp_seed, ul_jrand_seed, sizeof(tmp_seed)); - ul_jrand_seed[2] = ul_jrand_seed[2] ^ syscall(__NR_gettid); - for (cp = buf, i = 0; i < nbytes; i++) - *cp++ ^= (jrand48(tmp_seed) >> 7) & 0xFF; - memcpy(ul_jrand_seed, tmp_seed, - sizeof(ul_jrand_seed)-sizeof(unsigned short)); - } -#endif - - return; -} - -/* Assume that the gettimeofday() has microsecond granularity */ -#define MAX_ADJUSTMENT 10 - -/* - * Get clock from global sequence clock counter. - * - * Return -1 if the clock counter could not be opened/locked (in this case - * pseudorandom value is returned in @ret_clock_seq), otherwise return 0. - */ -static int get_clock(uint32_t *clock_high, uint32_t *clock_low, - uint16_t *ret_clock_seq, int *num) -{ - THREAD_LOCAL int adjustment = 0; - THREAD_LOCAL struct timeval last = {0, 0}; - THREAD_LOCAL int state_fd = -2; - THREAD_LOCAL FILE *state_f; - THREAD_LOCAL uint16_t clock_seq; - struct timeval tv; - uint64_t clock_reg; - mode_t save_umask; - int len; - int ret = 0; - - if (state_fd == -2) { - save_umask = umask(0); - state_fd = open(LIBUUID_CLOCK_FILE, O_RDWR|O_CREAT, 0660); - (void) umask(save_umask); - if (state_fd != -1) { - state_f = fdopen(state_fd, "r+"); - if (!state_f) { - close(state_fd); - state_fd = -1; - ret = -1; - } - } - else - ret = -1; - } - if (state_fd >= 0) { - rewind(state_f); - } - if (state_fd >= 0) { - unsigned int cl; - unsigned long tv1, tv2; - int a; - - if (fscanf(state_f, "clock: %04x tv: %lu %lu adj: %d\n", - &cl, &tv1, &tv2, &a) == 4) { - clock_seq = cl & 0x3FFF; - last.tv_sec = tv1; - last.tv_usec = tv2; - adjustment = a; - } - } - - if ((last.tv_sec == 0) && (last.tv_usec == 0)) { - random_get_bytes(&clock_seq, sizeof(clock_seq)); - clock_seq &= 0x3FFF; - gettimeofday(&last, NULL); - last.tv_sec--; - } - -try_again: - gettimeofday(&tv, NULL); - if ((tv.tv_sec < last.tv_sec) || - ((tv.tv_sec == last.tv_sec) && - (tv.tv_usec < last.tv_usec))) { - clock_seq = (clock_seq+1) & 0x3FFF; - adjustment = 0; - last = tv; - } else if ((tv.tv_sec == last.tv_sec) && - (tv.tv_usec == last.tv_usec)) { - if (adjustment >= MAX_ADJUSTMENT) - goto try_again; - adjustment++; - } else { - adjustment = 0; - last = tv; - } - - clock_reg = tv.tv_usec*10 + adjustment; - clock_reg += ((uint64_t) tv.tv_sec)*10000000; - clock_reg += (((uint64_t) 0x01B21DD2) << 32) + 0x13814000; - - if (num && (*num > 1)) { - adjustment += *num - 1; - last.tv_usec += adjustment / 10; - adjustment = adjustment % 10; - last.tv_sec += last.tv_usec / 1000000; - last.tv_usec = last.tv_usec % 1000000; - } - - if (state_fd >= 0) { - rewind(state_f); - len = fprintf(state_f, - "clock: %04x tv: %016lu %08lu adj: %08d\n", - clock_seq, (unsigned long) last.tv_sec, (unsigned long) last.tv_usec, adjustment); - fflush(state_f); - if (ftruncate(state_fd, len) < 0) { - fprintf(state_f, " \n"); - fflush(state_f); - } - rewind(state_f); - } - - *clock_high = clock_reg >> 32; - *clock_low = clock_reg; - *ret_clock_seq = clock_seq; - return ret; -} - - -int __uuid_generate_time(uuid_t out, int *num) -{ - static unsigned char node_id[6]; - static int has_init = 0; - struct uuid uu; - uint32_t clock_mid; - int ret; - - if (!has_init) { - if (get_node_id(node_id) <= 0) { - random_get_bytes(node_id, 6); - /* - * Set multicast bit, to prevent conflicts - * with IEEE 802 addresses obtained from - * network cards - */ - node_id[0] |= 0x01; - } - has_init = 1; - } - ret = get_clock(&clock_mid, &uu.time_low, &uu.clock_seq, num); - uu.clock_seq |= 0x8000; - uu.time_mid = (uint16_t) clock_mid; - uu.time_hi_and_version = ((clock_mid >> 16) & 0x0FFF) | 0x1000; - memcpy(uu.node, node_id, 6); - uuid_pack(&uu, out); - return ret; -} - -/* - * Generate time-based UUID and store it to @out - * - * Since there is no daemon here, use fall-back right away - */ -static int uuid_generate_time_generic(uuid_t out) { - return __uuid_generate_time(out, 0); -} - -/* - * Generate time-based UUID and store it to @out. - * - * Discards return value from uuid_generate_time_generic() - */ -void uuid_generate_time(uuid_t out) -{ - (void)uuid_generate_time_generic(out); -} - - -int uuid_generate_time_safe(uuid_t out) -{ - return uuid_generate_time_generic(out); -} - - -void __uuid_generate_random(uuid_t out, int *num) -{ - uuid_t buf; - struct uuid uu; - int i, n; - - if (!num || !*num) - n = 1; - else - n = *num; - - for (i = 0; i < n; i++) { - random_get_bytes(buf, sizeof(buf)); - uuid_unpack(buf, &uu); - - uu.clock_seq = (uu.clock_seq & 0x3FFF) | 0x8000; - uu.time_hi_and_version = (uu.time_hi_and_version & 0x0FFF) - | 0x4000; - uuid_pack(&uu, out); - out += sizeof(uuid_t); - } -} - -void uuid_generate_random(uuid_t out) -{ - int num = 1; - /* No real reason to use the daemon for random uuid's -- yet */ - - __uuid_generate_random(out, &num); -} - -/* - * Check whether good random source (/dev/random or /dev/urandom) - * is available. - */ -static int have_random_source(void) -{ - struct stat s; - - return (!stat("/dev/random", &s) || !stat("/dev/urandom", &s)); -} - - -/* - * This is the generic front-end to uuid_generate_random and - * uuid_generate_time. It uses uuid_generate_random only if - * /dev/urandom is available, since otherwise we won't have - * high-quality randomness. - */ -void uuid_generate(uuid_t out) -{ - if (have_random_source()) - uuid_generate_random(out); - else - uuid_generate_time(out); -} diff --git a/src/rigraph/vendor/uuid/isnull.c b/src/rigraph/vendor/uuid/isnull.c deleted file mode 100644 index 931e7e7..0000000 --- a/src/rigraph/vendor/uuid/isnull.c +++ /dev/null @@ -1,48 +0,0 @@ -/* - * isnull.c --- Check whether or not the UUID is null - * - * Copyright (C) 1996, 1997 Theodore Ts'o. - * - * %Begin-Header% - * 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, and the entire permission notice in its entirety, - * including the disclaimer of warranties. - * 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. The name of the author may not be used to endorse or promote - * products derived from this software without specific prior - * written permission. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF - * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR 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 NOT ADVISED OF THE POSSIBILITY OF SUCH - * DAMAGE. - * %End-Header% - */ - -#include "uuidP.h" - -/* Returns 1 if the uuid is the NULL uuid */ -int uuid_is_null(const uuid_t uu) -{ - const unsigned char *cp; - int i; - - for (i=0, cp = uu; i < 16; i++) - if (*cp++) - return 0; - return 1; -} - diff --git a/src/rigraph/vendor/uuid/pack.c b/src/rigraph/vendor/uuid/pack.c deleted file mode 100644 index 6e12476..0000000 --- a/src/rigraph/vendor/uuid/pack.c +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Internal routine for packing UUIDs - * - * Copyright (C) 1996, 1997 Theodore Ts'o. - * - * %Begin-Header% - * 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, and the entire permission notice in its entirety, - * including the disclaimer of warranties. - * 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. The name of the author may not be used to endorse or promote - * products derived from this software without specific prior - * written permission. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF - * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR 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 NOT ADVISED OF THE POSSIBILITY OF SUCH - * DAMAGE. - * %End-Header% - */ - -#include -#include "uuidP.h" - -void uuid_pack(const struct uuid *uu, uuid_t ptr) -{ - uint32_t tmp; - unsigned char *out = ptr; - - tmp = uu->time_low; - out[3] = (unsigned char) tmp; - tmp >>= 8; - out[2] = (unsigned char) tmp; - tmp >>= 8; - out[1] = (unsigned char) tmp; - tmp >>= 8; - out[0] = (unsigned char) tmp; - - tmp = uu->time_mid; - out[5] = (unsigned char) tmp; - tmp >>= 8; - out[4] = (unsigned char) tmp; - - tmp = uu->time_hi_and_version; - out[7] = (unsigned char) tmp; - tmp >>= 8; - out[6] = (unsigned char) tmp; - - tmp = uu->clock_seq; - out[9] = (unsigned char) tmp; - tmp >>= 8; - out[8] = (unsigned char) tmp; - - memcpy(out+10, uu->node, 6); -} - diff --git a/src/rigraph/vendor/uuid/parse.c b/src/rigraph/vendor/uuid/parse.c deleted file mode 100644 index 074383e..0000000 --- a/src/rigraph/vendor/uuid/parse.c +++ /dev/null @@ -1,79 +0,0 @@ -/* - * parse.c --- UUID parsing - * - * Copyright (C) 1996, 1997 Theodore Ts'o. - * - * %Begin-Header% - * 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, and the entire permission notice in its entirety, - * including the disclaimer of warranties. - * 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. The name of the author may not be used to endorse or promote - * products derived from this software without specific prior - * written permission. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF - * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR 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 NOT ADVISED OF THE POSSIBILITY OF SUCH - * DAMAGE. - * %End-Header% - */ - -#include -#include -#include -#include - -#include "uuidP.h" - -int uuid_parse(const char *in, uuid_t uu) -{ - struct uuid uuid; - int i; - const char *cp; - char buf[3]; - - if (strlen(in) != 36) - return -1; - for (i=0, cp = in; i <= 36; i++,cp++) { - if ((i == 8) || (i == 13) || (i == 18) || - (i == 23)) { - if (*cp == '-') - continue; - else - return -1; - } - if (i== 36) - if (*cp == 0) - continue; - if (!isxdigit(*cp)) - return -1; - } - uuid.time_low = strtoul(in, NULL, 16); - uuid.time_mid = strtoul(in+9, NULL, 16); - uuid.time_hi_and_version = strtoul(in+14, NULL, 16); - uuid.clock_seq = strtoul(in+19, NULL, 16); - cp = in+24; - buf[2] = 0; - for (i=0; i < 6; i++) { - buf[0] = *cp++; - buf[1] = *cp++; - uuid.node[i] = strtoul(buf, NULL, 16); - } - - uuid_pack(&uuid, uu); - return 0; -} diff --git a/src/rigraph/vendor/uuid/unpack.c b/src/rigraph/vendor/uuid/unpack.c deleted file mode 100644 index beaaff3..0000000 --- a/src/rigraph/vendor/uuid/unpack.c +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Internal routine for unpacking UUID - * - * Copyright (C) 1996, 1997 Theodore Ts'o. - * - * %Begin-Header% - * 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, and the entire permission notice in its entirety, - * including the disclaimer of warranties. - * 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. The name of the author may not be used to endorse or promote - * products derived from this software without specific prior - * written permission. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF - * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR 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 NOT ADVISED OF THE POSSIBILITY OF SUCH - * DAMAGE. - * %End-Header% - */ - -#include -#include "uuidP.h" - -void uuid_unpack(const uuid_t in, struct uuid *uu) -{ - const uint8_t *ptr = in; - uint32_t tmp; - - tmp = *ptr++; - tmp = (tmp << 8) | *ptr++; - tmp = (tmp << 8) | *ptr++; - tmp = (tmp << 8) | *ptr++; - uu->time_low = tmp; - - tmp = *ptr++; - tmp = (tmp << 8) | *ptr++; - uu->time_mid = tmp; - - tmp = *ptr++; - tmp = (tmp << 8) | *ptr++; - uu->time_hi_and_version = tmp; - - tmp = *ptr++; - tmp = (tmp << 8) | *ptr++; - uu->clock_seq = tmp; - - memcpy(uu->node, ptr, 6); -} - diff --git a/src/rigraph/vendor/uuid/unparse.c b/src/rigraph/vendor/uuid/unparse.c deleted file mode 100644 index a95bbb0..0000000 --- a/src/rigraph/vendor/uuid/unparse.c +++ /dev/null @@ -1,76 +0,0 @@ -/* - * unparse.c -- convert a UUID to string - * - * Copyright (C) 1996, 1997 Theodore Ts'o. - * - * %Begin-Header% - * 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, and the entire permission notice in its entirety, - * including the disclaimer of warranties. - * 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. The name of the author may not be used to endorse or promote - * products derived from this software without specific prior - * written permission. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF - * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR 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 NOT ADVISED OF THE POSSIBILITY OF SUCH - * DAMAGE. - * %End-Header% - */ - -#include - -#include "uuidP.h" - -static const char *fmt_lower = - "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x"; - -static const char *fmt_upper = - "%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X"; - -#ifdef UUID_UNPARSE_DEFAULT_UPPER -#define FMT_DEFAULT fmt_upper -#else -#define FMT_DEFAULT fmt_lower -#endif - -static void uuid_unparse_x(const uuid_t uu, char *out, const char *fmt) -{ - struct uuid uuid; - - uuid_unpack(uu, &uuid); - sprintf(out, fmt, - uuid.time_low, uuid.time_mid, uuid.time_hi_and_version, - uuid.clock_seq >> 8, uuid.clock_seq & 0xFF, - uuid.node[0], uuid.node[1], uuid.node[2], - uuid.node[3], uuid.node[4], uuid.node[5]); -} - -void uuid_unparse_lower(const uuid_t uu, char *out) -{ - uuid_unparse_x(uu, out, fmt_lower); -} - -void uuid_unparse_upper(const uuid_t uu, char *out) -{ - uuid_unparse_x(uu, out, fmt_upper); -} - -void uuid_unparse(const uuid_t uu, char *out) -{ - uuid_unparse_x(uu, out, FMT_DEFAULT); -} diff --git a/src/rigraph/vendor/uuid/uuid.h b/src/rigraph/vendor/uuid/uuid.h deleted file mode 100644 index 874d65a..0000000 --- a/src/rigraph/vendor/uuid/uuid.h +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Public include file for the UUID library - * - * Copyright (C) 1996, 1997, 1998 Theodore Ts'o. - * - * %Begin-Header% - * 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, and the entire permission notice in its entirety, - * including the disclaimer of warranties. - * 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. The name of the author may not be used to endorse or promote - * products derived from this software without specific prior - * written permission. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF - * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR 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 NOT ADVISED OF THE POSSIBILITY OF SUCH - * DAMAGE. - * %End-Header% - */ - -#ifndef _UUID_UUID_H -#define _UUID_UUID_H - -#include -#ifndef _WIN32 -#include -#endif -#include - -typedef unsigned char uuid_t[16]; - -/* UUID Variant definitions */ -#define UUID_VARIANT_NCS 0 -#define UUID_VARIANT_DCE 1 -#define UUID_VARIANT_MICROSOFT 2 -#define UUID_VARIANT_OTHER 3 - -/* UUID Type definitions */ -#define UUID_TYPE_DCE_TIME 1 -#define UUID_TYPE_DCE_RANDOM 4 - -/* Allow UUID constants to be defined */ -#ifdef __GNUC__ -#define UUID_DEFINE(name,u0,u1,u2,u3,u4,u5,u6,u7,u8,u9,u10,u11,u12,u13,u14,u15) \ - static const uuid_t name __attribute__ ((unused)) = {u0,u1,u2,u3,u4,u5,u6,u7,u8,u9,u10,u11,u12,u13,u14,u15} -#else -#define UUID_DEFINE(name,u0,u1,u2,u3,u4,u5,u6,u7,u8,u9,u10,u11,u12,u13,u14,u15) \ - static const uuid_t name = {u0,u1,u2,u3,u4,u5,u6,u7,u8,u9,u10,u11,u12,u13,u14,u15} -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -/* clear.c */ -void uuid_clear(uuid_t uu); - -/* compare.c */ -int uuid_compare(const uuid_t uu1, const uuid_t uu2); - -/* copy.c */ -void uuid_copy(uuid_t dst, const uuid_t src); - -/* gen_uuid.c */ -void uuid_generate(uuid_t out); -void uuid_generate_random(uuid_t out); -void uuid_generate_time(uuid_t out); -int uuid_generate_time_safe(uuid_t out); - -/* isnull.c */ -int uuid_is_null(const uuid_t uu); - -/* parse.c */ -int uuid_parse(const char *in, uuid_t uu); - -/* unparse.c */ -void uuid_unparse(const uuid_t uu, char *out); -void uuid_unparse_lower(const uuid_t uu, char *out); -void uuid_unparse_upper(const uuid_t uu, char *out); - -/* uuid_time.c */ -time_t uuid_time(const uuid_t uu, struct timeval *ret_tv); -int uuid_type(const uuid_t uu); -int uuid_variant(const uuid_t uu); - -#ifdef __cplusplus -} -#endif - -#endif /* _UUID_UUID_H */ diff --git a/src/rigraph/vendor/uuid/uuidP.h b/src/rigraph/vendor/uuid/uuidP.h deleted file mode 100644 index 604d8bf..0000000 --- a/src/rigraph/vendor/uuid/uuidP.h +++ /dev/null @@ -1,63 +0,0 @@ -/* - * uuid.h -- private header file for uuids - * - * Copyright (C) 1996, 1997 Theodore Ts'o. - * - * %Begin-Header% - * 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, and the entire permission notice in its entirety, - * including the disclaimer of warranties. - * 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. The name of the author may not be used to endorse or promote - * products derived from this software without specific prior - * written permission. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF - * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR 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 NOT ADVISED OF THE POSSIBILITY OF SUCH - * DAMAGE. - * %End-Header% - */ - -#include -#include - -#include "config.h" - -#include "uuid.h" - -#define LIBUUID_CLOCK_FILE "/var/lib/libuuid/clock.txt" - -/* - * Offset between 15-Oct-1582 and 1-Jan-70 - */ -#define TIME_OFFSET_HIGH 0x01B21DD2 -#define TIME_OFFSET_LOW 0x13814000 - -struct uuid { - uint32_t time_low; - uint16_t time_mid; - uint16_t time_hi_and_version; - uint16_t clock_seq; - uint8_t node[6]; -}; - - -/* - * prototypes - */ -void uuid_pack(const struct uuid *uu, uuid_t ptr); -void uuid_unpack(const uuid_t in, struct uuid *uu); diff --git a/src/rigraph/vendor/uuid/uuidd.h b/src/rigraph/vendor/uuid/uuidd.h deleted file mode 100644 index 2f70968..0000000 --- a/src/rigraph/vendor/uuid/uuidd.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Definitions used by the uuidd daemon - * - * Copyright (C) 2007 Theodore Ts'o. - * - * %Begin-Header% - * 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, and the entire permission notice in its entirety, - * including the disclaimer of warranties. - * 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. The name of the author may not be used to endorse or promote - * products derived from this software without specific prior - * written permission. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF - * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR 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 NOT ADVISED OF THE POSSIBILITY OF SUCH - * DAMAGE. - * %End-Header% - */ - -#ifndef _UUID_UUIDD_H -#define _UUID_UUIDD_H - -#define UUIDD_DIR _PATH_LOCALSTATEDIR "/uuidd" -#define UUIDD_SOCKET_PATH UUIDD_DIR "/request" -#define UUIDD_PIDFILE_PATH UUIDD_DIR "/uuidd.pid" -#define UUIDD_PATH "/usr/sbin/uuidd" - -#define UUIDD_OP_GETPID 0 -#define UUIDD_OP_GET_MAXOP 1 -#define UUIDD_OP_TIME_UUID 2 -#define UUIDD_OP_RANDOM_UUID 3 -#define UUIDD_OP_BULK_TIME_UUID 4 -#define UUIDD_OP_BULK_RANDOM_UUID 5 -#define UUIDD_MAX_OP UUIDD_OP_BULK_RANDOM_UUID - -extern int __uuid_generate_time(uuid_t out, int *num); -extern void __uuid_generate_random(uuid_t out, int *num); - -#endif /* _UUID_UUID_H */ diff --git a/src/rigraph/vendor/uuid/win32/config.h b/src/rigraph/vendor/uuid/win32/config.h deleted file mode 100644 index ffb2aba..0000000 --- a/src/rigraph/vendor/uuid/win32/config.h +++ /dev/null @@ -1,84 +0,0 @@ -/* src/config.h. Generated from config.h.in by configure. */ -/* src/config.h.in. Generated from configure.ac by autoheader. */ -/* -- reflects MinGW + Win32 -- */ - -/* Define to 1 if you have the header file. */ -#define HAVE_INTTYPES_H 1 - -/* Define to 1 if you have the `jrand48' function. */ -/* #undef HAVE_JRAND48 */ - -/* Define to 1 if you have the header file. */ -#define HAVE_MEMORY_H 1 - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_NETINET_IN_H */ - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_NET_IF_DL_H */ - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_NET_IF_H */ - -/* Define if struct sockaddr contains sa_len */ -/* #undef HAVE_SA_LEN */ - -/* Define to 1 if you have the header file. */ -#define HAVE_STDINT_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_STDLIB_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_STRINGS_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_STRING_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_SYS_FILE_H 1 - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_SYS_IOCTL_H */ - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_SYS_SOCKET_H */ - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_SYS_SOCKIO_H */ - -/* Define to 1 if you have the header file. */ -#define HAVE_SYS_STAT_H 1 - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_SYS_SYSCALL_H */ - -/* Define to 1 if you have the header file. */ -#define HAVE_SYS_TIME_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_SYS_TYPES_H 1 - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_SYS_UN_H */ - -/* Define to 1 if you have the header file. */ -#define HAVE_UNISTD_H 1 - -/* Define to the address where bug reports for this package should be sent. */ -#define PACKAGE_BUGREPORT "Simon.Urbanek@r-project.org" - -/* Define to the full name of this package. */ -#define PACKAGE_NAME "uuid" - -/* Define to the full name and version of this package. */ -#define PACKAGE_STRING "uuid 0.1" - -/* Define to the one symbol short name of this package. */ -#define PACKAGE_TARNAME "uuid" - -/* Define to the version of this package. */ -#define PACKAGE_VERSION "0.1" - -/* Define to 1 if you have the ANSI C header files. */ -#define STDC_HEADERS 1 From 72ef3f842f1f6a591994f7e7df569e4310b83b14 Mon Sep 17 00:00:00 2001 From: evanbiederstedt Date: Thu, 7 Apr 2022 19:22:18 -0400 Subject: [PATCH 09/20] finally getting somewhere --- R/RcppExports.R | 23 ------- src/.DS_Store | Bin 6148 -> 0 bytes src/Makevars | 14 ++-- src/RcppExports.cpp | 39 ----------- src/leiden.cpp | 80 ---------------------- src/rigraph/Makefile | 17 +++++ src/rigraph/core/centrality/betweenness.c | 6 +- 7 files changed, 26 insertions(+), 153 deletions(-) delete mode 100644 R/RcppExports.R delete mode 100644 src/.DS_Store delete mode 100644 src/RcppExports.cpp delete mode 100644 src/leiden.cpp create mode 100644 src/rigraph/Makefile diff --git a/R/RcppExports.R b/R/RcppExports.R deleted file mode 100644 index 0826ef9..0000000 --- a/R/RcppExports.R +++ /dev/null @@ -1,23 +0,0 @@ -# Generated by using Rcpp::compileAttributes() -> do not edit by hand -# Generator token: 10BE3573-1514-4C36-9D1C-5A225CD40393 - -#' Finds the optimal partition using the Leiden algorithm -#' -#' @param graph The igraph graph to define the partition on -#' @param edge_weights Vector of edge weights. In weighted graphs, a real number is assigned to each (directed or undirected) edge. Refer to igraph, weighted graphs. -#' @param resolution Integer resoluiton parameter controlling communities detected (default=1.0) Higher resolutions lead to more communities, while lower resolutions lead to fewer communities. -#' @param niter Number of iterations that the algorithm should be run for (default=2) -#' @return A vector of membership values -#' @examples -#' library(igraph) -#' library(leidenAlg) -#' -#' g <- make_star(10) -#' E(g)$weight <- seq(ecount(g)) -#' find_partition(g, E(g)$weight) -#' -#' @export -find_partition <- function(graph, edge_weights, resolution = 1.0, niter = 2L) { - .Call('_leidenAlg_find_partition', PACKAGE = 'leidenAlg', graph, edge_weights, resolution, niter) -} - diff --git a/src/.DS_Store b/src/.DS_Store deleted file mode 100644 index cbc1e85bc7a887fa4e25d2822f7a84ba834ad5ef..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeHK%}T>S5Z<-brW7Fug&r5Y7ObrlikA@U3mDOZN=-;)@FZeqVE3DypWVy{*&oIjcNgIaV>V;Vf`-UZsSq?*x^_%3B3E_*GD%fJ6iy)I?j}k?xpd_s2{V=JX@}Lcnq#}YTJ?s5j_CJ? z>yB6rd)F&I)&U(}pV41OL;)S&5{SZ}W3bQ& z9uTfm0d*=jPYkZp!7fakW3bSu(-~JQ!#rl?^6|pe>R=ZtoN-4Z^~3-%u*pD84{bdE zFW{G{eB^JY(1;iy2L2fXygu?qE)-?X)^FwESu3DDKtsW}0u>O@H!cA%z do not edit by hand -// Generator token: 10BE3573-1514-4C36-9D1C-5A225CD40393 - -#include -#define NDEBUG 1 -#include -#include - -using namespace Rcpp; - -#ifdef RCPP_USE_GLOBAL_ROSTREAM -Rcpp::Rostream& Rcpp::Rcout = Rcpp::Rcpp_cout_get(); -Rcpp::Rostream& Rcpp::Rcerr = Rcpp::Rcpp_cerr_get(); -#endif - -// find_partition -std::vector find_partition(SEXP graph, std::vector& edge_weights, double resolution, int niter); -RcppExport SEXP _leidenAlg_find_partition(SEXP graphSEXP, SEXP edge_weightsSEXP, SEXP resolutionSEXP, SEXP niterSEXP) { -BEGIN_RCPP - Rcpp::RObject rcpp_result_gen; - Rcpp::RNGScope rcpp_rngScope_gen; - Rcpp::traits::input_parameter< SEXP >::type graph(graphSEXP); - Rcpp::traits::input_parameter< std::vector& >::type edge_weights(edge_weightsSEXP); - Rcpp::traits::input_parameter< double >::type resolution(resolutionSEXP); - Rcpp::traits::input_parameter< int >::type niter(niterSEXP); - rcpp_result_gen = Rcpp::wrap(find_partition(graph, edge_weights, resolution, niter)); - return rcpp_result_gen; -END_RCPP -} - -static const R_CallMethodDef CallEntries[] = { - {"_leidenAlg_find_partition", (DL_FUNC) &_leidenAlg_find_partition, 4}, - {NULL, NULL, 0} -}; - -RcppExport void R_init_leidenAlg(DllInfo *dll) { - R_registerRoutines(dll, NULL, CallEntries, NULL, NULL); - R_useDynamicSymbols(dll, FALSE); -} diff --git a/src/leiden.cpp b/src/leiden.cpp deleted file mode 100644 index 073f59c..0000000 --- a/src/leiden.cpp +++ /dev/null @@ -1,80 +0,0 @@ -#include -#include -#include -#include -#include "igraph.h" -#include "leidenalg/include/GraphHelper.h" -#include "leidenalg/include/Optimiser.h" -#include "leidenalg/include/RBERVertexPartition.h" -#include "leidenalg/include/RBConfigurationVertexPartition.h" -#include - -using namespace std; -using namespace Rcpp; - - -// a wrapper for the Leidgen algorithm implementation (https://github.com/vtraag/leidenalg) - -int R_SEXP_to_vector(SEXP sv, igraph_vector_t *v) { - v->stor_begin=REAL(sv); - v->stor_end=v->stor_begin+GET_LENGTH(sv); - v->end=v->stor_end; - return 0; -} - -int R_SEXP_to_igraph(SEXP graph, igraph_t *res) { - - res->n=(igraph_integer_t) REAL(VECTOR_ELT(graph, 0))[0]; - res->directed=LOGICAL(VECTOR_ELT(graph, 1))[0]; - R_SEXP_to_vector(VECTOR_ELT(graph, 2), &res->from); - R_SEXP_to_vector(VECTOR_ELT(graph, 3), &res->to); - R_SEXP_to_vector(VECTOR_ELT(graph, 4), &res->oi); - R_SEXP_to_vector(VECTOR_ELT(graph, 5), &res->ii); - R_SEXP_to_vector(VECTOR_ELT(graph, 6), &res->os); - R_SEXP_to_vector(VECTOR_ELT(graph, 7), &res->is); - - /* attributes */ - REAL(VECTOR_ELT(VECTOR_ELT(graph, 8), 0))[0] = 1; /* R objects refcount */ - REAL(VECTOR_ELT(VECTOR_ELT(graph, 8), 0))[1] = 0; /* igraph_t objects */ - res->attr=VECTOR_ELT(graph, 8); - - return 0; -} - -//' Finds the optimal partition using the Leiden algorithm -//' -//' @param graph The igraph graph to define the partition on -//' @param edge_weights Vector of edge weights. In weighted graphs, a real number is assigned to each (directed or undirected) edge. Refer to igraph, weighted graphs. -//' @param resolution Integer resoluiton parameter controlling communities detected (default=1.0) Higher resolutions lead to more communities, while lower resolutions lead to fewer communities. -//' @param niter Number of iterations that the algorithm should be run for (default=2) -//' @return A vector of membership values -//' @examples -//' library(igraph) -//' library(leidenAlg) -//' -//' g <- make_star(10) -//' E(g)$weight <- seq(ecount(g)) -//' find_partition(g, E(g)$weight) -//' -//' @export -// [[Rcpp::export]] -std::vector find_partition(SEXP graph, std::vector& edge_weights, double resolution=1.0, int niter=2) { - igraph_t g; - - R_SEXP_to_igraph(graph, &g); - Graph og(&g, edge_weights); - Optimiser o( (int) (R::runif(0,1)*(double)RAND_MAX) ); - RBConfigurationVertexPartition p(&og,resolution); - //RBERVertexPartition p(&og,resolution); - //o.find_partition(og,resolution); - double val=1; - int iter=0; - while(val>0 && (iter Date: Thu, 7 Apr 2022 20:29:34 -0400 Subject: [PATCH 10/20] making progress --- src/Makevars | 5 +- src/rigraph/Makefile | 19 ++- src/rigraph/core/centrality/betweenness.c | 2 + .../core/centrality/centrality_other.c | 8 +- src/rigraph/core/centrality/centralization.c | 2 +- src/rigraph/core/centrality/closeness.c | 6 +- src/rigraph/core/centrality/prpack.cpp | 16 +- .../centrality/prpack/prpack_igraph_graph.cpp | 2 +- src/rigraph/core/cliques/cliquer/cliquer.c | 18 +-- src/rigraph/core/cliques/cliquer_wrapper.c | 10 +- src/rigraph/core/core/interruption.h | 4 +- src/rigraph/include/config.h | 139 ++++++++++++++++++ 12 files changed, 195 insertions(+), 36 deletions(-) create mode 100644 src/rigraph/include/config.h diff --git a/src/Makevars b/src/Makevars index 34855b2..e70087d 100644 --- a/src/Makevars +++ b/src/Makevars @@ -3,7 +3,10 @@ PKG_CXXFLAGS= -I"../inst/include" -I"./rigraph/include" -I"./leidenalg/include" $(SHLIB_OPENMP_CXXFLAGS) -PKG_CPPFLAGS= -I"../inst/include" -I"./rigraph/include" -I"./leidenalg/include" $(SHLIB_OPENMP_CXXFLAGS) + +PKG_CFLAGS = -std=c11 -std=gnu11 -O3 -pthread -fPIC $(SHLIB_OPENMP_CXXFLAGS) +PKG_CFLAGS += -I"./include" -I"../rigraph/include" +LDFLAGS += -lpthread diff --git a/src/rigraph/Makefile b/src/rigraph/Makefile index 45e3c8e..b8fd21a 100644 --- a/src/rigraph/Makefile +++ b/src/rigraph/Makefile @@ -1,17 +1,26 @@ -C ?= gcc -PKG_CFLAGS = -O3 -pthread -fPIC $(SHLIB_OPENMP_CXXFLAGS) -PKG_CFLAGS += -I"./include" -I"../rigraph/include" +C ?= gcc +CFLAGS = -O3 -std=c11 -std=gnu11 +PKG_CFLAGS = -O3 -std=c11 -std=gnu11 -pthread -fPIC $(SHLIB_OPENMP_CXXFLAGS) +PKG_CFLAGS += -I"./include" -I"../rigraph/include" LDFLAGS += -lpthread +CXX ?= g++ +PKG_CXXFLAGS = -O3 -std=c++11 -pthread -fPIC $(SHLIB_OPENMP_CXXFLAGS) +PKG_CXXFLAGS += -I"./include" -I"../rigraph/include" +LDFLAGS += -lpthread -OBJECTS=core/centrality/betweenness.o core/centrality/centrality_other.o core/centrality/centralization.o core/centrality/closeness.o core/centrality/coreness.o core/centrality/prpack.o core/centrality/prpack/prpack_base_graph.o core/centrality/prpack/prpack_igraph_graph.o core/centrality/prpack/prpack_preprocessed_ge_graph.o core/centrality/prpack/prpack_preprocessed_gs_graph.o core/centrality/prpack/prpack_preprocessed_scc_graph.o core/centrality/prpack/prpack_preprocessed_schur_graph.o core/centrality/prpack/prpack_result.o core/centrality/prpack/prpack_solver.o core/centrality/prpack/prpack_utils.o core/cliques/cliquer/cliquer.o core/cliques/cliquer/cliquer_graph.o core/cliques/cliquer/reorder.o core/cliques/cliquer_wrapper.o core/cliques/cliques.o core/cliques/glet.o core/cliques/maximal_cliques.o core/community/community_misc.o core/community/edge_betweenness.o core/community/fast_modularity.o core/community/fluid.o core/community/infomap/infomap.o core/community/infomap/infomap_FlowGraph.o core/community/infomap/infomap_Greedy.o core/community/infomap/infomap_Node.o core/community/label_propagation.o core/community/leading_eigenvector.o core/community/leiden.o core/community/louvain.o core/community/modularity.o core/community/optimal_modularity.o core/community/spinglass/NetDataTypes.o core/community/spinglass/NetRoutines.o core/community/spinglass/clustertool.o core/community/spinglass/pottsmodel_2.o core/community/walktrap/walktrap.o core/community/walktrap/walktrap_communities.o core/community/walktrap/walktrap_graph.o core/community/walktrap/walktrap_heap.o core/connectivity/cohesive_blocks.o core/connectivity/components.o core/connectivity/separators.o core/constructors/adjacency.o core/constructors/atlas.o core/constructors/basic_constructors.o core/constructors/de_bruijn.o core/constructors/famous.o core/constructors/full.o core/constructors/kautz.o core/constructors/lcf.o core/constructors/linegraph.o core/constructors/prufer.o core/constructors/regular.o core/core/array.o core/core/buckets.o core/core/cutheap.o core/core/dqueue.o core/core/error.o core/core/estack.o core/core/fixed_vectorlist.o core/core/grid.o core/core/heap.o core/core/indheap.o core/core/interruption.o core/core/marked_queue.o core/core/matrix.o core/core/memory.o core/core/printing.o core/core/progress.o core/core/psumtree.o core/core/set.o core/core/sparsemat.o core/core/spmatrix.o core/core/stack.o core/core/statusbar.o core/core/strvector.o core/core/trie.o core/core/vector.o core/core/vector_ptr.o core/flow/flow.o core/flow/st-cuts.o core/games/barabasi.o core/games/callaway_traits.o core/games/citations.o core/games/correlated.o core/games/degree_sequence.o core/games/degree_sequence_vl/gengraph_box_list.o core/games/degree_sequence_vl/gengraph_degree_sequence.o core/games/degree_sequence_vl/gengraph_graph_molloy_hash.o core/games/degree_sequence_vl/gengraph_graph_molloy_optimized.o core/games/degree_sequence_vl/gengraph_mr-connected.o core/games/degree_sequence_vl/gengraph_powerlaw.o core/games/degree_sequence_vl/gengraph_random.o core/games/dotproduct.o core/games/erdos_renyi.o core/games/establishment.o core/games/forestfire.o core/games/grg.o core/games/growing_random.o core/games/islands.o core/games/k_regular.o core/games/preference.o core/games/recent_degree.o core/games/sbm.o core/games/static_fitness.o core/games/tree.o core/games/watts_strogatz.o core/graph/adjlist.o core/graph/attributes.o core/graph/basic_query.o core/graph/cattributes.o core/graph/iterators.o core/graph/type_indexededgelist.o core/graph/visitors.o core/hrg/hrg.o core/hrg/hrg_types.o core/internal/glpk_support.o core/internal/hacks.o core/internal/lsap.o core/internal/qsort.o core/internal/qsort_r.o core/internal/zeroin.o core/io/dimacs.o core/io/dl-lexer.o core/io/dl-parser.o core/io/dl.o core/io/dot.o core/io/edgelist.o core/io/gml-lexer.o core/io/gml-parser.o core/io/gml-tree.o core/io/gml.o core/io/graphdb.o core/io/graphml.o core/io/leda.o core/io/lgl-lexer.o core/io/lgl-parser.o core/io/lgl.o core/io/ncol-lexer.o core/io/ncol-parser.o core/io/ncol.o core/io/pajek-lexer.o core/io/pajek-parser.o core/io/pajek.o core/isomorphism/bliss.o core/isomorphism/bliss/defs.o core/isomorphism/bliss/graph.o core/isomorphism/bliss/heap.o core/isomorphism/bliss/orbit.o core/isomorphism/bliss/partition.o core/isomorphism/bliss/uintseqhash.o core/isomorphism/bliss/utils.o core/isomorphism/isoclasses.o core/isomorphism/isomorphism_misc.o core/isomorphism/lad.o core/isomorphism/queries.o core/isomorphism/vf2.o core/layout/circular.o core/layout/davidson_harel.o core/layout/drl/DensityGrid.o core/layout/drl/DensityGrid_3d.o core/layout/drl/drl_graph.o core/layout/drl/drl_graph_3d.o core/layout/drl/drl_layout.o core/layout/drl/drl_layout_3d.o core/layout/drl/drl_parse.o core/layout/fruchterman_reingold.o core/layout/gem.o core/layout/graphopt.o core/layout/kamada_kawai.o core/layout/large_graph.o core/layout/layout_bipartite.o core/layout/layout_grid.o core/layout/layout_random.o core/layout/mds.o core/layout/merge_dla.o core/layout/merge_grid.o core/layout/reingold_tilford.o core/layout/sugiyama.o core/linalg/arpack.o core/linalg/blas.o core/linalg/eigen.o core/linalg/lapack.o core/math/bfgs.o core/math/complex.o core/math/utils.o core/misc/bipartite.o core/misc/chordality.o core/misc/cocitation.o core/misc/coloring.o core/misc/conversion.o core/misc/degree_sequence.o core/misc/embedding.o core/misc/feedback_arc_set.o core/misc/graphicality.o core/misc/matching.o core/misc/microscopic_update.o core/misc/mixing.o core/misc/motifs.o core/misc/other.o core/misc/scan.o core/misc/sir.o core/misc/spanning_trees.o core/operators/add_edge.o core/operators/complementer.o core/operators/compose.o core/operators/connect_neighborhood.o core/operators/contract.o core/operators/difference.o core/operators/disjoint_union.o core/operators/intersection.o core/operators/misc_internal.o core/operators/permute.o core/operators/rewire.o core/operators/rewire_edges.o core/operators/simplify.o core/operators/subgraph.o core/operators/union.o core/paths/all_shortest_paths.o core/paths/bellman_ford.o core/paths/dijkstra.o core/paths/distances.o core/paths/eulerian.o core/paths/histogram.o core/paths/johnson.o core/paths/random_walk.o core/paths/shortest_paths.o core/paths/simple_paths.o core/paths/unweighted.o core/properties/basic_properties.o core/properties/constraint.o core/properties/convergence_degree.o core/properties/dag.o core/properties/degrees.o core/properties/girth.o core/properties/loops.o core/properties/multiplicity.o core/properties/neighborhood.o core/properties/spectral.o core/properties/trees.o core/properties/triangles.o core/random/random.o core/scg/scg.o core/scg/scg_approximate_methods.o core/scg/scg_exact_scg.o core/scg/scg_kmeans.o core/scg/scg_optimal_method.o core/scg/scg_utils.o core/version.o vendor/cs/cs_add.o vendor/cs/cs_amd.o vendor/cs/cs_chol.o vendor/cs/cs_cholsol.o vendor/cs/cs_compress.o vendor/cs/cs_counts.o vendor/cs/cs_cumsum.o vendor/cs/cs_dfs.o vendor/cs/cs_dmperm.o vendor/cs/cs_droptol.o vendor/cs/cs_dropzeros.o vendor/cs/cs_dupl.o vendor/cs/cs_entry.o vendor/cs/cs_ereach.o vendor/cs/cs_etree.o vendor/cs/cs_fkeep.o vendor/cs/cs_gaxpy.o vendor/cs/cs_happly.o vendor/cs/cs_house.o vendor/cs/cs_ipvec.o vendor/cs/cs_leaf.o vendor/cs/cs_load.o vendor/cs/cs_lsolve.o vendor/cs/cs_ltsolve.o vendor/cs/cs_lu.o vendor/cs/cs_lusol.o vendor/cs/cs_malloc.o vendor/cs/cs_maxtrans.o vendor/cs/cs_multiply.o vendor/cs/cs_norm.o vendor/cs/cs_permute.o vendor/cs/cs_pinv.o vendor/cs/cs_post.o vendor/cs/cs_print.o vendor/cs/cs_pvec.o vendor/cs/cs_qr.o vendor/cs/cs_qrsol.o vendor/cs/cs_randperm.o vendor/cs/cs_reach.o vendor/cs/cs_scatter.o vendor/cs/cs_scc.o vendor/cs/cs_schol.o vendor/cs/cs_spsolve.o vendor/cs/cs_sqr.o vendor/cs/cs_symperm.o vendor/cs/cs_tdfs.o vendor/cs/cs_transpose.o vendor/cs/cs_updown.o vendor/cs/cs_usolve.o vendor/cs/cs_util.o vendor/cs/cs_utsolve.o vendor/mini-gmp/mini-gmp.o vendor/plfit/gss.o vendor/plfit/hzeta.o vendor/plfit/kolmogorov.o vendor/plfit/lbfgs.o vendor/plfit/mt.o vendor/plfit/options.o vendor/plfit/platform.o vendor/plfit/plfit.o vendor/plfit/plfit_error.o vendor/plfit/rbinom.o vendor/plfit/sampling.o vendor/arpack/dgetv0.o vendor/arpack/dlaqrb.o vendor/arpack/dmout.o vendor/arpack/dnaitr.o vendor/arpack/dnapps.o vendor/arpack/dnaup2.o vendor/arpack/dnaupd.o vendor/arpack/dnconv.o vendor/arpack/dneigh.o vendor/arpack/dneupd.o vendor/arpack/dngets.o vendor/arpack/dsaitr.o vendor/arpack/dsapps.o vendor/arpack/dsaup2.o vendor/arpack/dsaupd.o vendor/arpack/dsconv.o vendor/arpack/dseigt.o vendor/arpack/dsesrt.o vendor/arpack/dseupd.o vendor/arpack/dsgets.o vendor/arpack/dsortc.o vendor/arpack/dsortr.o vendor/arpack/dstatn.o vendor/arpack/dstats.o vendor/arpack/dstqrb.o vendor/arpack/dvout.o vendor/arpack/ivout.o vendor/arpack/second.o vendor/arpack/wrap.o vendor/simpleraytracer/Color.o vendor/simpleraytracer/Light.o vendor/simpleraytracer/Point.o vendor/simpleraytracer/RIgraphRay.o vendor/simpleraytracer/Ray.o vendor/simpleraytracer/RayTracer.o vendor/simpleraytracer/RayVector.o vendor/simpleraytracer/Shape.o vendor/simpleraytracer/Sphere.o vendor/simpleraytracer/Triangle.o vendor/simpleraytracer/unit_limiter.o vendor/uuid/R.o vendor/uuid/clear.o vendor/uuid/compare.o vendor/uuid/copy.o vendor/uuid/gen_uuid.o vendor/uuid/isnull.o vendor/uuid/pack.o vendor/uuid/parse.o vendor/uuid/unpack.o vendor/uuid/unparse.o rinterface.o rinterface_extra.o rrandom.o lazyeval.o +OBJECTS=core/centrality/betweenness.o core/centrality/centrality_other.o core/centrality/centralization.o core/centrality/closeness.o core/centrality/coreness.o core/centrality/prpack/prpack_base_graph.o core/centrality/prpack/prpack_igraph_graph.o core/centrality/prpack/prpack_preprocessed_ge_graph.o core/centrality/prpack/prpack_preprocessed_gs_graph.o core/centrality/prpack/prpack_preprocessed_scc_graph.o core/centrality/prpack/prpack_preprocessed_schur_graph.o core/centrality/prpack/prpack_result.o core/centrality/prpack/prpack_solver.o core/centrality/prpack/prpack_utils.o core/cliques/cliquer/cliquer.o core/cliques/cliquer/cliquer_graph.o core/cliques/cliquer/reorder.o core/cliques/cliquer_wrapper.o core/cliques/cliques.o core/cliques/glet.o core/cliques/maximal_cliques.o core/community/community_misc.o core/community/edge_betweenness.o core/community/fast_modularity.o core/community/fluid.o core/community/infomap/infomap.o core/community/infomap/infomap_FlowGraph.o core/community/infomap/infomap_Greedy.o core/community/infomap/infomap_Node.o core/community/label_propagation.o core/community/leading_eigenvector.o core/community/leiden.o core/community/louvain.o core/community/modularity.o core/community/optimal_modularity.o core/community/spinglass/NetDataTypes.o core/community/spinglass/NetRoutines.o core/community/spinglass/clustertool.o core/community/spinglass/pottsmodel_2.o core/community/walktrap/walktrap.o core/community/walktrap/walktrap_communities.o core/community/walktrap/walktrap_graph.o core/community/walktrap/walktrap_heap.o core/connectivity/cohesive_blocks.o core/connectivity/components.o core/connectivity/separators.o core/constructors/adjacency.o core/constructors/atlas.o core/constructors/basic_constructors.o core/constructors/de_bruijn.o core/constructors/famous.o core/constructors/full.o core/constructors/kautz.o core/constructors/lcf.o core/constructors/linegraph.o core/constructors/prufer.o core/constructors/regular.o core/core/array.o core/core/buckets.o core/core/cutheap.o core/core/dqueue.o core/core/error.o core/core/estack.o core/core/fixed_vectorlist.o core/core/grid.o core/core/heap.o core/core/indheap.o core/core/interruption.o core/core/marked_queue.o core/core/matrix.o core/core/memory.o core/core/printing.o core/core/progress.o core/core/psumtree.o core/core/set.o core/core/sparsemat.o core/core/spmatrix.o core/core/stack.o core/core/statusbar.o core/core/strvector.o core/core/trie.o core/core/vector.o core/core/vector_ptr.o core/flow/flow.o core/flow/st-cuts.o core/games/barabasi.o core/games/callaway_traits.o core/games/citations.o core/games/correlated.o core/games/degree_sequence.o core/games/degree_sequence_vl/gengraph_box_list.o core/games/degree_sequence_vl/gengraph_degree_sequence.o core/games/degree_sequence_vl/gengraph_graph_molloy_hash.o core/games/degree_sequence_vl/gengraph_graph_molloy_optimized.o core/games/degree_sequence_vl/gengraph_mr-connected.o core/games/degree_sequence_vl/gengraph_powerlaw.o core/games/degree_sequence_vl/gengraph_random.o core/games/dotproduct.o core/games/erdos_renyi.o core/games/establishment.o core/games/forestfire.o core/games/grg.o core/games/growing_random.o core/games/islands.o core/games/k_regular.o core/games/preference.o core/games/recent_degree.o core/games/sbm.o core/games/static_fitness.o core/games/tree.o core/games/watts_strogatz.o core/graph/adjlist.o core/graph/attributes.o core/graph/basic_query.o core/graph/cattributes.o core/graph/iterators.o core/graph/type_indexededgelist.o core/graph/visitors.o core/hrg/hrg.o core/hrg/hrg_types.o core/internal/glpk_support.o core/internal/hacks.o core/internal/lsap.o core/internal/qsort.o core/internal/qsort_r.o core/internal/zeroin.o core/io/dimacs.o core/io/dl-lexer.o core/io/dl-parser.o core/io/dl.o core/io/dot.o core/io/edgelist.o core/io/gml-lexer.o core/io/gml-parser.o core/io/gml-tree.o core/io/gml.o core/io/graphdb.o core/io/graphml.o core/io/leda.o core/io/lgl-lexer.o core/io/lgl-parser.o core/io/lgl.o core/io/ncol-lexer.o core/io/ncol-parser.o core/io/ncol.o core/io/pajek-lexer.o core/io/pajek-parser.o core/io/pajek.o core/isomorphism/bliss.o core/isomorphism/bliss/defs.o core/isomorphism/bliss/graph.o core/isomorphism/bliss/heap.o core/isomorphism/bliss/orbit.o core/isomorphism/bliss/partition.o core/isomorphism/bliss/uintseqhash.o core/isomorphism/bliss/utils.o core/isomorphism/isoclasses.o core/isomorphism/isomorphism_misc.o core/isomorphism/lad.o core/isomorphism/queries.o core/isomorphism/vf2.o core/layout/circular.o core/layout/davidson_harel.o core/layout/drl/DensityGrid.o core/layout/drl/DensityGrid_3d.o core/layout/drl/drl_graph.o core/layout/drl/drl_graph_3d.o core/layout/drl/drl_layout.o core/layout/drl/drl_layout_3d.o core/layout/drl/drl_parse.o core/layout/fruchterman_reingold.o core/layout/gem.o core/layout/graphopt.o core/layout/kamada_kawai.o core/layout/large_graph.o core/layout/layout_bipartite.o core/layout/layout_grid.o core/layout/layout_random.o core/layout/mds.o core/layout/merge_dla.o core/layout/merge_grid.o core/layout/reingold_tilford.o core/layout/sugiyama.o core/linalg/arpack.o core/linalg/blas.o core/linalg/eigen.o core/linalg/lapack.o core/math/bfgs.o core/math/complex.o core/math/utils.o core/misc/bipartite.o core/misc/chordality.o core/misc/cocitation.o core/misc/coloring.o core/misc/conversion.o core/misc/degree_sequence.o core/misc/embedding.o core/misc/feedback_arc_set.o core/misc/graphicality.o core/misc/matching.o core/misc/microscopic_update.o core/misc/mixing.o core/misc/motifs.o core/misc/other.o core/misc/scan.o core/misc/sir.o core/misc/spanning_trees.o core/operators/add_edge.o core/operators/complementer.o core/operators/compose.o core/operators/connect_neighborhood.o core/operators/contract.o core/operators/difference.o core/operators/disjoint_union.o core/operators/intersection.o core/operators/misc_internal.o core/operators/permute.o core/operators/rewire.o core/operators/rewire_edges.o core/operators/simplify.o core/operators/subgraph.o core/operators/union.o core/paths/all_shortest_paths.o core/paths/bellman_ford.o core/paths/dijkstra.o core/paths/distances.o core/paths/eulerian.o core/paths/histogram.o core/paths/johnson.o core/paths/random_walk.o core/paths/shortest_paths.o core/paths/simple_paths.o core/paths/unweighted.o core/properties/basic_properties.o core/properties/constraint.o core/properties/convergence_degree.o core/properties/dag.o core/properties/degrees.o core/properties/girth.o core/properties/loops.o core/properties/multiplicity.o core/properties/neighborhood.o core/properties/spectral.o core/properties/trees.o core/properties/triangles.o core/random/random.o core/scg/scg.o core/scg/scg_approximate_methods.o core/scg/scg_exact_scg.o core/scg/scg_kmeans.o core/scg/scg_optimal_method.o core/scg/scg_utils.o core/version.o vendor/cs/cs_add.o vendor/cs/cs_amd.o vendor/cs/cs_chol.o vendor/cs/cs_cholsol.o vendor/cs/cs_compress.o vendor/cs/cs_counts.o vendor/cs/cs_cumsum.o vendor/cs/cs_dfs.o vendor/cs/cs_dmperm.o vendor/cs/cs_droptol.o vendor/cs/cs_dropzeros.o vendor/cs/cs_dupl.o vendor/cs/cs_entry.o vendor/cs/cs_ereach.o vendor/cs/cs_etree.o vendor/cs/cs_fkeep.o vendor/cs/cs_gaxpy.o vendor/cs/cs_happly.o vendor/cs/cs_house.o vendor/cs/cs_ipvec.o vendor/cs/cs_leaf.o vendor/cs/cs_load.o vendor/cs/cs_lsolve.o vendor/cs/cs_ltsolve.o vendor/cs/cs_lu.o vendor/cs/cs_lusol.o vendor/cs/cs_malloc.o vendor/cs/cs_maxtrans.o vendor/cs/cs_multiply.o vendor/cs/cs_norm.o vendor/cs/cs_permute.o vendor/cs/cs_pinv.o vendor/cs/cs_post.o vendor/cs/cs_print.o vendor/cs/cs_pvec.o vendor/cs/cs_qr.o vendor/cs/cs_qrsol.o vendor/cs/cs_randperm.o vendor/cs/cs_reach.o vendor/cs/cs_scatter.o vendor/cs/cs_scc.o vendor/cs/cs_schol.o vendor/cs/cs_spsolve.o vendor/cs/cs_sqr.o vendor/cs/cs_symperm.o vendor/cs/cs_tdfs.o vendor/cs/cs_transpose.o vendor/cs/cs_updown.o vendor/cs/cs_usolve.o vendor/cs/cs_util.o vendor/cs/cs_utsolve.o vendor/mini-gmp/mini-gmp.o vendor/plfit/gss.o vendor/plfit/hzeta.o vendor/plfit/kolmogorov.o vendor/plfit/lbfgs.o vendor/plfit/mt.o vendor/plfit/options.o vendor/plfit/platform.o vendor/plfit/plfit.o vendor/plfit/plfit_error.o vendor/plfit/rbinom.o vendor/plfit/sampling.o vendor/arpack/dgetv0.o vendor/arpack/dlaqrb.o vendor/arpack/dmout.o vendor/arpack/dnaitr.o vendor/arpack/dnapps.o vendor/arpack/dnaup2.o vendor/arpack/dnaupd.o vendor/arpack/dnconv.o vendor/arpack/dneigh.o vendor/arpack/dneupd.o vendor/arpack/dngets.o vendor/arpack/dsaitr.o vendor/arpack/dsapps.o vendor/arpack/dsaup2.o vendor/arpack/dsaupd.o vendor/arpack/dsconv.o vendor/arpack/dseigt.o vendor/arpack/dsesrt.o vendor/arpack/dseupd.o vendor/arpack/dsgets.o vendor/arpack/dsortc.o vendor/arpack/dsortr.o vendor/arpack/dstatn.o vendor/arpack/dstats.o vendor/arpack/dstqrb.o vendor/arpack/dvout.o vendor/arpack/ivout.o vendor/arpack/second.o vendor/arpack/wrap.o vendor/simpleraytracer/Color.o vendor/simpleraytracer/Light.o vendor/simpleraytracer/Point.o vendor/simpleraytracer/RIgraphRay.o vendor/simpleraytracer/Ray.o vendor/simpleraytracer/RayTracer.o vendor/simpleraytracer/RayVector.o vendor/simpleraytracer/Shape.o vendor/simpleraytracer/Sphere.o vendor/simpleraytracer/Triangle.o vendor/simpleraytracer/unit_limiter.o vendor/uuid/R.o vendor/uuid/clear.o vendor/uuid/compare.o vendor/uuid/copy.o vendor/uuid/gen_uuid.o vendor/uuid/isnull.o vendor/uuid/pack.o vendor/uuid/parse.o vendor/uuid/unpack.o vendor/uuid/unparse.o rinterface.o rinterface_extra.o rrandom.o lazyeval.o LIB=../librigraph.a lib: $(LIB) -$(LIB): core/centrality/betweenness.o core/centrality/centrality_other.o +## ignoring: core/centrality/prpack.o + +$(LIB): core/centrality/betweenness.o core/centrality/centrality_other.o core/centrality/centralization.o core/centrality/closeness.o core/centrality/coreness.o core/centrality/prpack/prpack_base_graph.o core/centrality/prpack/prpack_igraph_graph.o core/centrality/prpack/prpack_preprocessed_ge_graph.o core/centrality/prpack/prpack_preprocessed_gs_graph.o core/centrality/prpack/prpack_preprocessed_scc_graph.o core/centrality/prpack/prpack_preprocessed_schur_graph.o core/centrality/prpack/prpack_result.o core/centrality/prpack/prpack_solver.o core/centrality/prpack/prpack_utils.o core/cliques/cliquer/cliquer.o core/cliques/cliquer/cliquer_graph.o core/cliques/cliquer/reorder.o core/cliques/cliquer_wrapper.o core/cliques/cliques.o core/cliques/glet.o core/cliques/maximal_cliques.o core/community/community_misc.o core/community/edge_betweenness.o core/community/fast_modularity.o core/community/fluid.o core/community/infomap/infomap.o core/community/infomap/infomap_FlowGraph.o core/community/infomap/infomap_Greedy.o core/community/infomap/infomap_Node.o core/community/label_propagation.o core/community/leading_eigenvector.o core/community/leiden.o core/community/louvain.o core/community/modularity.o core/community/optimal_modularity.o core/community/spinglass/NetDataTypes.o core/community/spinglass/NetRoutines.o core/community/spinglass/clustertool.o core/community/spinglass/pottsmodel_2.o core/community/walktrap/walktrap.o core/community/walktrap/walktrap_communities.o core/community/walktrap/walktrap_graph.o core/community/walktrap/walktrap_heap.o core/connectivity/cohesive_blocks.o core/connectivity/components.o core/connectivity/separators.o core/constructors/adjacency.o core/constructors/atlas.o core/constructors/basic_constructors.o core/constructors/de_bruijn.o core/constructors/famous.o core/constructors/full.o core/constructors/kautz.o core/constructors/lcf.o core/constructors/linegraph.o core/constructors/prufer.o core/constructors/regular.o core/core/array.o core/core/buckets.o core/core/cutheap.o core/core/dqueue.o core/core/error.o core/core/estack.o core/core/fixed_vectorlist.o core/core/grid.o core/core/heap.o core/core/indheap.o core/core/interruption.o core/core/marked_queue.o core/core/matrix.o core/core/memory.o core/core/printing.o core/core/progress.o core/core/psumtree.o core/core/set.o core/core/sparsemat.o core/core/spmatrix.o core/core/stack.o core/core/statusbar.o core/core/strvector.o core/core/trie.o core/core/vector.o core/core/vector_ptr.o core/flow/flow.o core/flow/st-cuts.o core/games/barabasi.o core/games/callaway_traits.o core/games/citations.o core/games/correlated.o core/games/degree_sequence.o core/games/degree_sequence_vl/gengraph_box_list.o core/games/degree_sequence_vl/gengraph_degree_sequence.o core/games/degree_sequence_vl/gengraph_graph_molloy_hash.o core/games/degree_sequence_vl/gengraph_graph_molloy_optimized.o core/games/degree_sequence_vl/gengraph_mr-connected.o core/games/degree_sequence_vl/gengraph_powerlaw.o core/games/degree_sequence_vl/gengraph_random.o core/games/dotproduct.o core/games/erdos_renyi.o core/games/establishment.o core/games/forestfire.o core/games/grg.o core/games/growing_random.o core/games/islands.o core/games/k_regular.o core/games/preference.o core/games/recent_degree.o core/games/sbm.o core/games/static_fitness.o core/games/tree.o core/games/watts_strogatz.o core/graph/adjlist.o core/graph/attributes.o core/graph/basic_query.o core/graph/cattributes.o core/graph/iterators.o core/graph/type_indexededgelist.o core/graph/visitors.o core/hrg/hrg.o core/hrg/hrg_types.o core/internal/glpk_support.o core/internal/hacks.o core/internal/lsap.o core/internal/qsort.o core/internal/qsort_r.o core/internal/zeroin.o core/io/dimacs.o core/io/dl-lexer.o core/io/dl-parser.o core/io/dl.o core/io/dot.o core/io/edgelist.o core/io/gml-lexer.o core/io/gml-parser.o core/io/gml-tree.o core/io/gml.o core/io/graphdb.o core/io/graphml.o core/io/leda.o core/io/lgl-lexer.o core/io/lgl-parser.o core/io/lgl.o core/io/ncol-lexer.o core/io/ncol-parser.o core/io/ncol.o core/io/pajek-lexer.o core/io/pajek-parser.o core/io/pajek.o core/isomorphism/bliss.o core/isomorphism/bliss/defs.o core/isomorphism/bliss/graph.o core/isomorphism/bliss/heap.o core/isomorphism/bliss/orbit.o core/isomorphism/bliss/partition.o core/isomorphism/bliss/uintseqhash.o core/isomorphism/bliss/utils.o core/isomorphism/isoclasses.o core/isomorphism/isomorphism_misc.o core/isomorphism/lad.o core/isomorphism/queries.o core/isomorphism/vf2.o core/layout/circular.o core/layout/davidson_harel.o core/layout/drl/DensityGrid.o core/layout/drl/DensityGrid_3d.o core/layout/drl/drl_graph.o core/layout/drl/drl_graph_3d.o core/layout/drl/drl_layout.o core/layout/drl/drl_layout_3d.o core/layout/drl/drl_parse.o core/layout/fruchterman_reingold.o core/layout/gem.o core/layout/graphopt.o core/layout/kamada_kawai.o core/layout/large_graph.o core/layout/layout_bipartite.o core/layout/layout_grid.o core/layout/layout_random.o core/layout/mds.o core/layout/merge_dla.o core/layout/merge_grid.o core/layout/reingold_tilford.o core/layout/sugiyama.o core/linalg/arpack.o core/linalg/blas.o core/linalg/eigen.o core/linalg/lapack.o core/math/bfgs.o core/math/complex.o core/math/utils.o core/misc/bipartite.o core/misc/chordality.o core/misc/cocitation.o core/misc/coloring.o core/misc/conversion.o core/misc/degree_sequence.o core/misc/embedding.o core/misc/feedback_arc_set.o core/misc/graphicality.o core/misc/matching.o core/misc/microscopic_update.o core/misc/mixing.o core/misc/motifs.o core/misc/other.o core/misc/scan.o core/misc/sir.o core/misc/spanning_trees.o core/operators/add_edge.o core/operators/complementer.o core/operators/compose.o core/operators/connect_neighborhood.o core/operators/contract.o core/operators/difference.o core/operators/disjoint_union.o core/operators/intersection.o core/operators/misc_internal.o core/operators/permute.o core/operators/rewire.o core/operators/rewire_edges.o core/operators/simplify.o core/operators/subgraph.o core/operators/union.o core/paths/all_shortest_paths.o core/paths/bellman_ford.o core/paths/dijkstra.o core/paths/distances.o core/paths/eulerian.o core/paths/histogram.o core/paths/johnson.o core/paths/random_walk.o core/paths/shortest_paths.o core/paths/simple_paths.o core/paths/unweighted.o core/properties/basic_properties.o core/properties/constraint.o core/properties/convergence_degree.o core/properties/dag.o core/properties/degrees.o core/properties/girth.o core/properties/loops.o core/properties/multiplicity.o core/properties/neighborhood.o core/properties/spectral.o core/properties/trees.o core/properties/triangles.o core/random/random.o core/scg/scg.o core/scg/scg_approximate_methods.o core/scg/scg_exact_scg.o core/scg/scg_kmeans.o core/scg/scg_optimal_method.o core/scg/scg_utils.o core/version.o vendor/cs/cs_add.o vendor/cs/cs_amd.o vendor/cs/cs_chol.o vendor/cs/cs_cholsol.o vendor/cs/cs_compress.o vendor/cs/cs_counts.o vendor/cs/cs_cumsum.o vendor/cs/cs_dfs.o vendor/cs/cs_dmperm.o vendor/cs/cs_droptol.o vendor/cs/cs_dropzeros.o vendor/cs/cs_dupl.o vendor/cs/cs_entry.o vendor/cs/cs_ereach.o vendor/cs/cs_etree.o vendor/cs/cs_fkeep.o vendor/cs/cs_gaxpy.o vendor/cs/cs_happly.o vendor/cs/cs_house.o vendor/cs/cs_ipvec.o vendor/cs/cs_leaf.o vendor/cs/cs_load.o vendor/cs/cs_lsolve.o vendor/cs/cs_ltsolve.o vendor/cs/cs_lu.o vendor/cs/cs_lusol.o vendor/cs/cs_malloc.o vendor/cs/cs_maxtrans.o vendor/cs/cs_multiply.o vendor/cs/cs_norm.o vendor/cs/cs_permute.o vendor/cs/cs_pinv.o vendor/cs/cs_post.o vendor/cs/cs_print.o vendor/cs/cs_pvec.o vendor/cs/cs_qr.o vendor/cs/cs_qrsol.o vendor/cs/cs_randperm.o vendor/cs/cs_reach.o vendor/cs/cs_scatter.o vendor/cs/cs_scc.o vendor/cs/cs_schol.o vendor/cs/cs_spsolve.o vendor/cs/cs_sqr.o vendor/cs/cs_symperm.o vendor/cs/cs_tdfs.o vendor/cs/cs_transpose.o vendor/cs/cs_updown.o vendor/cs/cs_usolve.o vendor/cs/cs_util.o vendor/cs/cs_utsolve.o vendor/mini-gmp/mini-gmp.o vendor/plfit/gss.o vendor/plfit/hzeta.o vendor/plfit/kolmogorov.o vendor/plfit/lbfgs.o vendor/plfit/mt.o vendor/plfit/options.o vendor/plfit/platform.o vendor/plfit/plfit.o vendor/plfit/plfit_error.o vendor/plfit/rbinom.o vendor/plfit/sampling.o vendor/arpack/dgetv0.o vendor/arpack/dlaqrb.o vendor/arpack/dmout.o vendor/arpack/dnaitr.o vendor/arpack/dnapps.o vendor/arpack/dnaup2.o vendor/arpack/dnaupd.o vendor/arpack/dnconv.o vendor/arpack/dneigh.o vendor/arpack/dneupd.o vendor/arpack/dngets.o vendor/arpack/dsaitr.o vendor/arpack/dsapps.o vendor/arpack/dsaup2.o vendor/arpack/dsaupd.o vendor/arpack/dsconv.o vendor/arpack/dseigt.o vendor/arpack/dsesrt.o vendor/arpack/dseupd.o vendor/arpack/dsgets.o vendor/arpack/dsortc.o vendor/arpack/dsortr.o vendor/arpack/dstatn.o vendor/arpack/dstats.o vendor/arpack/dstqrb.o vendor/arpack/dvout.o vendor/arpack/ivout.o vendor/arpack/second.o vendor/arpack/wrap.o vendor/simpleraytracer/Color.o vendor/simpleraytracer/Light.o vendor/simpleraytracer/Point.o vendor/simpleraytracer/RIgraphRay.o vendor/simpleraytracer/Ray.o vendor/simpleraytracer/RayTracer.o vendor/simpleraytracer/RayVector.o vendor/simpleraytracer/Shape.o vendor/simpleraytracer/Sphere.o vendor/simpleraytracer/Triangle.o vendor/simpleraytracer/unit_limiter.o vendor/uuid/R.o vendor/uuid/clear.o vendor/uuid/compare.o vendor/uuid/copy.o vendor/uuid/gen_uuid.o vendor/uuid/isnull.o vendor/uuid/pack.o vendor/uuid/parse.o vendor/uuid/unpack.o vendor/uuid/unparse.o rinterface.o rinterface_extra.o rrandom.o lazyeval.o ar rvs $@ $? clean: @-rm -f *.o $(LIB) + + diff --git a/src/rigraph/core/centrality/betweenness.c b/src/rigraph/core/centrality/betweenness.c index af9e9d8..1dde4a5 100644 --- a/src/rigraph/core/centrality/betweenness.c +++ b/src/rigraph/core/centrality/betweenness.c @@ -27,6 +27,8 @@ #include "igraph_stack.h" #include "igraph_dqueue.h" +#include "config.h" + #include "../core/indheap.h" #include "../core/interruption.h" #include "../core/math.h" diff --git a/src/rigraph/core/centrality/centrality_other.c b/src/rigraph/core/centrality/centrality_other.c index a31ac8a..3c4c2ee 100644 --- a/src/rigraph/core/centrality/centrality_other.c +++ b/src/rigraph/core/centrality/centrality_other.c @@ -30,10 +30,10 @@ #include "igraph_stack.h" #include "igraph_dqueue.h" -#include "centrality/prpack_internal.h" -#include "core/indheap.h" -#include "core/interruption.h" -#include "core/math.h" +#include "../centrality/prpack_internal.h" +#include "../core/indheap.h" +#include "../core/interruption.h" +#include "../core/math.h" #include "config.h" diff --git a/src/rigraph/core/centrality/centralization.c b/src/rigraph/core/centrality/centralization.c index 308e9fd..9516c12 100644 --- a/src/rigraph/core/centrality/centralization.c +++ b/src/rigraph/core/centrality/centralization.c @@ -23,7 +23,7 @@ #include "igraph_interface.h" #include "igraph_vector.h" -#include "core/math.h" +#include "../core/math.h" /** * \function igraph_centralization diff --git a/src/rigraph/core/centrality/closeness.c b/src/rigraph/core/centrality/closeness.c index 6435321..68ecb7e 100644 --- a/src/rigraph/core/centrality/closeness.c +++ b/src/rigraph/core/centrality/closeness.c @@ -25,9 +25,9 @@ #include "igraph_progress.h" #include "igraph_dqueue.h" -#include "core/indheap.h" -#include "core/interruption.h" -#include "core/math.h" +#include "../core/indheap.h" +#include "../core/interruption.h" +#include "../core/math.h" /***** Closeness centrality *****/ diff --git a/src/rigraph/core/centrality/prpack.cpp b/src/rigraph/core/centrality/prpack.cpp index ca109ca..bd70178 100644 --- a/src/rigraph/core/centrality/prpack.cpp +++ b/src/rigraph/core/centrality/prpack.cpp @@ -19,10 +19,16 @@ #include "igraph_error.h" -#include "centrality/prpack_internal.h" -#include "centrality/prpack/prpack_igraph_graph.h" -#include "centrality/prpack/prpack_solver.h" -#include "core/exceptions.h" +//#include "centrality/prpack_internal.h" +//#include "centrality/prpack/prpack_igraph_graph.h" +//#include "centrality/prpack/prpack_solver.h" +// #include "core/exceptions.h" + +#include "prpack_internal.h" +#include "prpack/prpack_igraph_graph.h" +#include "prpack/prpack_solver.h" +#include "../core/exceptions.h" + using namespace prpack; using namespace std; @@ -121,4 +127,4 @@ int igraph_i_personalized_pagerank_prpack(const igraph_t *graph, igraph_vector_t delete res; return IGRAPH_SUCCESS; -} +} \ No newline at end of file diff --git a/src/rigraph/core/centrality/prpack/prpack_igraph_graph.cpp b/src/rigraph/core/centrality/prpack/prpack_igraph_graph.cpp index 6554936..0b78cff 100644 --- a/src/rigraph/core/centrality/prpack/prpack_igraph_graph.cpp +++ b/src/rigraph/core/centrality/prpack/prpack_igraph_graph.cpp @@ -4,7 +4,7 @@ #include "igraph_interface.h" -using namespace prpack; +//using namespace prpack; using namespace std; #ifdef PRPACK_IGRAPH_SUPPORT diff --git a/src/rigraph/core/cliques/cliquer/cliquer.c b/src/rigraph/core/cliques/cliquer/cliquer.c index 2dd08b9..ab9f861 100644 --- a/src/rigraph/core/cliques/cliquer/cliquer.c +++ b/src/rigraph/core/cliques/cliquer/cliquer.c @@ -20,7 +20,7 @@ #include "config.h" /* Default cliquer options */ -IGRAPH_THREAD_LOCAL clique_options clique_default_options = { +clique_options clique_default_options = { reorder_by_default, NULL, /*clique_print_time*/ NULL, NULL, NULL, NULL, NULL, 0 }; @@ -32,18 +32,18 @@ IGRAPH_THREAD_LOCAL clique_options clique_default_options = { /* 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 int *clique_size; /* c[i] == max. clique size in {0,1,...,i-1} */ +static set_t current_clique; /* Current clique being searched. */ +static 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 +static int clique_list_count=0; /* No. of cliques in opts->clique_list[] */ +static 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; +static int **temp_list=NULL; +static int temp_count=0; /* @@ -52,7 +52,7 @@ static IGRAPH_THREAD_LOCAL int temp_count=0; * 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? */ +static int entrance_level=0; /* How many levels for entrance have occurred? */ #define ENTRANCE_SAVE() \ int *old_clique_size = clique_size; \ diff --git a/src/rigraph/core/cliques/cliquer_wrapper.c b/src/rigraph/core/cliques/cliquer_wrapper.c index 7b4fa2c..ac4317b 100644 --- a/src/rigraph/core/cliques/cliquer_wrapper.c +++ b/src/rigraph/core/cliques/cliquer_wrapper.c @@ -3,9 +3,9 @@ #include "igraph_memory.h" #include "igraph_constants.h" -#include "core/interruption.h" -#include "cliques/cliquer_internal.h" -#include "cliques/cliquer/cliquer.h" +#include "../core/interruption.h" +#include "cliquer_internal.h" +#include "cliquer/cliquer.h" #include "config.h" @@ -29,7 +29,7 @@ /* Nonzero value signals interuption from Cliquer callback function */ -static IGRAPH_THREAD_LOCAL int cliquer_interrupted; +static int cliquer_interrupted; /* For use with IGRAPH_FINALLY */ @@ -43,7 +43,7 @@ static void free_clique_list(igraph_vector_ptr_t *vp) { } /* We shall use this option struct for all calls to Cliquer */ -static IGRAPH_THREAD_LOCAL clique_options igraph_cliquer_opt = { +static clique_options igraph_cliquer_opt = { reorder_by_default, NULL, NULL, NULL, NULL, NULL, NULL, 0 }; diff --git a/src/rigraph/core/core/interruption.h b/src/rigraph/core/core/interruption.h index 6afda32..b9bcc34 100644 --- a/src/rigraph/core/core/interruption.h +++ b/src/rigraph/core/core/interruption.h @@ -28,10 +28,10 @@ #include "igraph_interrupt.h" #include "config.h" + __BEGIN_DECLS -extern IGRAPH_THREAD_LOCAL igraph_interruption_handler_t -*igraph_i_interruption_handler; +extern igraph_interruption_handler_t *igraph_i_interruption_handler; /** * \define IGRAPH_ALLOW_INTERRUPTION diff --git a/src/rigraph/include/config.h b/src/rigraph/include/config.h new file mode 100644 index 0000000..6f4c67c --- /dev/null +++ b/src/rigraph/include/config.h @@ -0,0 +1,139 @@ + +/* Define to 1 if you have the `expm1' function. */ +#undef HAVE_EXPM1 + +/* Define to 1 if you have the `finite' function. */ +#undef HAVE_FINITE + +/* Define to 1 if you have the `fmin' function. */ +#undef HAVE_FMIN + +/* Define to 1 if using the GNU Fortran compiler */ +#undef HAVE_GFORTRAN + +/* Define to 1 if you have the GLPK library */ +#undef HAVE_GLPK + +/* Define to 1 if you have the GMP library */ +#undef HAVE_GMP + +/* Define to 1 if you have the header file. */ +#undef HAVE_INTTYPES_H + +/* Define to 1 if you have the `isfinite' function. */ +#undef HAVE_ISFINITE + +/* Define to 1 if you have the libxml2 libraries installed */ +#undef HAVE_LIBXML + +/* Define to 1 if you have the `log1p' function. */ +#undef HAVE_LOG1P + +/* Define to 1 if you have the `log2' function. */ +#undef HAVE_LOG2 + +/* Define to 1 if you have the header file. */ +#undef HAVE_MEMORY_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_NETINET_IN_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_NET_IF_DL_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_NET_IF_H + +/* Define to 1 if you have the `rint' function. */ +#undef HAVE_RINT + +/* Define to 1 if you have the `rintf' function. */ +#undef HAVE_RINTF + +/* Define to 1 if you have the `round' function. */ +#undef HAVE_ROUND + +/* Define if struct sockaddr contains sa_len */ +#undef HAVE_SA_LEN + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDINT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDLIB_H + +/* Define to 1 if you have the `stpcpy' function. */ +#undef HAVE_STPCPY + +/* Define to 1 if you have the `strcasecmp' function. */ +#undef HAVE_STRCASECMP + +/* Define to 1 if you have the `strdup' function. */ +#undef HAVE_STRDUP + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRINGS_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRING_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_FILE_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_IOCTL_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_SOCKET_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_SOCKIO_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_STAT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_TIME_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_TYPES_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_UN_H + +/* Define to 1 if you have the sys/times.h header */ +#undef HAVE_TIMES_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_UNISTD_H + +/* Define to 1 if you have the `_stricmp' function. */ +#undef HAVE__STRICMP + +/* We don't care about thread-local storage in R */ +#undef IGRAPH_THREAD_LOCAL + +/* Define to 1 if you use the vendored mini-GMP library */ +#undef INTERNAL_GMP + +/* Define to the address where bug reports for this package should be sent. */ +#undef PACKAGE_BUGREPORT + +/* Define to the full name of this package. */ +#undef PACKAGE_NAME + +/* Define to the full name and version of this package. */ +#undef PACKAGE_STRING + +/* Define to the one symbol short name of this package. */ +#undef PACKAGE_TARNAME + +/* Define to the home page for this package. */ +#undef PACKAGE_URL + +/* Define to the version of this package. */ +#undef PACKAGE_VERSION + +/* Define to 1 if you have the ANSI C header files. */ +#undef STDC_HEADERS +#define HAVE_ISFINITE HAVE_DECL_ISFINITE \ No newline at end of file From 60e67a1919ef4d0e17022fb7c308e650144efab1 Mon Sep 17 00:00:00 2001 From: evanbiederstedt Date: Thu, 7 Apr 2022 20:46:38 -0400 Subject: [PATCH 11/20] at /core now, compiling --- src/rigraph/core/cliques/cliques.c | 6 +++--- src/rigraph/core/cliques/maximal_cliques.c | 2 +- src/rigraph/core/community/community_misc.c | 4 ++-- src/rigraph/core/community/edge_betweenness.c | 4 ++-- src/rigraph/core/community/fast_modularity.c | 2 +- src/rigraph/core/community/infomap/infomap.cc | 2 +- .../core/community/leading_eigenvector.c | 2 +- src/rigraph/core/community/leiden.c | 2 +- src/rigraph/core/community/louvain.c | 2 +- .../core/community/optimal_modularity.c | 4 ++-- .../core/community/spinglass/clustertool.cpp | 6 +++--- .../core/community/spinglass/pottsmodel_2.cpp | 2 +- .../core/community/walktrap/walktrap.cpp | 4 ++-- .../walktrap/walktrap_communities.cpp | 16 ++++++++-------- .../community/walktrap/walktrap_communities.h | 18 +++++++++--------- .../core/connectivity/cohesive_blocks.c | 2 +- src/rigraph/core/connectivity/components.c | 4 ++-- src/rigraph/core/connectivity/separators.c | 2 +- src/rigraph/core/constructors/atlas.c | 2 +- src/rigraph/core/constructors/famous.c | 2 +- src/rigraph/core/constructors/linegraph.c | 2 +- src/rigraph/core/constructors/regular.c | 2 +- 22 files changed, 46 insertions(+), 46 deletions(-) diff --git a/src/rigraph/core/cliques/cliques.c b/src/rigraph/core/cliques/cliques.c index 23ce9ae..bbb950f 100644 --- a/src/rigraph/core/cliques/cliques.c +++ b/src/rigraph/core/cliques/cliques.c @@ -31,9 +31,9 @@ #include "igraph_progress.h" #include "igraph_stack.h" -#include "cliques/cliquer_internal.h" -#include "core/interruption.h" -#include "core/set.h" +#include "cliquer_internal.h" +#include "../core/interruption.h" +#include "../core/set.h" #include /* memset */ diff --git a/src/rigraph/core/cliques/maximal_cliques.c b/src/rigraph/core/cliques/maximal_cliques.c index fba4e38..e949a46 100644 --- a/src/rigraph/core/cliques/maximal_cliques.c +++ b/src/rigraph/core/cliques/maximal_cliques.c @@ -30,7 +30,7 @@ #include "igraph_memory.h" #include "igraph_progress.h" -#include "core/interruption.h" +#include "../core/interruption.h" #define CONCAT2x(a,b) a ## b #define CONCAT2(a,b) CONCAT2x(a,b) diff --git a/src/rigraph/core/community/community_misc.c b/src/rigraph/core/community/community_misc.c index 4f01b2c..b519a02 100644 --- a/src/rigraph/core/community/community_misc.c +++ b/src/rigraph/core/community/community_misc.c @@ -38,8 +38,8 @@ #include "igraph_centrality.h" #include "igraph_structural.h" -#include "core/indheap.h" -#include "core/interruption.h" +#include "../core/indheap.h" +#include "../core/interruption.h" #include "config.h" diff --git a/src/rigraph/core/community/edge_betweenness.c b/src/rigraph/core/community/edge_betweenness.c index fc3cc97..b1c8739 100644 --- a/src/rigraph/core/community/edge_betweenness.c +++ b/src/rigraph/core/community/edge_betweenness.c @@ -31,8 +31,8 @@ #include "igraph_progress.h" #include "igraph_stack.h" -#include "core/indheap.h" -#include "core/interruption.h" +#include "../core/indheap.h" +#include "../core/interruption.h" #include diff --git a/src/rigraph/core/community/fast_modularity.c b/src/rigraph/core/community/fast_modularity.c index bcf0c35..2f0e16d 100644 --- a/src/rigraph/core/community/fast_modularity.c +++ b/src/rigraph/core/community/fast_modularity.c @@ -30,7 +30,7 @@ #include "igraph_structural.h" #include "igraph_vector_ptr.h" -#include "core/interruption.h" +#include "../core/interruption.h" /* #define IGRAPH_FASTCOMM_DEBUG */ diff --git a/src/rigraph/core/community/infomap/infomap.cc b/src/rigraph/core/community/infomap/infomap.cc index b1132c4..35a7ed3 100644 --- a/src/rigraph/core/community/infomap/infomap.cc +++ b/src/rigraph/core/community/infomap/infomap.cc @@ -32,7 +32,7 @@ #include #include "igraph_interface.h" #include "igraph_community.h" -#include "core/interruption.h" +#include "../../core/interruption.h" #include "infomap_Node.h" diff --git a/src/rigraph/core/community/leading_eigenvector.c b/src/rigraph/core/community/leading_eigenvector.c index 65f512e..dd990af 100644 --- a/src/rigraph/core/community/leading_eigenvector.c +++ b/src/rigraph/core/community/leading_eigenvector.c @@ -32,7 +32,7 @@ #include "igraph_statusbar.h" #include "igraph_structural.h" -#include "core/interruption.h" +#include "../core/interruption.h" /** * \section about_leading_eigenvector_methods diff --git a/src/rigraph/core/community/leiden.c b/src/rigraph/core/community/leiden.c index d7dc4f6..ba9debc 100644 --- a/src/rigraph/core/community/leiden.c +++ b/src/rigraph/core/community/leiden.c @@ -33,7 +33,7 @@ #include "igraph_vector.h" #include "igraph_constructors.h" -#include "core/interruption.h" +#include "../core/interruption.h" /* Move nodes in order to improve the quality of a partition. * diff --git a/src/rigraph/core/community/louvain.c b/src/rigraph/core/community/louvain.c index 7fb59c5..e2feab8 100644 --- a/src/rigraph/core/community/louvain.c +++ b/src/rigraph/core/community/louvain.c @@ -29,7 +29,7 @@ #include "igraph_qsort.h" #include "igraph_random.h" -#include "core/interruption.h" +#include "../core/interruption.h" /* Structure storing a community */ typedef struct { diff --git a/src/rigraph/core/community/optimal_modularity.c b/src/rigraph/core/community/optimal_modularity.c index cdb9328..ad25199 100644 --- a/src/rigraph/core/community/optimal_modularity.c +++ b/src/rigraph/core/community/optimal_modularity.c @@ -28,8 +28,8 @@ #include "igraph_interface.h" #include "igraph_structural.h" -#include "core/interruption.h" -#include "internal/glpk_support.h" +#include "../core/interruption.h" +#include "../internal/glpk_support.h" #include "config.h" diff --git a/src/rigraph/core/community/spinglass/clustertool.cpp b/src/rigraph/core/community/spinglass/clustertool.cpp index 9829c1b..08b0cc3 100644 --- a/src/rigraph/core/community/spinglass/clustertool.cpp +++ b/src/rigraph/core/community/spinglass/clustertool.cpp @@ -48,11 +48,11 @@ #include "igraph_community.h" #include "igraph_error.h" #include "igraph_random.h" -#include "core/math.h" +#include "../../core/math.h" #include "igraph_interface.h" #include "igraph_components.h" -#include "core/interruption.h" -#include "core/exceptions.h" +#include "../../core/interruption.h" +#include "../../core/exceptions.h" static int igraph_i_community_spinglass_orig( const igraph_t *graph, diff --git a/src/rigraph/core/community/spinglass/pottsmodel_2.cpp b/src/rigraph/core/community/spinglass/pottsmodel_2.cpp index 749aa6f..d26e0e9 100644 --- a/src/rigraph/core/community/spinglass/pottsmodel_2.cpp +++ b/src/rigraph/core/community/spinglass/pottsmodel_2.cpp @@ -46,7 +46,7 @@ #include "NetRoutines.h" #include "igraph_random.h" -#include "core/interruption.h" +#include "../../core/interruption.h" #include #include diff --git a/src/rigraph/core/community/walktrap/walktrap.cpp b/src/rigraph/core/community/walktrap/walktrap.cpp index 12f71ea..a046266 100644 --- a/src/rigraph/core/community/walktrap/walktrap.cpp +++ b/src/rigraph/core/community/walktrap/walktrap.cpp @@ -60,8 +60,8 @@ #include "igraph_components.h" #include "igraph_interface.h" -#include "core/exceptions.h" -#include "core/interruption.h" +#include "../../core/exceptions.h" +#include "../../core/interruption.h" using namespace igraph::walktrap; diff --git a/src/rigraph/core/community/walktrap/walktrap_communities.cpp b/src/rigraph/core/community/walktrap/walktrap_communities.cpp index 846cb95..1f108f1 100644 --- a/src/rigraph/core/community/walktrap/walktrap_communities.cpp +++ b/src/rigraph/core/community/walktrap/walktrap_communities.cpp @@ -64,14 +64,14 @@ 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; +int Probabilities::length = 0; +Communities* Probabilities::C = 0; +float* Probabilities::tmp_vector1 = 0; +float* Probabilities::tmp_vector2 = 0; +int* Probabilities::id = 0; +int* Probabilities::vertices1 = 0; +int* Probabilities::vertices2 = 0; +int Probabilities::current_id = 0; Neighbor::Neighbor() { diff --git a/src/rigraph/core/community/walktrap/walktrap_communities.h b/src/rigraph/core/community/walktrap/walktrap_communities.h index 690d7aa..9b028ac 100644 --- a/src/rigraph/core/community/walktrap/walktrap_communities.h +++ b/src/rigraph/core/community/walktrap/walktrap_communities.h @@ -69,15 +69,15 @@ 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 + static float* tmp_vector1; // + static float* tmp_vector2; // + static int* id; // + static int* vertices1; // + static int* vertices2; // + static int current_id; // + + static Communities* C; // pointer to all the communities + static int length; // length of the random walks int size; // number of probabilities stored diff --git a/src/rigraph/core/connectivity/cohesive_blocks.c b/src/rigraph/core/connectivity/cohesive_blocks.c index 62811f0..d1f13f3 100644 --- a/src/rigraph/core/connectivity/cohesive_blocks.c +++ b/src/rigraph/core/connectivity/cohesive_blocks.c @@ -33,7 +33,7 @@ #include "igraph_statusbar.h" #include "igraph_structural.h" -#include "core/interruption.h" +#include "../core/interruption.h" static void igraph_i_cohesive_blocks_free_graphs(igraph_vector_ptr_t *ptr) { long int i, n = igraph_vector_ptr_size(ptr); diff --git a/src/rigraph/core/connectivity/components.c b/src/rigraph/core/connectivity/components.c index 1f04d6b..a2e923a 100644 --- a/src/rigraph/core/connectivity/components.c +++ b/src/rigraph/core/connectivity/components.c @@ -33,8 +33,8 @@ #include "igraph_structural.h" #include "igraph_vector.h" -#include "core/interruption.h" -#include "operators/subgraph.h" +#include "../core/interruption.h" +#include "../operators/subgraph.h" #include diff --git a/src/rigraph/core/connectivity/separators.c b/src/rigraph/core/connectivity/separators.c index bdc7a24..1857a76 100644 --- a/src/rigraph/core/connectivity/separators.c +++ b/src/rigraph/core/connectivity/separators.c @@ -33,7 +33,7 @@ #include "igraph_structural.h" #include "igraph_vector.h" -#include "core/interruption.h" +#include "../core/interruption.h" static int igraph_i_is_separator(const igraph_t *graph, igraph_vit_t *vit, diff --git a/src/rigraph/core/constructors/atlas.c b/src/rigraph/core/constructors/atlas.c index ffa2eae..4b73faa 100644 --- a/src/rigraph/core/constructors/atlas.c +++ b/src/rigraph/core/constructors/atlas.c @@ -23,7 +23,7 @@ #include "igraph_constructors.h" -#include "constructors/atlas-edges.h" +#include "atlas-edges.h" /** * \function igraph_atlas diff --git a/src/rigraph/core/constructors/famous.c b/src/rigraph/core/constructors/famous.c index 101878d..0cf040e 100644 --- a/src/rigraph/core/constructors/famous.c +++ b/src/rigraph/core/constructors/famous.c @@ -24,7 +24,7 @@ #include "igraph_constructors.h" -#include "internal/hacks.h" +//#include "../internal/hacks.h" const igraph_real_t igraph_i_famous_bull[] = { 5, 5, 0, diff --git a/src/rigraph/core/constructors/linegraph.c b/src/rigraph/core/constructors/linegraph.c index 13f69e0..5387540 100644 --- a/src/rigraph/core/constructors/linegraph.c +++ b/src/rigraph/core/constructors/linegraph.c @@ -24,7 +24,7 @@ #include "igraph_interface.h" -#include "core/interruption.h" +#include "../core/interruption.h" /* Note to self: tried using adjacency lists instead of igraph_incident queries, * with minimal performance improvements on a graph with 70K vertices and 360K diff --git a/src/rigraph/core/constructors/regular.c b/src/rigraph/core/constructors/regular.c index ec87710..89d763c 100644 --- a/src/rigraph/core/constructors/regular.c +++ b/src/rigraph/core/constructors/regular.c @@ -26,7 +26,7 @@ #include "igraph_memory.h" #include "igraph_operators.h" -#include "core/interruption.h" +#include "../core/interruption.h" /** * \ingroup generators From a4f473a90964f261e912347ca2b49e2823d4aade Mon Sep 17 00:00:00 2001 From: evanbiederstedt Date: Thu, 7 Apr 2022 23:42:48 -0400 Subject: [PATCH 12/20] rigraph compiles --- src/rigraph/Makefile | 3 +- src/rigraph/core/core/buckets.c | 2 +- src/rigraph/core/core/cutheap.c | 2 +- src/rigraph/core/core/error.c | 14 +- src/rigraph/core/core/estack.c | 2 +- src/rigraph/core/core/fixed_vectorlist.c | 2 +- src/rigraph/core/core/grid.c | 2 +- src/rigraph/core/core/indheap.c | 2 +- src/rigraph/core/core/interruption.c | 2 +- src/rigraph/core/core/marked_queue.c | 2 +- src/rigraph/core/core/progress.c | 4 +- src/rigraph/core/core/set.c | 2 +- src/rigraph/core/core/sparsemat.c | 2 +- src/rigraph/core/core/statusbar.c | 2 +- src/rigraph/core/core/trie.c | 2 +- src/rigraph/core/core/vector.c | 4 +- src/rigraph/core/flow/flow.c | 8 +- src/rigraph/core/flow/st-cuts.c | 10 +- src/rigraph/core/games/barabasi.c | 2 +- src/rigraph/core/games/correlated.c | 2 +- src/rigraph/core/games/degree_sequence.c | 4 +- .../degree_sequence_vl/gengraph_definitions.h | 2 +- .../gengraph_graph_molloy_hash.cpp | 2 +- .../gengraph_mr-connected.cpp | 2 +- src/rigraph/core/games/forestfire.c | 2 +- src/rigraph/core/games/grg.c | 2 +- src/rigraph/core/games/preference.c | 2 +- src/rigraph/core/games/sbm.c | 2 +- src/rigraph/core/games/static_fitness.c | 2 +- src/rigraph/core/graph/adjlist.c | 2 +- src/rigraph/core/graph/attributes.c | 2 +- src/rigraph/core/graph/cattributes.c | 2 +- src/rigraph/core/graph/type_indexededgelist.c | 4 +- src/rigraph/core/hrg/dendro.h | 6 +- src/rigraph/core/hrg/graph.h | 2 +- src/rigraph/core/hrg/graph_simp.h | 4 +- src/rigraph/core/hrg/hrg.cc | 6 +- src/rigraph/core/hrg/hrg_types.cc | 10 +- src/rigraph/core/internal/glpk_support.c | 4 +- src/rigraph/core/internal/hacks.c | 2 +- src/rigraph/core/internal/zeroin.c | 2 +- src/rigraph/core/io/dimacs.c | 2 +- src/rigraph/core/io/dl-header.h | 2 +- src/rigraph/core/io/dl-lexer.c | 4 +- src/rigraph/core/io/dl-lexer.l | 4 +- src/rigraph/core/io/dl-parser.c | 10 +- src/rigraph/core/io/dl-parser.y | 10 +- src/rigraph/core/io/dl.c | 2 +- src/rigraph/core/io/dot.c | 4 +- src/rigraph/core/io/edgelist.c | 2 +- src/rigraph/core/io/gml-header.h | 2 +- src/rigraph/core/io/gml-lexer.c | 4 +- src/rigraph/core/io/gml-lexer.l | 4 +- src/rigraph/core/io/gml-parser.c | 12 +- src/rigraph/core/io/gml-tree.c | 2 +- src/rigraph/core/io/gml.c | 6 +- src/rigraph/core/io/graphdb.c | 2 +- src/rigraph/core/io/graphml.c | 8 +- src/rigraph/core/io/leda.c | 2 +- src/rigraph/core/io/lgl-header.h | 2 +- src/rigraph/core/io/lgl-lexer.c | 4 +- src/rigraph/core/io/lgl-lexer.l | 4 +- src/rigraph/core/io/lgl-parser.c | 10 +- src/rigraph/core/io/lgl-parser.y | 10 +- src/rigraph/core/io/lgl.c | 2 +- src/rigraph/core/io/ncol-header.h | 2 +- src/rigraph/core/io/ncol-lexer.c | 4 +- src/rigraph/core/io/ncol-lexer.l | 4 +- src/rigraph/core/io/ncol-parser.c | 10 +- src/rigraph/core/io/ncol-parser.y | 10 +- src/rigraph/core/io/ncol.c | 2 +- src/rigraph/core/io/pajek-header.h | 2 +- src/rigraph/core/io/pajek-lexer.c | 4 +- src/rigraph/core/io/pajek-lexer.l | 4 +- src/rigraph/core/io/pajek-parser.c | 10 +- src/rigraph/core/io/pajek-parser.y | 10 +- src/rigraph/core/io/pajek.c | 2 +- src/rigraph/core/isomorphism/bliss.cc | 2 +- src/rigraph/core/isomorphism/bliss/bignum.hh | 2 +- src/rigraph/core/isomorphism/isoclasses.c | 2 +- src/rigraph/core/isomorphism/lad.c | 2 +- src/rigraph/core/isomorphism/vf2.c | 2 +- src/rigraph/core/layout/circular.c | 4 +- src/rigraph/core/layout/davidson_harel.c | 6 +- src/rigraph/core/layout/drl/drl_graph.cpp | 2 +- src/rigraph/core/layout/drl/drl_graph_3d.cpp | 2 +- src/rigraph/core/layout/drl/drl_layout.cpp | 2 +- src/rigraph/core/layout/drl/drl_layout_3d.cpp | 2 +- .../core/layout/fruchterman_reingold.c | 4 +- src/rigraph/core/layout/gem.c | 4 +- src/rigraph/core/layout/graphopt.c | 2 +- src/rigraph/core/layout/kamada_kawai.c | 2 +- src/rigraph/core/layout/large_graph.c | 6 +- src/rigraph/core/layout/layout_internal.h | 2 +- src/rigraph/core/layout/merge_dla.c | 10 +- src/rigraph/core/layout/merge_grid.c | 2 +- src/rigraph/core/layout/reingold_tilford.c | 2 +- src/rigraph/core/layout/sugiyama.c | 4 +- src/rigraph/core/linalg/arpack.c | 2 +- src/rigraph/core/linalg/blas.c | 2 +- src/rigraph/core/linalg/lapack.c | 2 +- src/rigraph/core/math/bfgs.c | 2 +- src/rigraph/core/math/complex.c | 2 +- src/rigraph/core/math/utils.c | 6 +- src/rigraph/core/misc/bipartite.c | 2 +- src/rigraph/core/misc/cocitation.c | 2 +- src/rigraph/core/misc/coloring.c | 4 +- src/rigraph/core/misc/conversion.c | 6 +- src/rigraph/core/misc/feedback_arc_set.c | 4 +- src/rigraph/core/misc/motifs.c | 6 +- src/rigraph/core/misc/other.c | 6 +- src/rigraph/core/misc/scan.c | 4 +- src/rigraph/core/misc/sir.c | 2 +- src/rigraph/core/misc/spanning_trees.c | 4 +- src/rigraph/core/operators/complementer.c | 4 +- src/rigraph/core/operators/compose.c | 2 +- src/rigraph/core/operators/contract.c | 2 +- src/rigraph/core/operators/difference.c | 4 +- src/rigraph/core/operators/disjoint_union.c | 2 +- src/rigraph/core/operators/intersection.c | 2 +- src/rigraph/core/operators/misc_internal.c | 2 +- src/rigraph/core/operators/permute.c | 2 +- src/rigraph/core/operators/rewire.c | 4 +- src/rigraph/core/operators/rewire_edges.c | 2 +- src/rigraph/core/operators/simplify.c | 4 +- src/rigraph/core/operators/subgraph.c | 6 +- src/rigraph/core/operators/union.c | 2 +- src/rigraph/core/paths/all_shortest_paths.c | 2 +- src/rigraph/core/paths/bellman_ford.c | 4 +- src/rigraph/core/paths/dijkstra.c | 6 +- src/rigraph/core/paths/distances.c | 2 +- src/rigraph/core/paths/histogram.c | 2 +- src/rigraph/core/paths/random_walk.c | 2 +- src/rigraph/core/paths/shortest_paths.c | 4 +- src/rigraph/core/paths/simple_paths.c | 2 +- src/rigraph/core/paths/unweighted.c | 2 +- .../core/properties/convergence_degree.c | 2 +- src/rigraph/core/properties/girth.c | 2 +- src/rigraph/core/properties/triangles.c | 14 +- src/rigraph/core/random/random.c | 24 +- src/rigraph/core/scg/scg.c | 2 +- src/rigraph/rinterface.h | 2 +- src/rigraph/rrandom.c | 2 +- src/rigraph/vendor/arpack/debug.h | 16 + src/rigraph/vendor/arpack/dgetv0.f | 419 ++ src/rigraph/vendor/arpack/dlaqrb.f | 521 ++ src/rigraph/vendor/arpack/dmout.f | 167 + src/rigraph/vendor/arpack/dnaitr.f | 840 +++ src/rigraph/vendor/arpack/dnapps.f | 647 +++ src/rigraph/vendor/arpack/dnaup2.f | 838 +++ src/rigraph/vendor/arpack/dnaupd.f | 655 +++ src/rigraph/vendor/arpack/dnconv.f | 146 + src/rigraph/vendor/arpack/dneigh.f | 315 ++ src/rigraph/vendor/arpack/dneupd.f | 1044 ++++ src/rigraph/vendor/arpack/dngets.f | 231 + src/rigraph/vendor/arpack/dsaitr.f | 854 +++ src/rigraph/vendor/arpack/dsapps.f | 516 ++ src/rigraph/vendor/arpack/dsaup2.f | 853 +++ src/rigraph/vendor/arpack/dsaupd.f | 653 +++ src/rigraph/vendor/arpack/dsconv.f | 138 + src/rigraph/vendor/arpack/dseigt.f | 181 + src/rigraph/vendor/arpack/dsesrt.f | 217 + src/rigraph/vendor/arpack/dseupd.f | 905 ++++ src/rigraph/vendor/arpack/dsgets.f | 220 + src/rigraph/vendor/arpack/dsortc.f | 344 ++ src/rigraph/vendor/arpack/dsortr.f | 218 + src/rigraph/vendor/arpack/dstatn.f | 61 + src/rigraph/vendor/arpack/dstats.f | 47 + src/rigraph/vendor/arpack/dstqrb.f | 594 +++ src/rigraph/vendor/arpack/dvout.f | 122 + src/rigraph/vendor/arpack/ivout.f | 120 + src/rigraph/vendor/arpack/second.f | 35 + src/rigraph/vendor/arpack/stat.h | 21 + src/rigraph/vendor/arpack/wrap.f | 151 + src/rigraph/vendor/cs/SuiteSparse_config.h | 221 + src/rigraph/vendor/cs/cs.h | 758 +++ src/rigraph/vendor/cs/cs_add.c | 28 + src/rigraph/vendor/cs/cs_amd.c | 364 ++ src/rigraph/vendor/cs/cs_chol.c | 59 + src/rigraph/vendor/cs/cs_cholsol.c | 26 + src/rigraph/vendor/cs/cs_compress.c | 22 + src/rigraph/vendor/cs/cs_counts.c | 61 + src/rigraph/vendor/cs/cs_cumsum.c | 17 + src/rigraph/vendor/cs/cs_dfs.c | 36 + src/rigraph/vendor/cs/cs_dmperm.c | 144 + src/rigraph/vendor/cs/cs_droptol.c | 9 + src/rigraph/vendor/cs/cs_dropzeros.c | 9 + src/rigraph/vendor/cs/cs_dupl.c | 34 + src/rigraph/vendor/cs/cs_entry.c | 13 + src/rigraph/vendor/cs/cs_ereach.c | 23 + src/rigraph/vendor/cs/cs_etree.c | 30 + src/rigraph/vendor/cs/cs_fkeep.c | 25 + src/rigraph/vendor/cs/cs_gaxpy.c | 17 + src/rigraph/vendor/cs/cs_happly.c | 19 + src/rigraph/vendor/cs/cs_house.c | 30 + src/rigraph/vendor/cs/cs_ipvec.c | 9 + src/rigraph/vendor/cs/cs_leaf.c | 22 + src/rigraph/vendor/cs/cs_load.c | 26 + src/rigraph/vendor/cs/cs_lsolve.c | 18 + src/rigraph/vendor/cs/cs_ltsolve.c | 18 + src/rigraph/vendor/cs/cs_lu.c | 88 + src/rigraph/vendor/cs/cs_lusol.c | 26 + src/rigraph/vendor/cs/cs_malloc.c | 35 + src/rigraph/vendor/cs/cs_maxtrans.c | 92 + src/rigraph/vendor/cs/cs_multiply.c | 35 + src/rigraph/vendor/cs/cs_norm.c | 16 + src/rigraph/vendor/cs/cs_permute.c | 25 + src/rigraph/vendor/cs/cs_pinv.c | 11 + src/rigraph/vendor/cs/cs_post.c | 24 + src/rigraph/vendor/cs/cs_print.c | 55 + src/rigraph/vendor/cs/cs_pvec.c | 9 + src/rigraph/vendor/cs/cs_qr.c | 74 + src/rigraph/vendor/cs/cs_qrsol.c | 53 + src/rigraph/vendor/cs/cs_randperm.c | 28 + src/rigraph/vendor/cs/cs_reach.c | 19 + src/rigraph/vendor/cs/cs_scatter.c | 22 + src/rigraph/vendor/cs/cs_scc.c | 41 + src/rigraph/vendor/cs/cs_schol.c | 26 + src/rigraph/vendor/cs/cs_spsolve.c | 28 + src/rigraph/vendor/cs/cs_sqr.c | 87 + src/rigraph/vendor/cs/cs_symperm.c | 39 + src/rigraph/vendor/cs/cs_tdfs.c | 24 + src/rigraph/vendor/cs/cs_transpose.c | 25 + src/rigraph/vendor/cs/cs_updown.c | 48 + src/rigraph/vendor/cs/cs_usolve.c | 18 + src/rigraph/vendor/cs/cs_util.c | 120 + src/rigraph/vendor/cs/cs_utsolve.c | 18 + src/rigraph/vendor/mini-gmp/mini-gmp.c | 4578 +++++++++++++++++ src/rigraph/vendor/mini-gmp/mini-gmp.h | 305 ++ src/rigraph/vendor/plfit/arithmetic_ansi.h | 133 + .../vendor/plfit/arithmetic_sse_double.h | 294 ++ .../vendor/plfit/arithmetic_sse_float.h | 291 ++ src/rigraph/vendor/plfit/gss.c | 153 + src/rigraph/vendor/plfit/gss.h | 146 + src/rigraph/vendor/plfit/hzeta.c | 651 +++ src/rigraph/vendor/plfit/hzeta.h | 96 + src/rigraph/vendor/plfit/kolmogorov.c | 66 + src/rigraph/vendor/plfit/kolmogorov.h | 43 + src/rigraph/vendor/plfit/lbfgs.c | 1378 +++++ src/rigraph/vendor/plfit/lbfgs.h | 736 +++ src/rigraph/vendor/plfit/mt.c | 93 + src/rigraph/vendor/plfit/options.c | 52 + src/rigraph/vendor/plfit/platform.c | 36 + src/rigraph/vendor/plfit/platform.h | 69 + src/rigraph/vendor/plfit/plfit.c | 1342 +++++ src/rigraph/vendor/plfit/plfit.h | 140 + src/rigraph/vendor/plfit/plfit_error.c | 66 + src/rigraph/vendor/plfit/plfit_error.h | 86 + src/rigraph/vendor/plfit/plfit_mt.h | 101 + src/rigraph/vendor/plfit/plfit_sampling.h | 177 + src/rigraph/vendor/plfit/plfit_version.h | 28 + src/rigraph/vendor/plfit/rbinom.c | 208 + src/rigraph/vendor/plfit/sampling.c | 312 ++ src/rigraph/vendor/simpleraytracer/Color.cpp | 96 + src/rigraph/vendor/simpleraytracer/Color.h | 40 + src/rigraph/vendor/simpleraytracer/Light.cpp | 46 + src/rigraph/vendor/simpleraytracer/Light.h | 39 + src/rigraph/vendor/simpleraytracer/Point.cpp | 106 + src/rigraph/vendor/simpleraytracer/Point.h | 45 + .../vendor/simpleraytracer/RIgraphRay.cpp | 93 + src/rigraph/vendor/simpleraytracer/Ray.cpp | 44 + src/rigraph/vendor/simpleraytracer/Ray.h | 33 + .../vendor/simpleraytracer/RayTracer.cpp | 266 + .../vendor/simpleraytracer/RayTracer.h | 63 + .../vendor/simpleraytracer/RayVector.cpp | 128 + .../vendor/simpleraytracer/RayVector.h | 49 + src/rigraph/vendor/simpleraytracer/Shape.cpp | 106 + src/rigraph/vendor/simpleraytracer/Shape.h | 65 + src/rigraph/vendor/simpleraytracer/Sphere.cpp | 70 + src/rigraph/vendor/simpleraytracer/Sphere.h | 31 + .../vendor/simpleraytracer/Triangle.cpp | 90 + src/rigraph/vendor/simpleraytracer/Triangle.h | 27 + .../vendor/simpleraytracer/unit_limiter.cpp | 15 + .../vendor/simpleraytracer/unit_limiter.h | 10 + src/rigraph/vendor/uuid/COPYING | 28 + src/rigraph/vendor/uuid/Makevars.in | 2 + src/rigraph/vendor/uuid/Makevars.win | 1 + src/rigraph/vendor/uuid/R.c | 25 + src/rigraph/vendor/uuid/clear.c | 43 + src/rigraph/vendor/uuid/compare.c | 55 + src/rigraph/vendor/uuid/config.h.in | 82 + src/rigraph/vendor/uuid/copy.c | 45 + src/rigraph/vendor/uuid/gen_uuid.c | 536 ++ src/rigraph/vendor/uuid/isnull.c | 48 + src/rigraph/vendor/uuid/pack.c | 69 + src/rigraph/vendor/uuid/parse.c | 79 + src/rigraph/vendor/uuid/unpack.c | 63 + src/rigraph/vendor/uuid/unparse.c | 76 + src/rigraph/vendor/uuid/uuid.h | 104 + src/rigraph/vendor/uuid/uuidP.h | 63 + src/rigraph/vendor/uuid/uuidd.h | 54 + src/rigraph/vendor/uuid/win32/config.h | 84 + 292 files changed, 29987 insertions(+), 274 deletions(-) create mode 100644 src/rigraph/vendor/arpack/debug.h create mode 100644 src/rigraph/vendor/arpack/dgetv0.f create mode 100644 src/rigraph/vendor/arpack/dlaqrb.f create mode 100644 src/rigraph/vendor/arpack/dmout.f create mode 100644 src/rigraph/vendor/arpack/dnaitr.f create mode 100644 src/rigraph/vendor/arpack/dnapps.f create mode 100644 src/rigraph/vendor/arpack/dnaup2.f create mode 100644 src/rigraph/vendor/arpack/dnaupd.f create mode 100644 src/rigraph/vendor/arpack/dnconv.f create mode 100644 src/rigraph/vendor/arpack/dneigh.f create mode 100644 src/rigraph/vendor/arpack/dneupd.f create mode 100644 src/rigraph/vendor/arpack/dngets.f create mode 100644 src/rigraph/vendor/arpack/dsaitr.f create mode 100644 src/rigraph/vendor/arpack/dsapps.f create mode 100644 src/rigraph/vendor/arpack/dsaup2.f create mode 100644 src/rigraph/vendor/arpack/dsaupd.f create mode 100644 src/rigraph/vendor/arpack/dsconv.f create mode 100644 src/rigraph/vendor/arpack/dseigt.f create mode 100644 src/rigraph/vendor/arpack/dsesrt.f create mode 100644 src/rigraph/vendor/arpack/dseupd.f create mode 100644 src/rigraph/vendor/arpack/dsgets.f create mode 100644 src/rigraph/vendor/arpack/dsortc.f create mode 100644 src/rigraph/vendor/arpack/dsortr.f create mode 100644 src/rigraph/vendor/arpack/dstatn.f create mode 100644 src/rigraph/vendor/arpack/dstats.f create mode 100644 src/rigraph/vendor/arpack/dstqrb.f create mode 100644 src/rigraph/vendor/arpack/dvout.f create mode 100644 src/rigraph/vendor/arpack/ivout.f create mode 100644 src/rigraph/vendor/arpack/second.f create mode 100644 src/rigraph/vendor/arpack/stat.h create mode 100644 src/rigraph/vendor/arpack/wrap.f create mode 100644 src/rigraph/vendor/cs/SuiteSparse_config.h create mode 100644 src/rigraph/vendor/cs/cs.h create mode 100644 src/rigraph/vendor/cs/cs_add.c create mode 100644 src/rigraph/vendor/cs/cs_amd.c create mode 100644 src/rigraph/vendor/cs/cs_chol.c create mode 100644 src/rigraph/vendor/cs/cs_cholsol.c create mode 100644 src/rigraph/vendor/cs/cs_compress.c create mode 100644 src/rigraph/vendor/cs/cs_counts.c create mode 100644 src/rigraph/vendor/cs/cs_cumsum.c create mode 100644 src/rigraph/vendor/cs/cs_dfs.c create mode 100644 src/rigraph/vendor/cs/cs_dmperm.c create mode 100644 src/rigraph/vendor/cs/cs_droptol.c create mode 100644 src/rigraph/vendor/cs/cs_dropzeros.c create mode 100644 src/rigraph/vendor/cs/cs_dupl.c create mode 100644 src/rigraph/vendor/cs/cs_entry.c create mode 100644 src/rigraph/vendor/cs/cs_ereach.c create mode 100644 src/rigraph/vendor/cs/cs_etree.c create mode 100644 src/rigraph/vendor/cs/cs_fkeep.c create mode 100644 src/rigraph/vendor/cs/cs_gaxpy.c create mode 100644 src/rigraph/vendor/cs/cs_happly.c create mode 100644 src/rigraph/vendor/cs/cs_house.c create mode 100644 src/rigraph/vendor/cs/cs_ipvec.c create mode 100644 src/rigraph/vendor/cs/cs_leaf.c create mode 100644 src/rigraph/vendor/cs/cs_load.c create mode 100644 src/rigraph/vendor/cs/cs_lsolve.c create mode 100644 src/rigraph/vendor/cs/cs_ltsolve.c create mode 100644 src/rigraph/vendor/cs/cs_lu.c create mode 100644 src/rigraph/vendor/cs/cs_lusol.c create mode 100644 src/rigraph/vendor/cs/cs_malloc.c create mode 100644 src/rigraph/vendor/cs/cs_maxtrans.c create mode 100644 src/rigraph/vendor/cs/cs_multiply.c create mode 100644 src/rigraph/vendor/cs/cs_norm.c create mode 100644 src/rigraph/vendor/cs/cs_permute.c create mode 100644 src/rigraph/vendor/cs/cs_pinv.c create mode 100644 src/rigraph/vendor/cs/cs_post.c create mode 100644 src/rigraph/vendor/cs/cs_print.c create mode 100644 src/rigraph/vendor/cs/cs_pvec.c create mode 100644 src/rigraph/vendor/cs/cs_qr.c create mode 100644 src/rigraph/vendor/cs/cs_qrsol.c create mode 100644 src/rigraph/vendor/cs/cs_randperm.c create mode 100644 src/rigraph/vendor/cs/cs_reach.c create mode 100644 src/rigraph/vendor/cs/cs_scatter.c create mode 100644 src/rigraph/vendor/cs/cs_scc.c create mode 100644 src/rigraph/vendor/cs/cs_schol.c create mode 100644 src/rigraph/vendor/cs/cs_spsolve.c create mode 100644 src/rigraph/vendor/cs/cs_sqr.c create mode 100644 src/rigraph/vendor/cs/cs_symperm.c create mode 100644 src/rigraph/vendor/cs/cs_tdfs.c create mode 100644 src/rigraph/vendor/cs/cs_transpose.c create mode 100644 src/rigraph/vendor/cs/cs_updown.c create mode 100644 src/rigraph/vendor/cs/cs_usolve.c create mode 100644 src/rigraph/vendor/cs/cs_util.c create mode 100644 src/rigraph/vendor/cs/cs_utsolve.c create mode 100644 src/rigraph/vendor/mini-gmp/mini-gmp.c create mode 100644 src/rigraph/vendor/mini-gmp/mini-gmp.h create mode 100644 src/rigraph/vendor/plfit/arithmetic_ansi.h create mode 100644 src/rigraph/vendor/plfit/arithmetic_sse_double.h create mode 100644 src/rigraph/vendor/plfit/arithmetic_sse_float.h create mode 100644 src/rigraph/vendor/plfit/gss.c create mode 100644 src/rigraph/vendor/plfit/gss.h create mode 100644 src/rigraph/vendor/plfit/hzeta.c create mode 100644 src/rigraph/vendor/plfit/hzeta.h create mode 100644 src/rigraph/vendor/plfit/kolmogorov.c create mode 100644 src/rigraph/vendor/plfit/kolmogorov.h create mode 100644 src/rigraph/vendor/plfit/lbfgs.c create mode 100644 src/rigraph/vendor/plfit/lbfgs.h create mode 100644 src/rigraph/vendor/plfit/mt.c create mode 100644 src/rigraph/vendor/plfit/options.c create mode 100644 src/rigraph/vendor/plfit/platform.c create mode 100644 src/rigraph/vendor/plfit/platform.h create mode 100644 src/rigraph/vendor/plfit/plfit.c create mode 100644 src/rigraph/vendor/plfit/plfit.h create mode 100644 src/rigraph/vendor/plfit/plfit_error.c create mode 100644 src/rigraph/vendor/plfit/plfit_error.h create mode 100644 src/rigraph/vendor/plfit/plfit_mt.h create mode 100644 src/rigraph/vendor/plfit/plfit_sampling.h create mode 100644 src/rigraph/vendor/plfit/plfit_version.h create mode 100644 src/rigraph/vendor/plfit/rbinom.c create mode 100644 src/rigraph/vendor/plfit/sampling.c create mode 100644 src/rigraph/vendor/simpleraytracer/Color.cpp create mode 100644 src/rigraph/vendor/simpleraytracer/Color.h create mode 100644 src/rigraph/vendor/simpleraytracer/Light.cpp create mode 100644 src/rigraph/vendor/simpleraytracer/Light.h create mode 100644 src/rigraph/vendor/simpleraytracer/Point.cpp create mode 100644 src/rigraph/vendor/simpleraytracer/Point.h create mode 100644 src/rigraph/vendor/simpleraytracer/RIgraphRay.cpp create mode 100644 src/rigraph/vendor/simpleraytracer/Ray.cpp create mode 100644 src/rigraph/vendor/simpleraytracer/Ray.h create mode 100644 src/rigraph/vendor/simpleraytracer/RayTracer.cpp create mode 100644 src/rigraph/vendor/simpleraytracer/RayTracer.h create mode 100644 src/rigraph/vendor/simpleraytracer/RayVector.cpp create mode 100644 src/rigraph/vendor/simpleraytracer/RayVector.h create mode 100644 src/rigraph/vendor/simpleraytracer/Shape.cpp create mode 100644 src/rigraph/vendor/simpleraytracer/Shape.h create mode 100644 src/rigraph/vendor/simpleraytracer/Sphere.cpp create mode 100644 src/rigraph/vendor/simpleraytracer/Sphere.h create mode 100644 src/rigraph/vendor/simpleraytracer/Triangle.cpp create mode 100644 src/rigraph/vendor/simpleraytracer/Triangle.h create mode 100644 src/rigraph/vendor/simpleraytracer/unit_limiter.cpp create mode 100644 src/rigraph/vendor/simpleraytracer/unit_limiter.h create mode 100644 src/rigraph/vendor/uuid/COPYING create mode 100644 src/rigraph/vendor/uuid/Makevars.in create mode 100644 src/rigraph/vendor/uuid/Makevars.win create mode 100644 src/rigraph/vendor/uuid/R.c create mode 100644 src/rigraph/vendor/uuid/clear.c create mode 100644 src/rigraph/vendor/uuid/compare.c create mode 100644 src/rigraph/vendor/uuid/config.h.in create mode 100644 src/rigraph/vendor/uuid/copy.c create mode 100644 src/rigraph/vendor/uuid/gen_uuid.c create mode 100644 src/rigraph/vendor/uuid/isnull.c create mode 100644 src/rigraph/vendor/uuid/pack.c create mode 100644 src/rigraph/vendor/uuid/parse.c create mode 100644 src/rigraph/vendor/uuid/unpack.c create mode 100644 src/rigraph/vendor/uuid/unparse.c create mode 100644 src/rigraph/vendor/uuid/uuid.h create mode 100644 src/rigraph/vendor/uuid/uuidP.h create mode 100644 src/rigraph/vendor/uuid/uuidd.h create mode 100644 src/rigraph/vendor/uuid/win32/config.h diff --git a/src/rigraph/Makefile b/src/rigraph/Makefile index b8fd21a..b3398a3 100644 --- a/src/rigraph/Makefile +++ b/src/rigraph/Makefile @@ -4,6 +4,7 @@ PKG_CFLAGS = -O3 -std=c11 -std=gnu11 -pthread -fPIC $(SHLIB_OPENMP_CXXFLAGS) PKG_CFLAGS += -I"./include" -I"../rigraph/include" LDFLAGS += -lpthread + CXX ?= g++ PKG_CXXFLAGS = -O3 -std=c++11 -pthread -fPIC $(SHLIB_OPENMP_CXXFLAGS) PKG_CXXFLAGS += -I"./include" -I"../rigraph/include" @@ -14,7 +15,7 @@ OBJECTS=core/centrality/betweenness.o core/centrality/centrality_other.o core/ce LIB=../librigraph.a lib: $(LIB) -## ignoring: core/centrality/prpack.o +## ignoring: core/centrality/prpack.o $(LIB): core/centrality/betweenness.o core/centrality/centrality_other.o core/centrality/centralization.o core/centrality/closeness.o core/centrality/coreness.o core/centrality/prpack/prpack_base_graph.o core/centrality/prpack/prpack_igraph_graph.o core/centrality/prpack/prpack_preprocessed_ge_graph.o core/centrality/prpack/prpack_preprocessed_gs_graph.o core/centrality/prpack/prpack_preprocessed_scc_graph.o core/centrality/prpack/prpack_preprocessed_schur_graph.o core/centrality/prpack/prpack_result.o core/centrality/prpack/prpack_solver.o core/centrality/prpack/prpack_utils.o core/cliques/cliquer/cliquer.o core/cliques/cliquer/cliquer_graph.o core/cliques/cliquer/reorder.o core/cliques/cliquer_wrapper.o core/cliques/cliques.o core/cliques/glet.o core/cliques/maximal_cliques.o core/community/community_misc.o core/community/edge_betweenness.o core/community/fast_modularity.o core/community/fluid.o core/community/infomap/infomap.o core/community/infomap/infomap_FlowGraph.o core/community/infomap/infomap_Greedy.o core/community/infomap/infomap_Node.o core/community/label_propagation.o core/community/leading_eigenvector.o core/community/leiden.o core/community/louvain.o core/community/modularity.o core/community/optimal_modularity.o core/community/spinglass/NetDataTypes.o core/community/spinglass/NetRoutines.o core/community/spinglass/clustertool.o core/community/spinglass/pottsmodel_2.o core/community/walktrap/walktrap.o core/community/walktrap/walktrap_communities.o core/community/walktrap/walktrap_graph.o core/community/walktrap/walktrap_heap.o core/connectivity/cohesive_blocks.o core/connectivity/components.o core/connectivity/separators.o core/constructors/adjacency.o core/constructors/atlas.o core/constructors/basic_constructors.o core/constructors/de_bruijn.o core/constructors/famous.o core/constructors/full.o core/constructors/kautz.o core/constructors/lcf.o core/constructors/linegraph.o core/constructors/prufer.o core/constructors/regular.o core/core/array.o core/core/buckets.o core/core/cutheap.o core/core/dqueue.o core/core/error.o core/core/estack.o core/core/fixed_vectorlist.o core/core/grid.o core/core/heap.o core/core/indheap.o core/core/interruption.o core/core/marked_queue.o core/core/matrix.o core/core/memory.o core/core/printing.o core/core/progress.o core/core/psumtree.o core/core/set.o core/core/sparsemat.o core/core/spmatrix.o core/core/stack.o core/core/statusbar.o core/core/strvector.o core/core/trie.o core/core/vector.o core/core/vector_ptr.o core/flow/flow.o core/flow/st-cuts.o core/games/barabasi.o core/games/callaway_traits.o core/games/citations.o core/games/correlated.o core/games/degree_sequence.o core/games/degree_sequence_vl/gengraph_box_list.o core/games/degree_sequence_vl/gengraph_degree_sequence.o core/games/degree_sequence_vl/gengraph_graph_molloy_hash.o core/games/degree_sequence_vl/gengraph_graph_molloy_optimized.o core/games/degree_sequence_vl/gengraph_mr-connected.o core/games/degree_sequence_vl/gengraph_powerlaw.o core/games/degree_sequence_vl/gengraph_random.o core/games/dotproduct.o core/games/erdos_renyi.o core/games/establishment.o core/games/forestfire.o core/games/grg.o core/games/growing_random.o core/games/islands.o core/games/k_regular.o core/games/preference.o core/games/recent_degree.o core/games/sbm.o core/games/static_fitness.o core/games/tree.o core/games/watts_strogatz.o core/graph/adjlist.o core/graph/attributes.o core/graph/basic_query.o core/graph/cattributes.o core/graph/iterators.o core/graph/type_indexededgelist.o core/graph/visitors.o core/hrg/hrg.o core/hrg/hrg_types.o core/internal/glpk_support.o core/internal/hacks.o core/internal/lsap.o core/internal/qsort.o core/internal/qsort_r.o core/internal/zeroin.o core/io/dimacs.o core/io/dl-lexer.o core/io/dl-parser.o core/io/dl.o core/io/dot.o core/io/edgelist.o core/io/gml-lexer.o core/io/gml-parser.o core/io/gml-tree.o core/io/gml.o core/io/graphdb.o core/io/graphml.o core/io/leda.o core/io/lgl-lexer.o core/io/lgl-parser.o core/io/lgl.o core/io/ncol-lexer.o core/io/ncol-parser.o core/io/ncol.o core/io/pajek-lexer.o core/io/pajek-parser.o core/io/pajek.o core/isomorphism/bliss.o core/isomorphism/bliss/defs.o core/isomorphism/bliss/graph.o core/isomorphism/bliss/heap.o core/isomorphism/bliss/orbit.o core/isomorphism/bliss/partition.o core/isomorphism/bliss/uintseqhash.o core/isomorphism/bliss/utils.o core/isomorphism/isoclasses.o core/isomorphism/isomorphism_misc.o core/isomorphism/lad.o core/isomorphism/queries.o core/isomorphism/vf2.o core/layout/circular.o core/layout/davidson_harel.o core/layout/drl/DensityGrid.o core/layout/drl/DensityGrid_3d.o core/layout/drl/drl_graph.o core/layout/drl/drl_graph_3d.o core/layout/drl/drl_layout.o core/layout/drl/drl_layout_3d.o core/layout/drl/drl_parse.o core/layout/fruchterman_reingold.o core/layout/gem.o core/layout/graphopt.o core/layout/kamada_kawai.o core/layout/large_graph.o core/layout/layout_bipartite.o core/layout/layout_grid.o core/layout/layout_random.o core/layout/mds.o core/layout/merge_dla.o core/layout/merge_grid.o core/layout/reingold_tilford.o core/layout/sugiyama.o core/linalg/arpack.o core/linalg/blas.o core/linalg/eigen.o core/linalg/lapack.o core/math/bfgs.o core/math/complex.o core/math/utils.o core/misc/bipartite.o core/misc/chordality.o core/misc/cocitation.o core/misc/coloring.o core/misc/conversion.o core/misc/degree_sequence.o core/misc/embedding.o core/misc/feedback_arc_set.o core/misc/graphicality.o core/misc/matching.o core/misc/microscopic_update.o core/misc/mixing.o core/misc/motifs.o core/misc/other.o core/misc/scan.o core/misc/sir.o core/misc/spanning_trees.o core/operators/add_edge.o core/operators/complementer.o core/operators/compose.o core/operators/connect_neighborhood.o core/operators/contract.o core/operators/difference.o core/operators/disjoint_union.o core/operators/intersection.o core/operators/misc_internal.o core/operators/permute.o core/operators/rewire.o core/operators/rewire_edges.o core/operators/simplify.o core/operators/subgraph.o core/operators/union.o core/paths/all_shortest_paths.o core/paths/bellman_ford.o core/paths/dijkstra.o core/paths/distances.o core/paths/eulerian.o core/paths/histogram.o core/paths/johnson.o core/paths/random_walk.o core/paths/shortest_paths.o core/paths/simple_paths.o core/paths/unweighted.o core/properties/basic_properties.o core/properties/constraint.o core/properties/convergence_degree.o core/properties/dag.o core/properties/degrees.o core/properties/girth.o core/properties/loops.o core/properties/multiplicity.o core/properties/neighborhood.o core/properties/spectral.o core/properties/trees.o core/properties/triangles.o core/random/random.o core/scg/scg.o core/scg/scg_approximate_methods.o core/scg/scg_exact_scg.o core/scg/scg_kmeans.o core/scg/scg_optimal_method.o core/scg/scg_utils.o core/version.o vendor/cs/cs_add.o vendor/cs/cs_amd.o vendor/cs/cs_chol.o vendor/cs/cs_cholsol.o vendor/cs/cs_compress.o vendor/cs/cs_counts.o vendor/cs/cs_cumsum.o vendor/cs/cs_dfs.o vendor/cs/cs_dmperm.o vendor/cs/cs_droptol.o vendor/cs/cs_dropzeros.o vendor/cs/cs_dupl.o vendor/cs/cs_entry.o vendor/cs/cs_ereach.o vendor/cs/cs_etree.o vendor/cs/cs_fkeep.o vendor/cs/cs_gaxpy.o vendor/cs/cs_happly.o vendor/cs/cs_house.o vendor/cs/cs_ipvec.o vendor/cs/cs_leaf.o vendor/cs/cs_load.o vendor/cs/cs_lsolve.o vendor/cs/cs_ltsolve.o vendor/cs/cs_lu.o vendor/cs/cs_lusol.o vendor/cs/cs_malloc.o vendor/cs/cs_maxtrans.o vendor/cs/cs_multiply.o vendor/cs/cs_norm.o vendor/cs/cs_permute.o vendor/cs/cs_pinv.o vendor/cs/cs_post.o vendor/cs/cs_print.o vendor/cs/cs_pvec.o vendor/cs/cs_qr.o vendor/cs/cs_qrsol.o vendor/cs/cs_randperm.o vendor/cs/cs_reach.o vendor/cs/cs_scatter.o vendor/cs/cs_scc.o vendor/cs/cs_schol.o vendor/cs/cs_spsolve.o vendor/cs/cs_sqr.o vendor/cs/cs_symperm.o vendor/cs/cs_tdfs.o vendor/cs/cs_transpose.o vendor/cs/cs_updown.o vendor/cs/cs_usolve.o vendor/cs/cs_util.o vendor/cs/cs_utsolve.o vendor/mini-gmp/mini-gmp.o vendor/plfit/gss.o vendor/plfit/hzeta.o vendor/plfit/kolmogorov.o vendor/plfit/lbfgs.o vendor/plfit/mt.o vendor/plfit/options.o vendor/plfit/platform.o vendor/plfit/plfit.o vendor/plfit/plfit_error.o vendor/plfit/rbinom.o vendor/plfit/sampling.o vendor/arpack/dgetv0.o vendor/arpack/dlaqrb.o vendor/arpack/dmout.o vendor/arpack/dnaitr.o vendor/arpack/dnapps.o vendor/arpack/dnaup2.o vendor/arpack/dnaupd.o vendor/arpack/dnconv.o vendor/arpack/dneigh.o vendor/arpack/dneupd.o vendor/arpack/dngets.o vendor/arpack/dsaitr.o vendor/arpack/dsapps.o vendor/arpack/dsaup2.o vendor/arpack/dsaupd.o vendor/arpack/dsconv.o vendor/arpack/dseigt.o vendor/arpack/dsesrt.o vendor/arpack/dseupd.o vendor/arpack/dsgets.o vendor/arpack/dsortc.o vendor/arpack/dsortr.o vendor/arpack/dstatn.o vendor/arpack/dstats.o vendor/arpack/dstqrb.o vendor/arpack/dvout.o vendor/arpack/ivout.o vendor/arpack/second.o vendor/arpack/wrap.o vendor/simpleraytracer/Color.o vendor/simpleraytracer/Light.o vendor/simpleraytracer/Point.o vendor/simpleraytracer/RIgraphRay.o vendor/simpleraytracer/Ray.o vendor/simpleraytracer/RayTracer.o vendor/simpleraytracer/RayVector.o vendor/simpleraytracer/Shape.o vendor/simpleraytracer/Sphere.o vendor/simpleraytracer/Triangle.o vendor/simpleraytracer/unit_limiter.o vendor/uuid/R.o vendor/uuid/clear.o vendor/uuid/compare.o vendor/uuid/copy.o vendor/uuid/gen_uuid.o vendor/uuid/isnull.o vendor/uuid/pack.o vendor/uuid/parse.o vendor/uuid/unpack.o vendor/uuid/unparse.o rinterface.o rinterface_extra.o rrandom.o lazyeval.o ar rvs $@ $? diff --git a/src/rigraph/core/core/buckets.c b/src/rigraph/core/core/buckets.c index 67be507..343780d 100644 --- a/src/rigraph/core/core/buckets.c +++ b/src/rigraph/core/core/buckets.c @@ -23,7 +23,7 @@ #include "igraph_types.h" -#include "core/buckets.h" +#include "buckets.h" /* The igraph_buckets_t data structure can store at most 'size' * unique integers in 'bsize' buckets. It has the following simple diff --git a/src/rigraph/core/core/cutheap.c b/src/rigraph/core/core/cutheap.c index 1ab0713..86bb4c3 100644 --- a/src/rigraph/core/core/cutheap.c +++ b/src/rigraph/core/core/cutheap.c @@ -22,7 +22,7 @@ #include "igraph_types.h" -#include "core/cutheap.h" +#include "cutheap.h" #define PARENT(x) ((x)/2) #define LEFTCHILD(x) ((x)*2+1) diff --git a/src/rigraph/core/core/error.c b/src/rigraph/core/core/error.c index ff3047a..d7b65f7 100644 --- a/src/rigraph/core/core/error.c +++ b/src/rigraph/core/core/error.c @@ -78,10 +78,10 @@ static IGRAPH_NORETURN void igraph_abort() { /***** Handling errors *****/ -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]; -static IGRAPH_THREAD_LOCAL char igraph_i_fatalmsg_buffer[500]; +static igraph_error_handler_t *igraph_i_error_handler = 0; +static char igraph_i_errormsg_buffer[500]; +static char igraph_i_warningmsg_buffer[500]; +static char igraph_i_fatalmsg_buffer[500]; /* Error strings corresponding to each igraph_error_type_t enum value. */ static const char *igraph_i_error_strings[] = { @@ -227,7 +227,7 @@ igraph_error_handler_t *igraph_set_error_handler(igraph_error_handler_t *new_han /***** "Finally" stack *****/ -IGRAPH_THREAD_LOCAL struct igraph_i_protectedPtr igraph_i_finally_stack[100]; +struct igraph_i_protectedPtr igraph_i_finally_stack[100]; /* * Adds another element to the free list @@ -270,7 +270,7 @@ int IGRAPH_FINALLY_STACK_SIZE(void) { /***** Handling warnings *****/ -static IGRAPH_THREAD_LOCAL igraph_warning_handler_t *igraph_i_warning_handler = 0; +static igraph_warning_handler_t *igraph_i_warning_handler = 0; /** * \function igraph_warning_handler_ignore @@ -348,7 +348,7 @@ igraph_warning_handler_t *igraph_set_warning_handler(igraph_warning_handler_t *n /***** Handling fatal errors *****/ -static IGRAPH_THREAD_LOCAL igraph_fatal_handler_t *igraph_i_fatal_handler = NULL; +static igraph_fatal_handler_t *igraph_i_fatal_handler = NULL; igraph_fatal_handler_t *igraph_set_fatal_handler(igraph_fatal_handler_t *new_handler) { igraph_fatal_handler_t *previous_handler = igraph_i_fatal_handler; diff --git a/src/rigraph/core/core/estack.c b/src/rigraph/core/core/estack.c index 41a62ed..b5a4955 100644 --- a/src/rigraph/core/core/estack.c +++ b/src/rigraph/core/core/estack.c @@ -21,7 +21,7 @@ */ -#include "core/estack.h" +#include "estack.h" int igraph_estack_init(igraph_estack_t *s, long int setsize, long int stacksize) { diff --git a/src/rigraph/core/core/fixed_vectorlist.c b/src/rigraph/core/core/fixed_vectorlist.c index adc482e..56e138f 100644 --- a/src/rigraph/core/core/fixed_vectorlist.c +++ b/src/rigraph/core/core/fixed_vectorlist.c @@ -23,7 +23,7 @@ #include "igraph_memory.h" -#include "core/fixed_vectorlist.h" +#include "fixed_vectorlist.h" void igraph_fixed_vectorlist_destroy(igraph_fixed_vectorlist_t *l) { long int i, n = igraph_vector_ptr_size(&l->v); diff --git a/src/rigraph/core/core/grid.c b/src/rigraph/core/core/grid.c index 3426556..b27eb36 100644 --- a/src/rigraph/core/core/grid.c +++ b/src/rigraph/core/core/grid.c @@ -23,7 +23,7 @@ #include "igraph_types.h" -#include "core/grid.h" +#include "grid.h" #include diff --git a/src/rigraph/core/core/indheap.c b/src/rigraph/core/core/indheap.c index 67592b0..7d2a108 100644 --- a/src/rigraph/core/core/indheap.c +++ b/src/rigraph/core/core/indheap.c @@ -24,7 +24,7 @@ #include "igraph_memory.h" #include "igraph_error.h" -#include "core/indheap.h" +#include "indheap.h" #include /* memcpy & co. */ #include diff --git a/src/rigraph/core/core/interruption.c b/src/rigraph/core/core/interruption.c index d4bf3c9..b9a88ea 100644 --- a/src/rigraph/core/core/interruption.c +++ b/src/rigraph/core/core/interruption.c @@ -24,7 +24,7 @@ #include "igraph_interrupt.h" #include "config.h" -IGRAPH_THREAD_LOCAL igraph_interruption_handler_t +igraph_interruption_handler_t *igraph_i_interruption_handler = 0; int igraph_allow_interruption(void* data) { diff --git a/src/rigraph/core/core/marked_queue.c b/src/rigraph/core/core/marked_queue.c index 8166793..c75491f 100644 --- a/src/rigraph/core/core/marked_queue.c +++ b/src/rigraph/core/core/marked_queue.c @@ -21,7 +21,7 @@ */ -#include "core/marked_queue.h" +#include "marked_queue.h" #define BATCH_MARKER -1 diff --git a/src/rigraph/core/core/progress.c b/src/rigraph/core/core/progress.c index 436d403..77d0e57 100644 --- a/src/rigraph/core/core/progress.c +++ b/src/rigraph/core/core/progress.c @@ -25,8 +25,8 @@ #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]; +static igraph_progress_handler_t *igraph_i_progress_handler = 0; +static char igraph_i_progressmsg_buffer[1000]; /** * \function igraph_progress diff --git a/src/rigraph/core/core/set.c b/src/rigraph/core/core/set.c index 7ce264d..cb0a0c1 100644 --- a/src/rigraph/core/core/set.c +++ b/src/rigraph/core/core/set.c @@ -25,7 +25,7 @@ #include "igraph_memory.h" #include "igraph_error.h" -#include "core/set.h" +#include "set.h" #include /* memmove */ diff --git a/src/rigraph/core/core/sparsemat.c b/src/rigraph/core/core/sparsemat.c index c7c836e..fe5e34a 100644 --- a/src/rigraph/core/core/sparsemat.c +++ b/src/rigraph/core/core/sparsemat.c @@ -21,7 +21,7 @@ */ -#include +#include "../../vendor/cs/cs.h" #include "igraph_sparsemat.h" #include "igraph_error.h" diff --git a/src/rigraph/core/core/statusbar.c b/src/rigraph/core/core/statusbar.c index 07cdad2..0c3358d 100644 --- a/src/rigraph/core/core/statusbar.c +++ b/src/rigraph/core/core/statusbar.c @@ -29,7 +29,7 @@ #include #include -static IGRAPH_THREAD_LOCAL igraph_status_handler_t *igraph_i_status_handler = 0; +static igraph_status_handler_t *igraph_i_status_handler = 0; /** * \function igraph_status diff --git a/src/rigraph/core/core/trie.c b/src/rigraph/core/core/trie.c index 1ea9687..184257f 100644 --- a/src/rigraph/core/core/trie.c +++ b/src/rigraph/core/core/trie.c @@ -26,7 +26,7 @@ #include "igraph_random.h" #include "igraph_error.h" -#include "core/trie.h" +#include "trie.h" #include "config.h" diff --git a/src/rigraph/core/core/vector.c b/src/rigraph/core/core/vector.c index 68512db..2b77e6a 100644 --- a/src/rigraph/core/core/vector.c +++ b/src/rigraph/core/core/vector.c @@ -70,9 +70,9 @@ #include "igraph_pmt_off.h" #undef BASE_COMPLEX -#include "core/math.h" +#include "../core/math.h" -#include "core/indheap.h" +#include "../core/indheap.h" /** * \ingroup vector diff --git a/src/rigraph/core/flow/flow.c b/src/rigraph/core/flow/flow.c index ff82d89..1169640 100644 --- a/src/rigraph/core/flow/flow.c +++ b/src/rigraph/core/flow/flow.c @@ -37,10 +37,10 @@ #include "igraph_structural.h" #include "igraph_topology.h" -#include "core/buckets.h" -#include "core/cutheap.h" -#include "core/interruption.h" -#include "core/math.h" +#include "../core/buckets.h" +#include "../core/cutheap.h" +#include "../core/interruption.h" +#include "../core/math.h" #include "config.h" diff --git a/src/rigraph/core/flow/st-cuts.c b/src/rigraph/core/flow/st-cuts.c index 1fa7a0a..f363038 100644 --- a/src/rigraph/core/flow/st-cuts.c +++ b/src/rigraph/core/flow/st-cuts.c @@ -34,11 +34,11 @@ #include "igraph_stack.h" #include "igraph_visitor.h" -#include "core/math.h" -#include "core/estack.h" -#include "core/marked_queue.h" -#include "graph/attributes.h" -#include "flow/flow_internal.h" +#include "../core/math.h" +#include "../core/estack.h" +#include "../core/marked_queue.h" +#include "../graph/attributes.h" +#include "flow_internal.h" typedef int igraph_provan_shier_pivot_t(const igraph_t *graph, const igraph_marked_queue_t *S, diff --git a/src/rigraph/core/games/barabasi.c b/src/rigraph/core/games/barabasi.c index 63eedbe..02dc616 100644 --- a/src/rigraph/core/games/barabasi.c +++ b/src/rigraph/core/games/barabasi.c @@ -30,7 +30,7 @@ #include "igraph_psumtree.h" #include "igraph_random.h" -#include "core/interruption.h" +#include "../core/interruption.h" static int igraph_i_barabasi_game_bag(igraph_t *graph, igraph_integer_t n, igraph_integer_t m, diff --git a/src/rigraph/core/games/correlated.c b/src/rigraph/core/games/correlated.c index 732d00b..7eb54e2 100644 --- a/src/rigraph/core/games/correlated.c +++ b/src/rigraph/core/games/correlated.c @@ -30,7 +30,7 @@ #include "igraph_random.h" #include "igraph_structural.h" -#include "core/interruption.h" +#include "../core/interruption.h" /* The "code" of an edge is a single index representing its location in the adjacency matrix, * More specifically, the relevant parts of the adjacency matrix (i.e. non-diagonal in directed, diff --git a/src/rigraph/core/games/degree_sequence.c b/src/rigraph/core/games/degree_sequence.c index b4e6dd4..5b67ae0 100644 --- a/src/rigraph/core/games/degree_sequence.c +++ b/src/rigraph/core/games/degree_sequence.c @@ -31,8 +31,8 @@ #include "igraph_random.h" #include "igraph_vector_ptr.h" -#include "core/interruption.h" -#include "core/set.h" +#include "../core/interruption.h" +#include "../core/set.h" static int igraph_i_degree_sequence_game_simple(igraph_t *graph, const igraph_vector_t *out_seq, diff --git a/src/rigraph/core/games/degree_sequence_vl/gengraph_definitions.h b/src/rigraph/core/games/degree_sequence_vl/gengraph_definitions.h index b52ebd1..2fdc72d 100644 --- a/src/rigraph/core/games/degree_sequence_vl/gengraph_definitions.h +++ b/src/rigraph/core/games/degree_sequence_vl/gengraph_definitions.h @@ -25,7 +25,7 @@ #include #include -#include "internal/hacks.h" +//#include "../../internal/hacks.h" namespace gengraph { diff --git a/src/rigraph/core/games/degree_sequence_vl/gengraph_graph_molloy_hash.cpp b/src/rigraph/core/games/degree_sequence_vl/gengraph_graph_molloy_hash.cpp index dc055a2..9722958 100644 --- a/src/rigraph/core/games/degree_sequence_vl/gengraph_graph_molloy_hash.cpp +++ b/src/rigraph/core/games/degree_sequence_vl/gengraph_graph_molloy_hash.cpp @@ -31,7 +31,7 @@ #include "gengraph_graph_molloy_hash.h" #include "config.h" -#include "core/math.h" +#include "../../core/math.h" #include "igraph_constructors.h" #include "igraph_error.h" #include "igraph_statusbar.h" diff --git a/src/rigraph/core/games/degree_sequence_vl/gengraph_mr-connected.cpp b/src/rigraph/core/games/degree_sequence_vl/gengraph_mr-connected.cpp index 0417c24..951523e 100644 --- a/src/rigraph/core/games/degree_sequence_vl/gengraph_mr-connected.cpp +++ b/src/rigraph/core/games/degree_sequence_vl/gengraph_mr-connected.cpp @@ -29,7 +29,7 @@ #include "igraph_types.h" #include "igraph_error.h" -#include "core/exceptions.h" +#include "../../core/exceptions.h" namespace gengraph { diff --git a/src/rigraph/core/games/forestfire.c b/src/rigraph/core/games/forestfire.c index a4d69bf..433cf46 100644 --- a/src/rigraph/core/games/forestfire.c +++ b/src/rigraph/core/games/forestfire.c @@ -29,7 +29,7 @@ #include "igraph_constructors.h" #include "igraph_dqueue.h" -#include "core/interruption.h" +#include "../core/interruption.h" typedef struct igraph_i_forest_fire_data_t { igraph_vector_t *inneis; diff --git a/src/rigraph/core/games/grg.c b/src/rigraph/core/games/grg.c index 797cd66..2f0173b 100644 --- a/src/rigraph/core/games/grg.c +++ b/src/rigraph/core/games/grg.c @@ -26,7 +26,7 @@ #include "igraph_constructors.h" #include "igraph_random.h" -#include "core/interruption.h" +#include "../core/interruption.h" /** * \function igraph_grg_game diff --git a/src/rigraph/core/games/preference.c b/src/rigraph/core/games/preference.c index ac987e0..554f7a6 100644 --- a/src/rigraph/core/games/preference.c +++ b/src/rigraph/core/games/preference.c @@ -27,7 +27,7 @@ #include "igraph_memory.h" #include "igraph_random.h" -#include "core/interruption.h" +#include "../core/interruption.h" static void igraph_i_preference_game_free_vids_by_type(igraph_vector_ptr_t *vecs) { int i = 0, n; diff --git a/src/rigraph/core/games/sbm.c b/src/rigraph/core/games/sbm.c index b2d75e8..cc95868 100644 --- a/src/rigraph/core/games/sbm.c +++ b/src/rigraph/core/games/sbm.c @@ -29,7 +29,7 @@ #include "igraph_constructors.h" #include "igraph_games.h" -#include "core/interruption.h" +#include "../core/interruption.h" #include /* for DBL_EPSILON */ #include /* for sqrt */ diff --git a/src/rigraph/core/games/static_fitness.c b/src/rigraph/core/games/static_fitness.c index 8a7d4c0..943149c 100644 --- a/src/rigraph/core/games/static_fitness.c +++ b/src/rigraph/core/games/static_fitness.c @@ -30,7 +30,7 @@ #include "igraph_progress.h" #include "igraph_random.h" -#include "core/interruption.h" +#include "../core/interruption.h" /** * \ingroup generators diff --git a/src/rigraph/core/graph/adjlist.c b/src/rigraph/core/graph/adjlist.c index e3da88d..9d1911e 100644 --- a/src/rigraph/core/graph/adjlist.c +++ b/src/rigraph/core/graph/adjlist.c @@ -25,7 +25,7 @@ #include "igraph_memory.h" #include "igraph_interface.h" -#include "core/interruption.h" +#include "../core/interruption.h" #include /* memset */ #include diff --git a/src/rigraph/core/graph/attributes.c b/src/rigraph/core/graph/attributes.c index ba40189..e1f53d9 100644 --- a/src/rigraph/core/graph/attributes.c +++ b/src/rigraph/core/graph/attributes.c @@ -24,7 +24,7 @@ #include "igraph_attributes.h" #include "igraph_memory.h" -#include "graph/attributes.h" +#include "attributes.h" #include "config.h" diff --git a/src/rigraph/core/graph/cattributes.c b/src/rigraph/core/graph/cattributes.c index 515c691..9520a2f 100644 --- a/src/rigraph/core/graph/cattributes.c +++ b/src/rigraph/core/graph/cattributes.c @@ -23,7 +23,7 @@ #include "igraph_attributes.h" #include "igraph_memory.h" -#include "core/math.h" +#include "../core/math.h" #include "igraph_interface.h" #include "igraph_random.h" diff --git a/src/rigraph/core/graph/type_indexededgelist.c b/src/rigraph/core/graph/type_indexededgelist.c index b5c94e8..9494408 100644 --- a/src/rigraph/core/graph/type_indexededgelist.c +++ b/src/rigraph/core/graph/type_indexededgelist.c @@ -25,8 +25,8 @@ #include "igraph_interface.h" #include "igraph_memory.h" -#include "graph/attributes.h" -#include "graph/neighbors.h" +#include "attributes.h" +#include "neighbors.h" /* Internal functions */ diff --git a/src/rigraph/core/hrg/dendro.h b/src/rigraph/core/hrg/dendro.h index a2d092b..869801d 100644 --- a/src/rigraph/core/hrg/dendro.h +++ b/src/rigraph/core/hrg/dendro.h @@ -65,9 +65,9 @@ #ifndef IGRAPH_HRG_DENDRO #define IGRAPH_HRG_DENDRO -#include "hrg/graph.h" -#include "hrg/rbtree.h" -#include "hrg/splittree_eq.h" +#include "graph.h" +#include "rbtree.h" +#include "splittree_eq.h" #include "igraph_hrg.h" diff --git a/src/rigraph/core/hrg/graph.h b/src/rigraph/core/hrg/graph.h index f990f93..4eee422 100644 --- a/src/rigraph/core/hrg/graph.h +++ b/src/rigraph/core/hrg/graph.h @@ -61,7 +61,7 @@ #ifndef IGRAPH_HRG_GRAPH #define IGRAPH_HRG_GRAPH -#include "hrg/rbtree.h" +#include "rbtree.h" #include #include diff --git a/src/rigraph/core/hrg/graph_simp.h b/src/rigraph/core/hrg/graph_simp.h index f14fa78..3859543 100644 --- a/src/rigraph/core/hrg/graph_simp.h +++ b/src/rigraph/core/hrg/graph_simp.h @@ -60,8 +60,8 @@ #ifndef IGRAPH_HRG_SIMPLEGRAPH #define IGRAPH_HRG_SIMPLEGRAPH -#include "hrg/rbtree.h" -#include "hrg/dendro.h" +#include "rbtree.h" +#include "dendro.h" #include #include diff --git a/src/rigraph/core/hrg/hrg.cc b/src/rigraph/core/hrg/hrg.cc index d98d596..d56e54b 100644 --- a/src/rigraph/core/hrg/hrg.cc +++ b/src/rigraph/core/hrg/hrg.cc @@ -27,9 +27,9 @@ #include "igraph_hrg.h" #include "igraph_random.h" -#include "hrg/dendro.h" -#include "hrg/graph.h" -#include "hrg/graph_simp.h" +#include "dendro.h" +#include "graph.h" +#include "graph_simp.h" using namespace fitHRG; diff --git a/src/rigraph/core/hrg/hrg_types.cc b/src/rigraph/core/hrg/hrg_types.cc index 467c2b9..33e7271 100644 --- a/src/rigraph/core/hrg/hrg_types.cc +++ b/src/rigraph/core/hrg/hrg_types.cc @@ -31,11 +31,11 @@ // // *********************************************************************** -#include "hrg/rbtree.h" -#include "hrg/dendro.h" -#include "hrg/graph.h" -#include "hrg/splittree_eq.h" -#include "hrg/graph_simp.h" +#include "rbtree.h" +#include "dendro.h" +#include "graph.h" +#include "splittree_eq.h" +#include "graph_simp.h" #include "igraph_hrg.h" #include "igraph_constructors.h" diff --git a/src/rigraph/core/internal/glpk_support.c b/src/rigraph/core/internal/glpk_support.c index 257f218..341df62 100644 --- a/src/rigraph/core/internal/glpk_support.c +++ b/src/rigraph/core/internal/glpk_support.c @@ -22,13 +22,13 @@ */ -#include "internal/glpk_support.h" +#include "glpk_support.h" #ifdef HAVE_GLPK #include "igraph_error.h" -#include "core/interruption.h" +#include "../core/interruption.h" #include diff --git a/src/rigraph/core/internal/hacks.c b/src/rigraph/core/internal/hacks.c index d6653e0..951189a 100644 --- a/src/rigraph/core/internal/hacks.c +++ b/src/rigraph/core/internal/hacks.c @@ -21,7 +21,7 @@ */ -#include "internal/hacks.h" +//#include "hacks.h" #include #include diff --git a/src/rigraph/core/internal/zeroin.c b/src/rigraph/core/internal/zeroin.c index 3d1d8d0..b6a15ee 100644 --- a/src/rigraph/core/internal/zeroin.c +++ b/src/rigraph/core/internal/zeroin.c @@ -82,7 +82,7 @@ #include "igraph_nongraph.h" #include "igraph_types.h" -#include "core/interruption.h" +#include "../core/interruption.h" #include #include diff --git a/src/rigraph/core/io/dimacs.c b/src/rigraph/core/io/dimacs.c index c615b26..c515847 100644 --- a/src/rigraph/core/io/dimacs.c +++ b/src/rigraph/core/io/dimacs.c @@ -26,7 +26,7 @@ #include "igraph_interface.h" #include "igraph_iterators.h" -#include "core/interruption.h" +#include "../core/interruption.h" #include diff --git a/src/rigraph/core/io/dl-header.h b/src/rigraph/core/io/dl-header.h index fe4d340..1b5b141 100644 --- a/src/rigraph/core/io/dl-header.h +++ b/src/rigraph/core/io/dl-header.h @@ -23,7 +23,7 @@ #include "igraph_error.h" #include "igraph_types.h" -#include "core/trie.h" +#include "../core/trie.h" typedef enum { IGRAPH_DL_MATRIX, IGRAPH_DL_EDGELIST1, IGRAPH_DL_NODELIST1 diff --git a/src/rigraph/core/io/dl-lexer.c b/src/rigraph/core/io/dl-lexer.c index e4ae5d6..1f6a90b 100644 --- a/src/rigraph/core/io/dl-lexer.c +++ b/src/rigraph/core/io/dl-lexer.c @@ -810,8 +810,8 @@ static const flex_int16_t yy_chk[318] = #include #include -#include "io/dl-header.h" -#include "io/dl-parser.h" +#include "dl-header.h" +#include "dl-parser.h" #define YY_EXTRA_TYPE igraph_i_dl_parsedata_t* #define YY_USER_ACTION yylloc->first_line = yylineno; diff --git a/src/rigraph/core/io/dl-lexer.l b/src/rigraph/core/io/dl-lexer.l index a1f6974..7fa6ec8 100644 --- a/src/rigraph/core/io/dl-lexer.l +++ b/src/rigraph/core/io/dl-lexer.l @@ -48,8 +48,8 @@ #include #include -#include "io/dl-header.h" -#include "io/parsers/dl-parser.h" +#include "dl-header.h" +#include "parsers/dl-parser.h" #define YY_EXTRA_TYPE igraph_i_dl_parsedata_t* #define YY_USER_ACTION yylloc->first_line = yylineno; diff --git a/src/rigraph/core/io/dl-parser.c b/src/rigraph/core/io/dl-parser.c index 08e822a..312388d 100644 --- a/src/rigraph/core/io/dl-parser.c +++ b/src/rigraph/core/io/dl-parser.c @@ -98,11 +98,11 @@ #include "config.h" -#include "core/math.h" -#include "internal/hacks.h" -#include "io/dl-header.h" -#include "io/dl-parser.h" -#include "io/dl-lexer.h" +#include "../core/math.h" +//#include "../internal/hacks.h" +#include "dl-header.h" +#include "dl-parser.h" +#include "dl-lexer.h" #include diff --git a/src/rigraph/core/io/dl-parser.y b/src/rigraph/core/io/dl-parser.y index 557ff9a..db51de0 100644 --- a/src/rigraph/core/io/dl-parser.y +++ b/src/rigraph/core/io/dl-parser.y @@ -46,11 +46,11 @@ #include "config.h" -#include "core/math.h" -#include "internal/hacks.h" -#include "io/dl-header.h" -#include "io/parsers/dl-parser.h" -#include "io/parsers/dl-lexer.h" +#include "../core/math.h" +#include "../internal/hacks.h" +#include "../io/dl-header.h" +#include "../io/parsers/dl-parser.h" +#include "../io/parsers/dl-lexer.h" #include diff --git a/src/rigraph/core/io/dl.c b/src/rigraph/core/io/dl.c index 26e1347..8782ffc 100644 --- a/src/rigraph/core/io/dl.c +++ b/src/rigraph/core/io/dl.c @@ -25,7 +25,7 @@ #include "igraph_attributes.h" #include "igraph_interface.h" -#include "io/dl-header.h" +#include "dl-header.h" int igraph_dl_yylex_init_extra (igraph_i_dl_parsedata_t* user_defined, void* scanner); diff --git a/src/rigraph/core/io/dot.c b/src/rigraph/core/io/dot.c index b3ef3bc..f76cae8 100644 --- a/src/rigraph/core/io/dot.c +++ b/src/rigraph/core/io/dot.c @@ -28,8 +28,8 @@ #include "igraph_memory.h" #include "igraph_version.h" -#include "graph/attributes.h" -#include "internal/hacks.h" /* strcasecmp */ +#include "../graph/attributes.h" +//#include "internal/hacks.h" /* strcasecmp */ #include #include diff --git a/src/rigraph/core/io/edgelist.c b/src/rigraph/core/io/edgelist.c index 4eef988..9ce2f37 100644 --- a/src/rigraph/core/io/edgelist.c +++ b/src/rigraph/core/io/edgelist.c @@ -26,7 +26,7 @@ #include "igraph_interface.h" #include "igraph_iterators.h" -#include "core/interruption.h" +#include "../core/interruption.h" #include diff --git a/src/rigraph/core/io/gml-header.h b/src/rigraph/core/io/gml-header.h index 618db87..1a55365 100644 --- a/src/rigraph/core/io/gml-header.h +++ b/src/rigraph/core/io/gml-header.h @@ -22,7 +22,7 @@ #include "igraph_error.h" -#include "io/gml-tree.h" +#include "gml-tree.h" typedef struct { void *scanner; diff --git a/src/rigraph/core/io/gml-lexer.c b/src/rigraph/core/io/gml-lexer.c index a5ea7fc..717503d 100644 --- a/src/rigraph/core/io/gml-lexer.c +++ b/src/rigraph/core/io/gml-lexer.c @@ -719,8 +719,8 @@ static const flex_int16_t yy_chk[61] = #include "config.h" #include -#include "io/gml-header.h" -#include "io/gml-parser.h" +#include "gml-header.h" +#include "gml-parser.h" #define YY_EXTRA_TYPE igraph_i_gml_parsedata_t* #define YY_USER_ACTION yylloc->first_line = yylineno; diff --git a/src/rigraph/core/io/gml-lexer.l b/src/rigraph/core/io/gml-lexer.l index 0acd8fa..3078dd7 100644 --- a/src/rigraph/core/io/gml-lexer.l +++ b/src/rigraph/core/io/gml-lexer.l @@ -47,8 +47,8 @@ #include "config.h" #include -#include "io/gml-header.h" -#include "io/parsers/gml-parser.h" +#include "gml-header.h" +#include "gml-parser.h" #define YY_EXTRA_TYPE igraph_i_gml_parsedata_t* #define YY_USER_ACTION yylloc->first_line = yylineno; diff --git a/src/rigraph/core/io/gml-parser.c b/src/rigraph/core/io/gml-parser.c index 3d0b526..e60758a 100644 --- a/src/rigraph/core/io/gml-parser.c +++ b/src/rigraph/core/io/gml-parser.c @@ -104,12 +104,12 @@ #include "igraph_memory.h" #include "config.h" -#include "core/math.h" -#include "io/gml-header.h" -#include "io/gml-tree.h" -#include "io/gml-parser.h" -#include "io/gml-lexer.h" -#include "internal/hacks.h" /* strcasecmp */ +#include "../core/math.h" +#include "gml-header.h" +#include "gml-tree.h" +#include "gml-parser.h" +#include "gml-lexer.h" +//#include "internal/hacks.h" /* strcasecmp */ int igraph_gml_yyerror(YYLTYPE* locp, igraph_i_gml_parsedata_t *context, const char *s); diff --git a/src/rigraph/core/io/gml-tree.c b/src/rigraph/core/io/gml-tree.c index 8d0fe07..8e798d1 100644 --- a/src/rigraph/core/io/gml-tree.c +++ b/src/rigraph/core/io/gml-tree.c @@ -23,7 +23,7 @@ #include "igraph_memory.h" #include "igraph_error.h" -#include "io/gml-tree.h" +#include "gml-tree.h" #include diff --git a/src/rigraph/core/io/gml.c b/src/rigraph/core/io/gml.c index 2b09eb3..83d2f9b 100644 --- a/src/rigraph/core/io/gml.c +++ b/src/rigraph/core/io/gml.c @@ -27,9 +27,9 @@ #include "igraph_memory.h" #include "igraph_version.h" -#include "core/trie.h" -#include "graph/attributes.h" -#include "io/gml-header.h" +#include "../core/trie.h" +#include "../graph/attributes.h" +#include "gml-header.h" #include #include diff --git a/src/rigraph/core/io/graphdb.c b/src/rigraph/core/io/graphdb.c index 21e1d4b..a0bdef1 100644 --- a/src/rigraph/core/io/graphdb.c +++ b/src/rigraph/core/io/graphdb.c @@ -24,7 +24,7 @@ #include "igraph_constructors.h" -#include "core/trie.h" +#include "../core/trie.h" static int igraph_i_read_graph_graphdb_getword(FILE *instream) { int b1, b2; diff --git a/src/rigraph/core/io/graphml.c b/src/rigraph/core/io/graphml.c index ba361cd..ff838bd 100644 --- a/src/rigraph/core/io/graphml.c +++ b/src/rigraph/core/io/graphml.c @@ -26,10 +26,10 @@ #include "igraph_interface.h" #include "igraph_memory.h" -#include "core/math.h" -#include "core/trie.h" -#include "graph/attributes.h" -#include "internal/hacks.h" /* strcasecmp */ +#include "../core/math.h" +#include "../core/trie.h" +#include "../graph/attributes.h" +//#include "internal/hacks.h" /* strcasecmp */ #include "config.h" diff --git a/src/rigraph/core/io/leda.c b/src/rigraph/core/io/leda.c index b33f286..ac6295b 100644 --- a/src/rigraph/core/io/leda.c +++ b/src/rigraph/core/io/leda.c @@ -26,7 +26,7 @@ #include "igraph_interface.h" #include "igraph_iterators.h" -#include "graph/attributes.h" +#include "../graph/attributes.h" #include diff --git a/src/rigraph/core/io/lgl-header.h b/src/rigraph/core/io/lgl-header.h index fdb8f58..b624171 100644 --- a/src/rigraph/core/io/lgl-header.h +++ b/src/rigraph/core/io/lgl-header.h @@ -23,7 +23,7 @@ #include "igraph_error.h" #include "igraph_vector.h" -#include "core/trie.h" +#include "../core/trie.h" typedef struct { void *scanner; diff --git a/src/rigraph/core/io/lgl-lexer.c b/src/rigraph/core/io/lgl-lexer.c index cb35922..0f75ccf 100644 --- a/src/rigraph/core/io/lgl-lexer.c +++ b/src/rigraph/core/io/lgl-lexer.c @@ -705,8 +705,8 @@ static const flex_int16_t yy_chk[19] = #include "config.h" #include -#include "io/lgl-header.h" -#include "io/lgl-parser.h" +#include "lgl-header.h" +#include "lgl-parser.h" #define YY_EXTRA_TYPE igraph_i_lgl_parsedata_t* #define YY_USER_ACTION yylloc->first_line = yylineno; diff --git a/src/rigraph/core/io/lgl-lexer.l b/src/rigraph/core/io/lgl-lexer.l index e1ed9ac..7746b2a 100644 --- a/src/rigraph/core/io/lgl-lexer.l +++ b/src/rigraph/core/io/lgl-lexer.l @@ -47,8 +47,8 @@ #include "config.h" #include -#include "io/lgl-header.h" -#include "io/parsers/lgl-parser.h" +#include "lgl-header.h" +#include "lgl-parser.h" #define YY_EXTRA_TYPE igraph_i_lgl_parsedata_t* #define YY_USER_ACTION yylloc->first_line = yylineno; diff --git a/src/rigraph/core/io/lgl-parser.c b/src/rigraph/core/io/lgl-parser.c index 6dfae34..c370788 100644 --- a/src/rigraph/core/io/lgl-parser.c +++ b/src/rigraph/core/io/lgl-parser.c @@ -104,11 +104,11 @@ #include "igraph_error.h" #include "config.h" -#include "core/math.h" -#include "io/lgl-header.h" -#include "io/lgl-parser.h" -#include "io/lgl-lexer.h" -#include "internal/hacks.h" +#include "../core/math.h" +#include "lgl-header.h" +#include "lgl-parser.h" +#include "lgl-lexer.h" +//#include "internal/hacks.h" int igraph_lgl_yyerror(YYLTYPE* locp, igraph_i_lgl_parsedata_t *context, const char *s); diff --git a/src/rigraph/core/io/lgl-parser.y b/src/rigraph/core/io/lgl-parser.y index 2bc834c..7043594 100644 --- a/src/rigraph/core/io/lgl-parser.y +++ b/src/rigraph/core/io/lgl-parser.y @@ -52,11 +52,11 @@ #include "igraph_error.h" #include "config.h" -#include "core/math.h" -#include "io/lgl-header.h" -#include "io/parsers/lgl-parser.h" -#include "io/parsers/lgl-lexer.h" -#include "internal/hacks.h" +#include "../core/math.h" +#include "lgl-header.h" +#include "lgl-parser.h" +#include "lgl-lexer.h" +//#include "internal/hacks.h" int igraph_lgl_yyerror(YYLTYPE* locp, igraph_i_lgl_parsedata_t *context, const char *s); diff --git a/src/rigraph/core/io/lgl.c b/src/rigraph/core/io/lgl.c index 5d66bfc..ab251ba 100644 --- a/src/rigraph/core/io/lgl.c +++ b/src/rigraph/core/io/lgl.c @@ -25,7 +25,7 @@ #include "igraph_attributes.h" #include "igraph_interface.h" -#include "graph/attributes.h" +#include "../graph/attributes.h" #include "lgl-header.h" diff --git a/src/rigraph/core/io/ncol-header.h b/src/rigraph/core/io/ncol-header.h index 2ed06f2..c6e9129 100644 --- a/src/rigraph/core/io/ncol-header.h +++ b/src/rigraph/core/io/ncol-header.h @@ -23,7 +23,7 @@ #include "igraph_error.h" #include "igraph_vector.h" -#include "core/trie.h" +#include "../core/trie.h" typedef struct { void *scanner; diff --git a/src/rigraph/core/io/ncol-lexer.c b/src/rigraph/core/io/ncol-lexer.c index bfab00c..d8190ea 100644 --- a/src/rigraph/core/io/ncol-lexer.c +++ b/src/rigraph/core/io/ncol-lexer.c @@ -705,8 +705,8 @@ static const flex_int16_t yy_chk[17] = #include "config.h" #include -#include "io/ncol-header.h" -#include "io/ncol-parser.h" +#include "ncol-header.h" +#include "ncol-parser.h" #define YY_EXTRA_TYPE igraph_i_ncol_parsedata_t* #define YY_USER_ACTION yylloc->first_line = yylineno; diff --git a/src/rigraph/core/io/ncol-lexer.l b/src/rigraph/core/io/ncol-lexer.l index a2ed8d3..e2be4bb 100644 --- a/src/rigraph/core/io/ncol-lexer.l +++ b/src/rigraph/core/io/ncol-lexer.l @@ -47,8 +47,8 @@ #include "config.h" #include -#include "io/ncol-header.h" -#include "io/parsers/ncol-parser.h" +#include "ncol-header.h" +#include "ncol-parser.h" #define YY_EXTRA_TYPE igraph_i_ncol_parsedata_t* #define YY_USER_ACTION yylloc->first_line = yylineno; diff --git a/src/rigraph/core/io/ncol-parser.c b/src/rigraph/core/io/ncol-parser.c index e54271f..b1eb7d2 100644 --- a/src/rigraph/core/io/ncol-parser.c +++ b/src/rigraph/core/io/ncol-parser.c @@ -104,11 +104,11 @@ #include "igraph_error.h" #include "config.h" -#include "core/math.h" -#include "io/ncol-header.h" -#include "io/ncol-parser.h" -#include "io/ncol-lexer.h" -#include "internal/hacks.h" +#include "../core/math.h" +#include "ncol-header.h" +#include "ncol-parser.h" +#include "ncol-lexer.h" +//#include "internal/hacks.h" int igraph_ncol_yyerror(YYLTYPE* locp, igraph_i_ncol_parsedata_t *context, diff --git a/src/rigraph/core/io/ncol-parser.y b/src/rigraph/core/io/ncol-parser.y index db833a8..e9a20bf 100644 --- a/src/rigraph/core/io/ncol-parser.y +++ b/src/rigraph/core/io/ncol-parser.y @@ -52,11 +52,11 @@ #include "igraph_error.h" #include "config.h" -#include "core/math.h" -#include "io/ncol-header.h" -#include "io/parsers/ncol-parser.h" -#include "io/parsers/ncol-lexer.h" -#include "internal/hacks.h" +#include "../core/math.h" +#include "ncol-header.h" +#include "ncol-parser.h" +#include "ncol-lexer.h" +//#include "internal/hacks.h" int igraph_ncol_yyerror(YYLTYPE* locp, igraph_i_ncol_parsedata_t *context, diff --git a/src/rigraph/core/io/ncol.c b/src/rigraph/core/io/ncol.c index 9d6aa16..ee657de 100644 --- a/src/rigraph/core/io/ncol.c +++ b/src/rigraph/core/io/ncol.c @@ -25,7 +25,7 @@ #include "igraph_attributes.h" #include "igraph_interface.h" -#include "graph/attributes.h" +#include "../graph/attributes.h" #include "ncol-header.h" diff --git a/src/rigraph/core/io/pajek-header.h b/src/rigraph/core/io/pajek-header.h index 92caae4..aebd338 100644 --- a/src/rigraph/core/io/pajek-header.h +++ b/src/rigraph/core/io/pajek-header.h @@ -24,7 +24,7 @@ #include "igraph_vector.h" #include "igraph_vector_ptr.h" -#include "core/trie.h" +#include "../core/trie.h" typedef struct { void *scanner; diff --git a/src/rigraph/core/io/pajek-lexer.c b/src/rigraph/core/io/pajek-lexer.c index 1e9d6dd..8592591 100644 --- a/src/rigraph/core/io/pajek-lexer.c +++ b/src/rigraph/core/io/pajek-lexer.c @@ -826,8 +826,8 @@ static const flex_int16_t yy_chk[339] = #include "config.h" #include -#include "io/pajek-header.h" -#include "io/pajek-parser.h" +#include "pajek-header.h" +#include "pajek-parser.h" #define YY_EXTRA_TYPE igraph_i_pajek_parsedata_t* #define YY_USER_ACTION yylloc->first_line = yylineno; diff --git a/src/rigraph/core/io/pajek-lexer.l b/src/rigraph/core/io/pajek-lexer.l index 4ba29dc..e73b71e 100644 --- a/src/rigraph/core/io/pajek-lexer.l +++ b/src/rigraph/core/io/pajek-lexer.l @@ -47,8 +47,8 @@ #include "config.h" #include -#include "io/pajek-header.h" -#include "io/parsers/pajek-parser.h" +#include "pajek-header.h" +#include "pajek-parser.h" #define YY_EXTRA_TYPE igraph_i_pajek_parsedata_t* #define YY_USER_ACTION yylloc->first_line = yylineno; diff --git a/src/rigraph/core/io/pajek-parser.c b/src/rigraph/core/io/pajek-parser.c index 960009b..c1659c4 100644 --- a/src/rigraph/core/io/pajek-parser.c +++ b/src/rigraph/core/io/pajek-parser.c @@ -106,11 +106,11 @@ #include "igraph_attributes.h" #include "config.h" -#include "core/math.h" -#include "io/pajek-header.h" -#include "io/pajek-parser.h" /* it must come first because of YYSTYPE */ -#include "io/pajek-lexer.h" -#include "internal/hacks.h" +#include "../core/math.h" +#include "pajek-header.h" +#include "pajek-parser.h" /* it must come first because of YYSTYPE */ +#include "pajek-lexer.h" +//#include "internal/hacks.h" int igraph_pajek_yyerror(YYLTYPE* locp, igraph_i_pajek_parsedata_t *context, diff --git a/src/rigraph/core/io/pajek-parser.y b/src/rigraph/core/io/pajek-parser.y index e181ea0..e57c9e6 100644 --- a/src/rigraph/core/io/pajek-parser.y +++ b/src/rigraph/core/io/pajek-parser.y @@ -54,11 +54,11 @@ #include "igraph_attributes.h" #include "config.h" -#include "core/math.h" -#include "io/pajek-header.h" -#include "io/parsers/pajek-parser.h" /* it must come first because of YYSTYPE */ -#include "io/parsers/pajek-lexer.h" -#include "internal/hacks.h" +#include "../core/math.h" +#include "pajek-header.h" +#include "pajek-parser.h" /* it must come first because of YYSTYPE */ +#include "pajek-lexer.h" +//#include "internal/hacks.h" int igraph_pajek_yyerror(YYLTYPE* locp, igraph_i_pajek_parsedata_t *context, diff --git a/src/rigraph/core/io/pajek.c b/src/rigraph/core/io/pajek.c index 2a7240c..998352b 100644 --- a/src/rigraph/core/io/pajek.c +++ b/src/rigraph/core/io/pajek.c @@ -27,7 +27,7 @@ #include "igraph_interface.h" #include "igraph_memory.h" -#include "graph/attributes.h" +#include "../graph/attributes.h" #include "pajek-header.h" diff --git a/src/rigraph/core/isomorphism/bliss.cc b/src/rigraph/core/isomorphism/bliss.cc index 2439f62..f7611a2 100644 --- a/src/rigraph/core/isomorphism/bliss.cc +++ b/src/rigraph/core/isomorphism/bliss.cc @@ -27,7 +27,7 @@ #include "igraph_vector.h" #include "igraph_vector_ptr.h" -#include "core/exceptions.h" +#include "../core/exceptions.h" using namespace bliss; using namespace std; diff --git a/src/rigraph/core/isomorphism/bliss/bignum.hh b/src/rigraph/core/isomorphism/bliss/bignum.hh index 546da5b..0e4ec82 100644 --- a/src/rigraph/core/isomorphism/bliss/bignum.hh +++ b/src/rigraph/core/isomorphism/bliss/bignum.hh @@ -23,7 +23,7 @@ #define BLISS_USE_GMP #if defined(BLISS_USE_GMP) -#include "internal/gmp_internal.h" +#include "../../internal/gmp_internal.h" #endif #include diff --git a/src/rigraph/core/isomorphism/isoclasses.c b/src/rigraph/core/isomorphism/isoclasses.c index d2a4eca..56750d0 100644 --- a/src/rigraph/core/isomorphism/isoclasses.c +++ b/src/rigraph/core/isomorphism/isoclasses.c @@ -24,7 +24,7 @@ #include "igraph_constructors.h" #include "igraph_interface.h" -#include "isomorphism/isoclasses.h" +#include "isoclasses.h" /* * Small labelled graphs are encoded into a compact representation, a "code", diff --git a/src/rigraph/core/isomorphism/lad.c b/src/rigraph/core/isomorphism/lad.c index d6be665..82f9a2f 100644 --- a/src/rigraph/core/isomorphism/lad.c +++ b/src/rigraph/core/isomorphism/lad.c @@ -55,7 +55,7 @@ #include "igraph_matrix.h" #include "igraph_qsort.h" -#include "core/interruption.h" +#include "../core/interruption.h" #include #include diff --git a/src/rigraph/core/isomorphism/vf2.c b/src/rigraph/core/isomorphism/vf2.c index ade96f5..33ac251 100644 --- a/src/rigraph/core/isomorphism/vf2.c +++ b/src/rigraph/core/isomorphism/vf2.c @@ -28,7 +28,7 @@ #include "igraph_memory.h" #include "igraph_stack.h" -#include "core/interruption.h" +#include "../core/interruption.h" /** * \section about_vf2 diff --git a/src/rigraph/core/layout/circular.c b/src/rigraph/core/layout/circular.c index 9a8c4ae..1a5eab0 100644 --- a/src/rigraph/core/layout/circular.c +++ b/src/rigraph/core/layout/circular.c @@ -25,8 +25,8 @@ #include "igraph_interface.h" -#include "core/interruption.h" -#include "core/math.h" +#include "../core/interruption.h" +#include "../core/math.h" /** * \ingroup layout diff --git a/src/rigraph/core/layout/davidson_harel.c b/src/rigraph/core/layout/davidson_harel.c index 57b706f..78ae484 100644 --- a/src/rigraph/core/layout/davidson_harel.c +++ b/src/rigraph/core/layout/davidson_harel.c @@ -26,9 +26,9 @@ #include "igraph_interface.h" #include "igraph_random.h" -#include "core/math.h" -#include "core/interruption.h" -#include "layout/layout_internal.h" +#include "../core/math.h" +#include "../core/interruption.h" +#include "layout_internal.h" #include diff --git a/src/rigraph/core/layout/drl/drl_graph.cpp b/src/rigraph/core/layout/drl/drl_graph.cpp index 71a8796..25427f1 100644 --- a/src/rigraph/core/layout/drl/drl_graph.cpp +++ b/src/rigraph/core/layout/drl/drl_graph.cpp @@ -43,7 +43,7 @@ using namespace std; #include "igraph_random.h" #include "igraph_interface.h" #include "igraph_progress.h" -#include "core/interruption.h" +#include "../../core/interruption.h" #ifdef MUSE_MPI #include #endif diff --git a/src/rigraph/core/layout/drl/drl_graph_3d.cpp b/src/rigraph/core/layout/drl/drl_graph_3d.cpp index 98d11a0..7eb4a32 100644 --- a/src/rigraph/core/layout/drl/drl_graph_3d.cpp +++ b/src/rigraph/core/layout/drl/drl_graph_3d.cpp @@ -42,7 +42,7 @@ using namespace std; #include "igraph_random.h" #include "igraph_interface.h" #include "igraph_progress.h" -#include "core/interruption.h" +#include "../../core/interruption.h" #ifdef MUSE_MPI #include #endif diff --git a/src/rigraph/core/layout/drl/drl_layout.cpp b/src/rigraph/core/layout/drl/drl_layout.cpp index 7a4296e..92f3b0f 100644 --- a/src/rigraph/core/layout/drl/drl_layout.cpp +++ b/src/rigraph/core/layout/drl/drl_layout.cpp @@ -65,7 +65,7 @@ using namespace drl; #include "igraph_random.h" #include "igraph_interface.h" -#include "core/exceptions.h" +#include "../../core/exceptions.h" namespace drl { diff --git a/src/rigraph/core/layout/drl/drl_layout_3d.cpp b/src/rigraph/core/layout/drl/drl_layout_3d.cpp index 7a922f0..0df4fe7 100644 --- a/src/rigraph/core/layout/drl/drl_layout_3d.cpp +++ b/src/rigraph/core/layout/drl/drl_layout_3d.cpp @@ -65,7 +65,7 @@ using namespace drl3d; #include "igraph_random.h" #include "igraph_interface.h" -#include "core/exceptions.h" +#include "../../core/exceptions.h" /** * \function igraph_layout_drl_3d diff --git a/src/rigraph/core/layout/fruchterman_reingold.c b/src/rigraph/core/layout/fruchterman_reingold.c index 98b5772..307b5fe 100644 --- a/src/rigraph/core/layout/fruchterman_reingold.c +++ b/src/rigraph/core/layout/fruchterman_reingold.c @@ -27,8 +27,8 @@ #include "igraph_interface.h" #include "igraph_components.h" -#include "core/grid.h" -#include "core/interruption.h" +#include "../core/grid.h" +#include "../core/interruption.h" static int igraph_layout_i_fr(const igraph_t *graph, igraph_matrix_t *res, diff --git a/src/rigraph/core/layout/gem.c b/src/rigraph/core/layout/gem.c index cd49656..b2e4288 100644 --- a/src/rigraph/core/layout/gem.c +++ b/src/rigraph/core/layout/gem.c @@ -26,8 +26,8 @@ #include "igraph_interface.h" #include "igraph_random.h" -#include "core/math.h" -#include "core/interruption.h" +#include "../core/math.h" +#include "../core/interruption.h" /** * \ingroup layout diff --git a/src/rigraph/core/layout/graphopt.c b/src/rigraph/core/layout/graphopt.c index 1668932..e72b6a1 100644 --- a/src/rigraph/core/layout/graphopt.c +++ b/src/rigraph/core/layout/graphopt.c @@ -26,7 +26,7 @@ #include "igraph_interface.h" #include "igraph_progress.h" -#include "core/interruption.h" +#include "../core/interruption.h" #define COULOMBS_CONSTANT 8987500000.0 diff --git a/src/rigraph/core/layout/kamada_kawai.c b/src/rigraph/core/layout/kamada_kawai.c index 49143f8..5db2e9b 100644 --- a/src/rigraph/core/layout/kamada_kawai.c +++ b/src/rigraph/core/layout/kamada_kawai.c @@ -27,7 +27,7 @@ #include "igraph_paths.h" #include "igraph_random.h" -#include "core/interruption.h" +#include "../core/interruption.h" /** * \ingroup layout diff --git a/src/rigraph/core/layout/large_graph.c b/src/rigraph/core/layout/large_graph.c index 9dfd518..cd7cddb 100644 --- a/src/rigraph/core/layout/large_graph.c +++ b/src/rigraph/core/layout/large_graph.c @@ -30,9 +30,9 @@ #include "igraph_structural.h" #include "igraph_visitor.h" -#include "core/grid.h" -#include "core/interruption.h" -#include "core/math.h" +#include "../core/grid.h" +#include "../core/interruption.h" +#include "../core/math.h" static void igraph_i_norm2d(igraph_real_t *x, igraph_real_t *y) { igraph_real_t len = sqrt((*x) * (*x) + (*y) * (*y)); diff --git a/src/rigraph/core/layout/layout_internal.h b/src/rigraph/core/layout/layout_internal.h index a6647d2..bfc52ec 100644 --- a/src/rigraph/core/layout/layout_internal.h +++ b/src/rigraph/core/layout/layout_internal.h @@ -25,7 +25,7 @@ #include "igraph_types.h" -#include "layout/merge_grid.h" +#include "merge_grid.h" __BEGIN_DECLS diff --git a/src/rigraph/core/layout/merge_dla.c b/src/rigraph/core/layout/merge_dla.c index bdfe338..5e15830 100644 --- a/src/rigraph/core/layout/merge_dla.c +++ b/src/rigraph/core/layout/merge_dla.c @@ -25,11 +25,11 @@ #include "igraph_progress.h" #include "igraph_random.h" -#include "core/grid.h" -#include "core/interruption.h" -#include "core/math.h" -#include "layout/merge_grid.h" -#include "layout/layout_internal.h" +#include "../core/grid.h" +#include "../core/interruption.h" +#include "../core/math.h" +#include "merge_grid.h" +#include "layout_internal.h" /** * \function igraph_layout_merge_dla diff --git a/src/rigraph/core/layout/merge_grid.c b/src/rigraph/core/layout/merge_grid.c index 5b7d94b..f7f6538 100644 --- a/src/rigraph/core/layout/merge_grid.c +++ b/src/rigraph/core/layout/merge_grid.c @@ -22,7 +22,7 @@ #include "igraph_memory.h" -#include "layout/merge_grid.h" +#include "merge_grid.h" static int igraph_i_layout_mergegrid_which(igraph_i_layout_mergegrid_t *grid, igraph_real_t xc, igraph_real_t yc, diff --git a/src/rigraph/core/layout/reingold_tilford.c b/src/rigraph/core/layout/reingold_tilford.c index 7d10180..5edb013 100644 --- a/src/rigraph/core/layout/reingold_tilford.c +++ b/src/rigraph/core/layout/reingold_tilford.c @@ -32,7 +32,7 @@ #include "igraph_progress.h" #include "igraph_structural.h" -#include "core/math.h" +#include "../core/math.h" static int igraph_i_layout_reingold_tilford_unreachable( const igraph_t *graph, diff --git a/src/rigraph/core/layout/sugiyama.c b/src/rigraph/core/layout/sugiyama.c index a046e11..8e9740e 100644 --- a/src/rigraph/core/layout/sugiyama.c +++ b/src/rigraph/core/layout/sugiyama.c @@ -34,8 +34,8 @@ #include "igraph_structural.h" #include "igraph_types.h" -#include "internal/glpk_support.h" -#include "misc/feedback_arc_set.h" +#include "../internal/glpk_support.h" +#include "../misc/feedback_arc_set.h" #include "config.h" diff --git a/src/rigraph/core/linalg/arpack.c b/src/rigraph/core/linalg/arpack.c index e8156a9..b9bbbb9 100644 --- a/src/rigraph/core/linalg/arpack.c +++ b/src/rigraph/core/linalg/arpack.c @@ -25,7 +25,7 @@ #include "igraph_arpack.h" #include "igraph_memory.h" -#include "linalg/arpack_internal.h" +#include "arpack_internal.h" #include #include diff --git a/src/rigraph/core/linalg/blas.c b/src/rigraph/core/linalg/blas.c index 8a4d017..bc625c5 100644 --- a/src/rigraph/core/linalg/blas.c +++ b/src/rigraph/core/linalg/blas.c @@ -25,7 +25,7 @@ #include "igraph_error.h" #include "igraph_blas.h" -#include "linalg/blas_internal.h" +#include "blas_internal.h" /** * \function igraph_blas_dgemv diff --git a/src/rigraph/core/linalg/lapack.c b/src/rigraph/core/linalg/lapack.c index 5652caf..7d0890d 100644 --- a/src/rigraph/core/linalg/lapack.c +++ b/src/rigraph/core/linalg/lapack.c @@ -23,7 +23,7 @@ #include "igraph_lapack.h" -#include "linalg/lapack_internal.h" +#include "lapack_internal.h" /** * \function igraph_lapack_dgetrf diff --git a/src/rigraph/core/math/bfgs.c b/src/rigraph/core/math/bfgs.c index a272029..09631db 100644 --- a/src/rigraph/core/math/bfgs.c +++ b/src/rigraph/core/math/bfgs.c @@ -22,7 +22,7 @@ */ #include "igraph_nongraph.h" -#include "core/interruption.h" +#include "../core/interruption.h" #include "igraph_statusbar.h" #include diff --git a/src/rigraph/core/math/complex.c b/src/rigraph/core/math/complex.c index e47b168..b397da9 100644 --- a/src/rigraph/core/math/complex.c +++ b/src/rigraph/core/math/complex.c @@ -22,7 +22,7 @@ */ #include "igraph_complex.h" -#include "core/math.h" +#include "../core/math.h" #include /** diff --git a/src/rigraph/core/math/utils.c b/src/rigraph/core/math/utils.c index abf175c..2909960 100644 --- a/src/rigraph/core/math/utils.c +++ b/src/rigraph/core/math/utils.c @@ -23,7 +23,7 @@ #include "igraph_types.h" -#include "core/math.h" +#include "../core/math.h" #include "config.h" @@ -148,8 +148,8 @@ double igraph_log1p(double x) { +.63533936180236187354180266666666e-31, }; - static IGRAPH_THREAD_LOCAL int nlnrel = 0; - static IGRAPH_THREAD_LOCAL double xmin = 0.0; + static int nlnrel = 0; + static double xmin = 0.0; if (xmin == 0.0) { xmin = -1 + sqrt(DBL_EPSILON); /*was sqrt(d1mach(4)); */ diff --git a/src/rigraph/core/misc/bipartite.c b/src/rigraph/core/misc/bipartite.c index e26ceb4..49158b3 100644 --- a/src/rigraph/core/misc/bipartite.c +++ b/src/rigraph/core/misc/bipartite.c @@ -30,7 +30,7 @@ #include "igraph_random.h" #include "igraph_nongraph.h" -#include "graph/attributes.h" +#include "../graph/attributes.h" /** * \section about_bipartite Bipartite networks in igraph diff --git a/src/rigraph/core/misc/cocitation.c b/src/rigraph/core/misc/cocitation.c index 6f9c787..4b59420 100644 --- a/src/rigraph/core/misc/cocitation.c +++ b/src/rigraph/core/misc/cocitation.c @@ -27,7 +27,7 @@ #include "igraph_adjlist.h" #include "igraph_interface.h" -#include "core/interruption.h" +#include "../core/interruption.h" #include diff --git a/src/rigraph/core/misc/coloring.c b/src/rigraph/core/misc/coloring.c index 39b0ff9..6fc0231 100644 --- a/src/rigraph/core/misc/coloring.c +++ b/src/rigraph/core/misc/coloring.c @@ -22,8 +22,8 @@ #include "igraph_interface.h" #include "igraph_adjlist.h" -#include "core/indheap.h" -#include "core/interruption.h" +#include "../core/indheap.h" +#include "../core/interruption.h" static int igraph_i_vertex_coloring_greedy_cn(const igraph_t *graph, igraph_vector_int_t *colors) { long i, vertex, maxdeg; diff --git a/src/rigraph/core/misc/conversion.c b/src/rigraph/core/misc/conversion.c index b16a70e..918ebaf 100644 --- a/src/rigraph/core/misc/conversion.c +++ b/src/rigraph/core/misc/conversion.c @@ -31,9 +31,9 @@ #include "igraph_sparsemat.h" #include "igraph_random.h" -#include "core/fixed_vectorlist.h" -#include "graph/attributes.h" -#include "misc/conversion_internal.h" +#include "../core/fixed_vectorlist.h" +#include "../graph/attributes.h" +#include "conversion_internal.h" /** * \ingroup conversion diff --git a/src/rigraph/core/misc/feedback_arc_set.c b/src/rigraph/core/misc/feedback_arc_set.c index 5f753a3..8f0ccef 100644 --- a/src/rigraph/core/misc/feedback_arc_set.c +++ b/src/rigraph/core/misc/feedback_arc_set.c @@ -29,8 +29,8 @@ #include "igraph_structural.h" #include "igraph_visitor.h" -#include "internal/glpk_support.h" -#include "misc/feedback_arc_set.h" +#include "../internal/glpk_support.h" +#include "feedback_arc_set.h" int igraph_i_feedback_arc_set_ip(const igraph_t *graph, igraph_vector_t *result, const igraph_vector_t *weights); diff --git a/src/rigraph/core/misc/motifs.c b/src/rigraph/core/misc/motifs.c index 5b96f76..0a496b9 100644 --- a/src/rigraph/core/misc/motifs.c +++ b/src/rigraph/core/misc/motifs.c @@ -30,9 +30,9 @@ #include "igraph_nongraph.h" #include "igraph_stack.h" -#include "core/interruption.h" -#include "isomorphism/isoclasses.h" -#include "graph/neighbors.h" +#include "../core/interruption.h" +#include "../isomorphism/isoclasses.h" +#include "../graph/neighbors.h" /** * Callback function for igraph_motifs_randesu that counts the motifs by diff --git a/src/rigraph/core/misc/other.c b/src/rigraph/core/misc/other.c index 45dbcbb..7b81fb0 100644 --- a/src/rigraph/core/misc/other.c +++ b/src/rigraph/core/misc/other.c @@ -25,9 +25,9 @@ #include "igraph_random.h" #include "igraph_types.h" -#include "core/interruption.h" -#include "plfit/plfit_error.h" -#include "plfit/plfit.h" +#include "../core/interruption.h" +#include "../../vendor/plfit/plfit_error.h" +#include "../../vendor/plfit/plfit.h" #include diff --git a/src/rigraph/core/misc/scan.c b/src/rigraph/core/misc/scan.c index f9d1a3b..573e426 100644 --- a/src/rigraph/core/misc/scan.c +++ b/src/rigraph/core/misc/scan.c @@ -34,8 +34,8 @@ #include "igraph_stack.h" #include "igraph_structural.h" -#include "core/interruption.h" -#include "properties/properties_internal.h" +#include "../core/interruption.h" +#include "../properties/properties_internal.h" /** * \section about_local_scan diff --git a/src/rigraph/core/misc/sir.c b/src/rigraph/core/misc/sir.c index 4adb12a..69cd737 100644 --- a/src/rigraph/core/misc/sir.c +++ b/src/rigraph/core/misc/sir.c @@ -30,7 +30,7 @@ #include "igraph_memory.h" #include "igraph_structural.h" -#include "core/interruption.h" +#include "../core/interruption.h" int igraph_sir_init(igraph_sir_t *sir) { IGRAPH_CHECK(igraph_vector_init(&sir->times, 1)); diff --git a/src/rigraph/core/misc/spanning_trees.c b/src/rigraph/core/misc/spanning_trees.c index 1b2fa25..b4cc3d5 100644 --- a/src/rigraph/core/misc/spanning_trees.c +++ b/src/rigraph/core/misc/spanning_trees.c @@ -32,8 +32,8 @@ #include "igraph_random.h" #include "igraph_structural.h" -#include "core/indheap.h" -#include "core/interruption.h" +#include "../core/indheap.h" +#include "../core/interruption.h" static int igraph_i_minimum_spanning_tree_unweighted(const igraph_t *graph, igraph_vector_t *result); diff --git a/src/rigraph/core/operators/complementer.c b/src/rigraph/core/operators/complementer.c index fe71188..0c429b8 100644 --- a/src/rigraph/core/operators/complementer.c +++ b/src/rigraph/core/operators/complementer.c @@ -25,8 +25,8 @@ #include "igraph_constructors.h" #include "igraph_interface.h" -#include "graph/attributes.h" -#include "core/interruption.h" +#include "../graph/attributes.h" +#include "../core/interruption.h" /** * \function igraph_complementer diff --git a/src/rigraph/core/operators/compose.c b/src/rigraph/core/operators/compose.c index eada9af..702b236 100644 --- a/src/rigraph/core/operators/compose.c +++ b/src/rigraph/core/operators/compose.c @@ -25,7 +25,7 @@ #include "igraph_constructors.h" #include "igraph_interface.h" -#include "core/interruption.h" +#include "../core/interruption.h" /** * \function igraph_compose diff --git a/src/rigraph/core/operators/contract.c b/src/rigraph/core/operators/contract.c index af2fbe2..8c449a4 100644 --- a/src/rigraph/core/operators/contract.c +++ b/src/rigraph/core/operators/contract.c @@ -26,7 +26,7 @@ #include "igraph_interface.h" #include "igraph_memory.h" -#include "graph/attributes.h" +#include "../graph/attributes.h" static void igraph_i_simplify_free(igraph_vector_ptr_t *p) { long int i, n = igraph_vector_ptr_size(p); diff --git a/src/rigraph/core/operators/difference.c b/src/rigraph/core/operators/difference.c index c229068..37f05a6 100644 --- a/src/rigraph/core/operators/difference.c +++ b/src/rigraph/core/operators/difference.c @@ -26,8 +26,8 @@ #include "igraph_constructors.h" #include "igraph_interface.h" -#include "graph/attributes.h" -#include "core/interruption.h" +#include "../graph/attributes.h" +#include "../core/interruption.h" /** * \function igraph_difference diff --git a/src/rigraph/core/operators/disjoint_union.c b/src/rigraph/core/operators/disjoint_union.c index 0ea11a9..97e8da0 100644 --- a/src/rigraph/core/operators/disjoint_union.c +++ b/src/rigraph/core/operators/disjoint_union.c @@ -25,7 +25,7 @@ #include "igraph_constructors.h" #include "igraph_interface.h" -#include "operators/misc_internal.h" +#include "../operators/misc_internal.h" /** * \function igraph_disjoint_union diff --git a/src/rigraph/core/operators/intersection.c b/src/rigraph/core/operators/intersection.c index 610c3cd..abfab78 100644 --- a/src/rigraph/core/operators/intersection.c +++ b/src/rigraph/core/operators/intersection.c @@ -28,7 +28,7 @@ #include "igraph_memory.h" #include "igraph_qsort.h" -#include "operators/misc_internal.h" +#include "../operators/misc_internal.h" #include diff --git a/src/rigraph/core/operators/misc_internal.c b/src/rigraph/core/operators/misc_internal.c index d2a4863..e3acb73 100644 --- a/src/rigraph/core/operators/misc_internal.c +++ b/src/rigraph/core/operators/misc_internal.c @@ -20,7 +20,7 @@ */ -#include "operators/misc_internal.h" +#include "../operators/misc_internal.h" #include "igraph_constructors.h" #include "igraph_conversion.h" diff --git a/src/rigraph/core/operators/permute.c b/src/rigraph/core/operators/permute.c index 9057eb8..befb987 100644 --- a/src/rigraph/core/operators/permute.c +++ b/src/rigraph/core/operators/permute.c @@ -25,7 +25,7 @@ #include "igraph_constructors.h" #include "igraph_interface.h" -#include "graph/attributes.h" +#include "../graph/attributes.h" /** * \function igraph_permute_vertices diff --git a/src/rigraph/core/operators/rewire.c b/src/rigraph/core/operators/rewire.c index 2ac97ee..a32138c 100644 --- a/src/rigraph/core/operators/rewire.c +++ b/src/rigraph/core/operators/rewire.c @@ -31,8 +31,8 @@ #include "igraph_random.h" #include "igraph_structural.h" -#include "core/interruption.h" -#include "operators/rewire_internal.h" +#include "../core/interruption.h" +#include "../operators/rewire_internal.h" /* Threshold that defines when to switch over to using adjacency lists during * rewiring */ diff --git a/src/rigraph/core/operators/rewire_edges.c b/src/rigraph/core/operators/rewire_edges.c index 48211e2..134bb48 100644 --- a/src/rigraph/core/operators/rewire_edges.c +++ b/src/rigraph/core/operators/rewire_edges.c @@ -28,7 +28,7 @@ #include "igraph_interface.h" #include "igraph_random.h" -#include "graph/attributes.h" +#include "../graph/attributes.h" static int igraph_i_rewire_edges_no_multiple(igraph_t *graph, igraph_real_t prob, igraph_bool_t loops, diff --git a/src/rigraph/core/operators/simplify.c b/src/rigraph/core/operators/simplify.c index 6924f18..80300c8 100644 --- a/src/rigraph/core/operators/simplify.c +++ b/src/rigraph/core/operators/simplify.c @@ -25,8 +25,8 @@ #include "igraph_constructors.h" #include "igraph_interface.h" -#include "core/fixed_vectorlist.h" -#include "graph/attributes.h" +#include "../core/fixed_vectorlist.h" +#include "../graph/attributes.h" /** * \ingroup structural diff --git a/src/rigraph/core/operators/subgraph.c b/src/rigraph/core/operators/subgraph.c index f798f5d..d46c9df 100644 --- a/src/rigraph/core/operators/subgraph.c +++ b/src/rigraph/core/operators/subgraph.c @@ -26,9 +26,9 @@ #include "igraph_interface.h" #include "igraph_memory.h" -#include "core/interruption.h" -#include "graph/attributes.h" -#include "operators/subgraph.h" +#include "../core/interruption.h" +#include "../graph/attributes.h" +#include "subgraph.h" /** * Subgraph creation, old version: it copies the graph and then deletes diff --git a/src/rigraph/core/operators/union.c b/src/rigraph/core/operators/union.c index d55390d..09663b8 100644 --- a/src/rigraph/core/operators/union.c +++ b/src/rigraph/core/operators/union.c @@ -30,7 +30,7 @@ #include "igraph_memory.h" #include "igraph_qsort.h" -#include "operators/misc_internal.h" +#include "misc_internal.h" /** * \function igraph_union diff --git a/src/rigraph/core/paths/all_shortest_paths.c b/src/rigraph/core/paths/all_shortest_paths.c index 0bf5f78..8c4a539 100644 --- a/src/rigraph/core/paths/all_shortest_paths.c +++ b/src/rigraph/core/paths/all_shortest_paths.c @@ -28,7 +28,7 @@ #include "igraph_interface.h" #include "igraph_memory.h" -#include "core/interruption.h" +#include "../core/interruption.h" #include /* memset */ diff --git a/src/rigraph/core/paths/bellman_ford.c b/src/rigraph/core/paths/bellman_ford.c index 9e93f80..18e6e04 100644 --- a/src/rigraph/core/paths/bellman_ford.c +++ b/src/rigraph/core/paths/bellman_ford.c @@ -26,8 +26,8 @@ #include "igraph_memory.h" #include "igraph_stack.h" -#include "core/indheap.h" -#include "core/interruption.h" +#include "../core/indheap.h" +#include "../core/interruption.h" #include diff --git a/src/rigraph/core/paths/dijkstra.c b/src/rigraph/core/paths/dijkstra.c index 0dd7ccb..3e91ab1 100644 --- a/src/rigraph/core/paths/dijkstra.c +++ b/src/rigraph/core/paths/dijkstra.c @@ -29,9 +29,9 @@ #include "igraph_memory.h" #include "igraph_stack.h" -#include "core/indheap.h" -#include "core/interruption.h" -#include "core/math.h" +#include "../core/indheap.h" +#include "../core/interruption.h" +#include "../core/math.h" #include /* memset */ diff --git a/src/rigraph/core/paths/distances.c b/src/rigraph/core/paths/distances.c index 1e5a91e..7aae4a7 100644 --- a/src/rigraph/core/paths/distances.c +++ b/src/rigraph/core/paths/distances.c @@ -31,7 +31,7 @@ #include "igraph_interface.h" #include "igraph_adjlist.h" -#include "core/interruption.h" +#include "../core/interruption.h" static int igraph_i_eccentricity(const igraph_t *graph, igraph_vector_t *res, diff --git a/src/rigraph/core/paths/histogram.c b/src/rigraph/core/paths/histogram.c index a84858d..76128f3 100644 --- a/src/rigraph/core/paths/histogram.c +++ b/src/rigraph/core/paths/histogram.c @@ -27,7 +27,7 @@ #include "igraph_interface.h" #include "igraph_progress.h" -#include "core/interruption.h" +#include "../core/interruption.h" /** * \function igraph_path_length_hist diff --git a/src/rigraph/core/paths/random_walk.c b/src/rigraph/core/paths/random_walk.c index a629d01..d301677 100644 --- a/src/rigraph/core/paths/random_walk.c +++ b/src/rigraph/core/paths/random_walk.c @@ -28,7 +28,7 @@ #include "igraph_random.h" #include "igraph_memory.h" -#include "core/interruption.h" +#include "../core/interruption.h" /** * \function igraph_random_walk diff --git a/src/rigraph/core/paths/shortest_paths.c b/src/rigraph/core/paths/shortest_paths.c index 0c967e6..34b4202 100644 --- a/src/rigraph/core/paths/shortest_paths.c +++ b/src/rigraph/core/paths/shortest_paths.c @@ -26,8 +26,8 @@ #include "igraph_memory.h" #include "igraph_progress.h" -#include "core/indheap.h" -#include "core/interruption.h" +#include "../core/indheap.h" +#include "../core/interruption.h" #include diff --git a/src/rigraph/core/paths/simple_paths.c b/src/rigraph/core/paths/simple_paths.c index 45c0d3c..e5bfba0 100644 --- a/src/rigraph/core/paths/simple_paths.c +++ b/src/rigraph/core/paths/simple_paths.c @@ -29,7 +29,7 @@ #include "igraph_adjlist.h" #include "igraph_stack.h" -#include "core/interruption.h" +#include "../core/interruption.h" /** * \function igraph_get_all_simple_paths diff --git a/src/rigraph/core/paths/unweighted.c b/src/rigraph/core/paths/unweighted.c index 9db9d72..46f40ea 100644 --- a/src/rigraph/core/paths/unweighted.c +++ b/src/rigraph/core/paths/unweighted.c @@ -25,7 +25,7 @@ #include "igraph_interface.h" #include "igraph_memory.h" -#include "core/interruption.h" +#include "../core/interruption.h" /** * \ingroup structural diff --git a/src/rigraph/core/properties/convergence_degree.c b/src/rigraph/core/properties/convergence_degree.c index 8230b5e..948d5b0 100644 --- a/src/rigraph/core/properties/convergence_degree.c +++ b/src/rigraph/core/properties/convergence_degree.c @@ -28,7 +28,7 @@ #include "igraph_interface.h" #include "igraph_memory.h" -#include "core/interruption.h" +#include "../core/interruption.h" #include diff --git a/src/rigraph/core/properties/girth.c b/src/rigraph/core/properties/girth.c index 18bd02c..4a732a0 100644 --- a/src/rigraph/core/properties/girth.c +++ b/src/rigraph/core/properties/girth.c @@ -28,7 +28,7 @@ #include "igraph_dqueue.h" #include "igraph_interface.h" -#include "core/interruption.h" +#include "../core/interruption.h" #include diff --git a/src/rigraph/core/properties/triangles.c b/src/rigraph/core/properties/triangles.c index 99aa20f..8957cc9 100644 --- a/src/rigraph/core/properties/triangles.c +++ b/src/rigraph/core/properties/triangles.c @@ -30,8 +30,8 @@ #include "igraph_motifs.h" #include "igraph_structural.h" -#include "core/interruption.h" -#include "properties/properties_internal.h" +#include "../core/interruption.h" +#include "properties_internal.h" /** * \function igraph_transitivity_avglocal_undirected @@ -119,7 +119,7 @@ int igraph_transitivity_local_undirected1(const igraph_t *graph, igraph_transitivity_mode_t mode) { #define TRANSIT -#include "properties/triangles_template1.h" +#include "triangles_template1.h" #undef TRANSIT return IGRAPH_SUCCESS; @@ -374,7 +374,7 @@ int igraph_transitivity_local_undirected4(const igraph_t *graph, igraph_transitivity_mode_t mode) { #define TRANSIT 1 -#include "properties/triangles_template.h" +#include "triangles_template.h" #undef TRANSIT return 0; @@ -446,13 +446,13 @@ int igraph_transitivity_local_undirected(const igraph_t *graph, static int igraph_adjacent_triangles1(const igraph_t *graph, igraph_vector_t *res, const igraph_vs_t vids) { -# include "properties/triangles_template1.h" +# include "triangles_template1.h" return 0; } static int igraph_adjacent_triangles4(const igraph_t *graph, igraph_vector_t *res) { -# include "properties/triangles_template.h" +# include "triangles_template.h" return 0; } @@ -504,7 +504,7 @@ int igraph_adjacent_triangles(const igraph_t *graph, int igraph_list_triangles(const igraph_t *graph, igraph_vector_int_t *res) { # define TRIANGLES -# include "properties/triangles_template.h" +# include "triangles_template.h" # undef TRIANGLES return IGRAPH_SUCCESS; } diff --git a/src/rigraph/core/random/random.c b/src/rigraph/core/random/random.c index 5cb0753..0cb0497 100644 --- a/src/rigraph/core/random/random.c +++ b/src/rigraph/core/random/random.c @@ -29,7 +29,7 @@ #include "igraph_vector.h" #include "igraph_memory.h" -#include "core/math.h" +#include "../core/math.h" #include "config.h" #include @@ -507,7 +507,7 @@ igraph_i_rng_mt19937_state_t igraph_i_rng_default_state; * igraph_rng_set_default() function. */ -IGRAPH_THREAD_LOCAL igraph_rng_t igraph_i_rng_default = { +igraph_rng_t igraph_i_rng_default = { addr(igraph_rngtype_mt19937), addr(igraph_i_rng_default_state), /* def= */ 1 @@ -1566,12 +1566,12 @@ static double igraph_rpois(igraph_rng_t *rng, double mu) { }; /* These are static --- persistent between calls for same mu : */ - static IGRAPH_THREAD_LOCAL int l, m; + static 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.*/ + static double b1, b2, c, c0, c1, c2, c3; + static double pp[36], p0, p, q, s, d, omega; + static double big_l;/* integer "w/o overflow" */ + static 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; @@ -1780,12 +1780,12 @@ static double igraph_rgeom(igraph_rng_t *rng, double p) { static 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 double c, fm, npq, p1, p2, p3, p4, qn; + static 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; + static double psave = -1.0; + static int nsave = -1; + static 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; diff --git a/src/rigraph/core/scg/scg.c b/src/rigraph/core/scg/scg.c index ab435d1..9510fff 100644 --- a/src/rigraph/core/scg/scg.c +++ b/src/rigraph/core/scg/scg.c @@ -86,7 +86,7 @@ #include "igraph_memory.h" #include "igraph_qsort.h" -#include "misc/conversion_internal.h" +#include "../misc/conversion_internal.h" #include "scg_headers.h" diff --git a/src/rigraph/rinterface.h b/src/rigraph/rinterface.h index e890f09..d03f2f4 100644 --- a/src/rigraph/rinterface.h +++ b/src/rigraph/rinterface.h @@ -21,7 +21,7 @@ */ -#include "uuid/uuid.h" +#include "vendor/uuid/uuid.h" #define R_IGRAPH_TYPE_VERSION "0.8.0" #define R_IGRAPH_VERSION_VAR ".__igraph_version__." diff --git a/src/rigraph/rrandom.c b/src/rigraph/rrandom.c index 68f0067..874f894 100644 --- a/src/rigraph/rrandom.c +++ b/src/rigraph/rrandom.c @@ -30,7 +30,7 @@ #include "igraph_vector.h" #include "igraph_memory.h" -#include "core/math.h" +#include "core/core/math.h" #include "config.h" #include diff --git a/src/rigraph/vendor/arpack/debug.h b/src/rigraph/vendor/arpack/debug.h new file mode 100644 index 0000000..5eb0bb1 --- /dev/null +++ b/src/rigraph/vendor/arpack/debug.h @@ -0,0 +1,16 @@ +c +c\SCCS Information: @(#) +c FILE: debug.h SID: 2.3 DATE OF SID: 11/16/95 RELEASE: 2 +c +c %---------------------------------% +c | See debug.doc for documentation | +c %---------------------------------% + integer logfil, ndigit, mgetv0, + & msaupd, msaup2, msaitr, mseigt, msapps, msgets, mseupd, + & mnaupd, mnaup2, mnaitr, mneigh, mnapps, mngets, mneupd, + & mcaupd, mcaup2, mcaitr, mceigh, mcapps, mcgets, mceupd + common /debug/ + & logfil, ndigit, mgetv0, + & msaupd, msaup2, msaitr, mseigt, msapps, msgets, mseupd, + & mnaupd, mnaup2, mnaitr, mneigh, mnapps, mngets, mneupd, + & mcaupd, mcaup2, mcaitr, mceigh, mcapps, mcgets, mceupd diff --git a/src/rigraph/vendor/arpack/dgetv0.f b/src/rigraph/vendor/arpack/dgetv0.f new file mode 100644 index 0000000..b64d47a --- /dev/null +++ b/src/rigraph/vendor/arpack/dgetv0.f @@ -0,0 +1,419 @@ +c----------------------------------------------------------------------- +c\BeginDoc +c +c\Name: igraphdgetv0 +c +c\Description: +c Generate a random initial residual vector for the Arnoldi process. +c Force the residual vector to be in the range of the operator OP. +c +c\Usage: +c call igraphdgetv0 +c ( IDO, BMAT, ITRY, INITV, N, J, V, LDV, RESID, RNORM, +c IPNTR, WORKD, IERR ) +c +c\Arguments +c IDO Integer. (INPUT/OUTPUT) +c Reverse communication flag. IDO must be zero on the first +c call to igraphdgetv0. +c ------------------------------------------------------------- +c IDO = 0: first call to the reverse communication interface +c IDO = -1: compute Y = OP * X where +c IPNTR(1) is the pointer into WORKD for X, +c IPNTR(2) is the pointer into WORKD for Y. +c This is for the initialization phase to force the +c starting vector into the range of OP. +c IDO = 2: compute Y = B * X where +c IPNTR(1) is the pointer into WORKD for X, +c IPNTR(2) is the pointer into WORKD for Y. +c IDO = 99: done +c ------------------------------------------------------------- +c +c BMAT Character*1. (INPUT) +c BMAT specifies the type of the matrix B in the (generalized) +c eigenvalue problem A*x = lambda*B*x. +c B = 'I' -> standard eigenvalue problem A*x = lambda*x +c B = 'G' -> generalized eigenvalue problem A*x = lambda*B*x +c +c ITRY Integer. (INPUT) +c ITRY counts the number of times that igraphdgetv0 is called. +c It should be set to 1 on the initial call to igraphdgetv0. +c +c INITV Logical variable. (INPUT) +c .TRUE. => the initial residual vector is given in RESID. +c .FALSE. => generate a random initial residual vector. +c +c N Integer. (INPUT) +c Dimension of the problem. +c +c J Integer. (INPUT) +c Index of the residual vector to be generated, with respect to +c the Arnoldi process. J > 1 in case of a "restart". +c +c V Double precision N by J array. (INPUT) +c The first J-1 columns of V contain the current Arnoldi basis +c if this is a "restart". +c +c LDV Integer. (INPUT) +c Leading dimension of V exactly as declared in the calling +c program. +c +c RESID Double precision array of length N. (INPUT/OUTPUT) +c Initial residual vector to be generated. If RESID is +c provided, force RESID into the range of the operator OP. +c +c RNORM Double precision scalar. (OUTPUT) +c B-norm of the generated residual. +c +c IPNTR Integer array of length 3. (OUTPUT) +c +c WORKD Double precision work array of length 2*N. (REVERSE COMMUNICATION). +c On exit, WORK(1:N) = B*RESID to be used in SSAITR. +c +c IERR Integer. (OUTPUT) +c = 0: Normal exit. +c = -1: Cannot generate a nontrivial restarted residual vector +c in the range of the operator OP. +c +c\EndDoc +c +c----------------------------------------------------------------------- +c +c\BeginLib +c +c\Local variables: +c xxxxxx real +c +c\References: +c 1. D.C. Sorensen, "Implicit Application of Polynomial Filters in +c a k-Step Arnoldi Method", SIAM J. Matr. Anal. Apps., 13 (1992), +c pp 357-385. +c 2. R.B. Lehoucq, "Analysis and Implementation of an Implicitly +c Restarted Arnoldi Iteration", Rice University Technical Report +c TR95-13, Department of Computational and Applied Mathematics. +c +c\Routines called: +c igraphsecond ARPACK utility routine for timing. +c igraphdvout ARPACK utility routine for vector output. +c dlarnv LAPACK routine for generating a random vector. +c dgemv Level 2 BLAS routine for matrix vector multiplication. +c dcopy Level 1 BLAS that copies one vector to another. +c ddot Level 1 BLAS that computes the scalar product of two vectors. +c dnrm2 Level 1 BLAS that computes the norm of a vector. +c +c\Author +c Danny Sorensen Phuong Vu +c Richard Lehoucq CRPC / Rice University +c Dept. of Computational & Houston, Texas +c Applied Mathematics +c Rice University +c Houston, Texas +c +c\SCCS Information: @(#) +c FILE: getv0.F SID: 2.6 DATE OF SID: 8/27/96 RELEASE: 2 +c +c\EndLib +c +c----------------------------------------------------------------------- +c + subroutine igraphdgetv0 + & ( ido, bmat, itry, initv, n, j, v, ldv, resid, rnorm, + & ipntr, workd, ierr ) +c +c %----------------------------------------------------% +c | Include files for debugging and timing information | +c %----------------------------------------------------% +c + include 'debug.h' + include 'stat.h' +c +c %------------------% +c | Scalar Arguments | +c %------------------% +c + character bmat*1 + logical initv + integer ido, ierr, itry, j, ldv, n + Double precision + & rnorm +c +c %-----------------% +c | Array Arguments | +c %-----------------% +c + integer ipntr(3) + Double precision + & resid(n), v(ldv,j), workd(2*n) +c +c %------------% +c | Parameters | +c %------------% +c + Double precision + & one, zero + parameter (one = 1.0D+0, zero = 0.0D+0) +c +c %------------------------% +c | Local Scalars & Arrays | +c %------------------------% +c + logical first, inits, orth + integer idist, iseed(4), iter, msglvl, jj + Double precision + & rnorm0 + save first, iseed, inits, iter, msglvl, orth, rnorm0 +c +c %----------------------% +c | External Subroutines | +c %----------------------% +c + external dlarnv, igraphdvout, dcopy, dgemv, igraphsecond +c +c %--------------------% +c | External Functions | +c %--------------------% +c + Double precision + & ddot, dnrm2 + external ddot, dnrm2 +c +c %---------------------% +c | Intrinsic Functions | +c %---------------------% +c + intrinsic abs, sqrt +c +c %-----------------% +c | Data Statements | +c %-----------------% +c + data inits /.true./ +c +c %-----------------------% +c | Executable Statements | +c %-----------------------% +c +c +c %-----------------------------------% +c | Initialize the seed of the LAPACK | +c | random number generator | +c %-----------------------------------% +c + if (inits) then + iseed(1) = 1 + iseed(2) = 3 + iseed(3) = 5 + iseed(4) = 7 + inits = .false. + end if +c + if (ido .eq. 0) then +c +c %-------------------------------% +c | Initialize timing statistics | +c | & message level for debugging | +c %-------------------------------% +c + call igraphsecond (t0) + msglvl = mgetv0 +c + ierr = 0 + iter = 0 + first = .FALSE. + orth = .FALSE. +c +c %-----------------------------------------------------% +c | Possibly generate a random starting vector in RESID | +c | Use a LAPACK random number generator used by the | +c | matrix generation routines. | +c | idist = 1: uniform (0,1) distribution; | +c | idist = 2: uniform (-1,1) distribution; | +c | idist = 3: normal (0,1) distribution; | +c %-----------------------------------------------------% +c + if (.not.initv) then + idist = 2 + call dlarnv (idist, iseed, n, resid) + end if +c +c %----------------------------------------------------------% +c | Force the starting vector into the range of OP to handle | +c | the generalized problem when B is possibly (singular). | +c %----------------------------------------------------------% +c + call igraphsecond (t2) + if (bmat .eq. 'G') then + nopx = nopx + 1 + ipntr(1) = 1 + ipntr(2) = n + 1 + call dcopy (n, resid, 1, workd, 1) + ido = -1 + go to 9000 + end if + end if +c +c %-----------------------------------------% +c | Back from computing OP*(initial-vector) | +c %-----------------------------------------% +c + if (first) go to 20 +c +c %-----------------------------------------------% +c | Back from computing B*(orthogonalized-vector) | +c %-----------------------------------------------% +c + if (orth) go to 40 +c + if (bmat .eq. 'G') then + call igraphsecond (t3) + tmvopx = tmvopx + (t3 - t2) + end if +c +c %------------------------------------------------------% +c | Starting vector is now in the range of OP; r = OP*r; | +c | Compute B-norm of starting vector. | +c %------------------------------------------------------% +c + call igraphsecond (t2) + first = .TRUE. + if (bmat .eq. 'G') then + nbx = nbx + 1 + call dcopy (n, workd(n+1), 1, resid, 1) + ipntr(1) = n + 1 + ipntr(2) = 1 + ido = 2 + go to 9000 + else if (bmat .eq. 'I') then + call dcopy (n, resid, 1, workd, 1) + end if +c + 20 continue +c + if (bmat .eq. 'G') then + call igraphsecond (t3) + tmvbx = tmvbx + (t3 - t2) + end if +c + first = .FALSE. + if (bmat .eq. 'G') then + rnorm0 = ddot (n, resid, 1, workd, 1) + rnorm0 = sqrt(abs(rnorm0)) + else if (bmat .eq. 'I') then + rnorm0 = dnrm2(n, resid, 1) + end if + rnorm = rnorm0 +c +c %---------------------------------------------% +c | Exit if this is the very first Arnoldi step | +c %---------------------------------------------% +c + if (j .eq. 1) go to 50 +c +c %---------------------------------------------------------------- +c | Otherwise need to B-orthogonalize the starting vector against | +c | the current Arnoldi basis using Gram-Schmidt with iter. ref. | +c | This is the case where an invariant subspace is encountered | +c | in the middle of the Arnoldi factorization. | +c | | +c | s = V^{T}*B*r; r = r - V*s; | +c | | +c | Stopping criteria used for iter. ref. is discussed in | +c | Parlett's book, page 107 and in Gragg & Reichel TOMS paper. | +c %---------------------------------------------------------------% +c + orth = .TRUE. + 30 continue +c + call dgemv ('T', n, j-1, one, v, ldv, workd, 1, + & zero, workd(n+1), 1) + call dgemv ('N', n, j-1, -one, v, ldv, workd(n+1), 1, + & one, resid, 1) +c +c %----------------------------------------------------------% +c | Compute the B-norm of the orthogonalized starting vector | +c %----------------------------------------------------------% +c + call igraphsecond (t2) + if (bmat .eq. 'G') then + nbx = nbx + 1 + call dcopy (n, resid, 1, workd(n+1), 1) + ipntr(1) = n + 1 + ipntr(2) = 1 + ido = 2 + go to 9000 + else if (bmat .eq. 'I') then + call dcopy (n, resid, 1, workd, 1) + end if +c + 40 continue +c + if (bmat .eq. 'G') then + call igraphsecond (t3) + tmvbx = tmvbx + (t3 - t2) + end if +c + if (bmat .eq. 'G') then + rnorm = ddot (n, resid, 1, workd, 1) + rnorm = sqrt(abs(rnorm)) + else if (bmat .eq. 'I') then + rnorm = dnrm2(n, resid, 1) + end if +c +c %--------------------------------------% +c | Check for further orthogonalization. | +c %--------------------------------------% +c + if (msglvl .gt. 2) then + call igraphdvout (logfil, 1, [rnorm0], ndigit, + & '_getv0: re-orthonalization ; rnorm0 is') + call igraphdvout (logfil, 1, [rnorm], ndigit, + & '_getv0: re-orthonalization ; rnorm is') + end if +c + if (rnorm .gt. 0.717*rnorm0) go to 50 +c + iter = iter + 1 + if (iter .le. 1) then +c +c %-----------------------------------% +c | Perform iterative refinement step | +c %-----------------------------------% +c + rnorm0 = rnorm + go to 30 + else +c +c %------------------------------------% +c | Iterative refinement step "failed" | +c %------------------------------------% +c + do 45 jj = 1, n + resid(jj) = zero + 45 continue + rnorm = zero + ierr = -1 + end if +c + 50 continue +c + if (msglvl .gt. 0) then + call igraphdvout (logfil, 1, [rnorm], ndigit, + & '_getv0: B-norm of initial / restarted starting vector') + end if + if (msglvl .gt. 2) then + call igraphdvout (logfil, n, resid, ndigit, + & '_getv0: initial / restarted starting vector') + end if + ido = 99 +c + call igraphsecond (t1) + tgetv0 = tgetv0 + (t1 - t0) +c + 9000 continue + return +c +c %---------------% +c | End of igraphdgetv0 | +c %---------------% +c + end diff --git a/src/rigraph/vendor/arpack/dlaqrb.f b/src/rigraph/vendor/arpack/dlaqrb.f new file mode 100644 index 0000000..5fcefec --- /dev/null +++ b/src/rigraph/vendor/arpack/dlaqrb.f @@ -0,0 +1,521 @@ +c----------------------------------------------------------------------- +c\BeginDoc +c +c\Name: igraphdlaqrb +c +c\Description: +c Compute the eigenvalues and the Schur decomposition of an upper +c Hessenberg submatrix in rows and columns ILO to IHI. Only the +c last component of the Schur vectors are computed. +c +c This is mostly a modification of the LAPACK routine dlahqr. +c +c\Usage: +c call igraphdlaqrb +c ( WANTT, N, ILO, IHI, H, LDH, WR, WI, Z, INFO ) +c +c\Arguments +c WANTT Logical variable. (INPUT) +c = .TRUE. : the full Schur form T is required; +c = .FALSE.: only eigenvalues are required. +c +c N Integer. (INPUT) +c The order of the matrix H. N >= 0. +c +c ILO Integer. (INPUT) +c IHI Integer. (INPUT) +c It is assumed that H is already upper quasi-triangular in +c rows and columns IHI+1:N, and that H(ILO,ILO-1) = 0 (unless +c ILO = 1). SLAQRB works primarily with the Hessenberg +c submatrix in rows and columns ILO to IHI, but applies +c transformations to all of H if WANTT is .TRUE.. +c 1 <= ILO <= max(1,IHI); IHI <= N. +c +c H Double precision array, dimension (LDH,N). (INPUT/OUTPUT) +c On entry, the upper Hessenberg matrix H. +c On exit, if WANTT is .TRUE., H is upper quasi-triangular in +c rows and columns ILO:IHI, with any 2-by-2 diagonal blocks in +c standard form. If WANTT is .FALSE., the contents of H are +c unspecified on exit. +c +c LDH Integer. (INPUT) +c The leading dimension of the array H. LDH >= max(1,N). +c +c WR Double precision array, dimension (N). (OUTPUT) +c WI Double precision array, dimension (N). (OUTPUT) +c The real and imaginary parts, respectively, of the computed +c eigenvalues ILO to IHI are stored in the corresponding +c elements of WR and WI. If two eigenvalues are computed as a +c complex conjugate pair, they are stored in consecutive +c elements of WR and WI, say the i-th and (i+1)th, with +c WI(i) > 0 and WI(i+1) < 0. If WANTT is .TRUE., the +c eigenvalues are stored in the same order as on the diagonal +c of the Schur form returned in H, with WR(i) = H(i,i), and, if +c H(i:i+1,i:i+1) is a 2-by-2 diagonal block, +c WI(i) = sqrt(H(i+1,i)*H(i,i+1)) and WI(i+1) = -WI(i). +c +c Z Double precision array, dimension (N). (OUTPUT) +c On exit Z contains the last components of the Schur vectors. +c +c INFO Integer. (OUPUT) +c = 0: successful exit +c > 0: SLAQRB failed to compute all the eigenvalues ILO to IHI +c in a total of 30*(IHI-ILO+1) iterations; if INFO = i, +c elements i+1:ihi of WR and WI contain those eigenvalues +c which have been successfully computed. +c +c\Remarks +c 1. None. +c +c----------------------------------------------------------------------- +c +c\BeginLib +c +c\Local variables: +c xxxxxx real +c +c\Routines called: +c dlabad LAPACK routine that computes machine constants. +c dlamch LAPACK routine that determines machine constants. +c dlanhs LAPACK routine that computes various norms of a matrix. +c dlanv2 LAPACK routine that computes the Schur factorization of +c 2 by 2 nonsymmetric matrix in standard form. +c dlarfg LAPACK Householder reflection construction routine. +c dcopy Level 1 BLAS that copies one vector to another. +c drot Level 1 BLAS that applies a rotation to a 2 by 2 matrix. + +c +c\Author +c Danny Sorensen Phuong Vu +c Richard Lehoucq CRPC / Rice University +c Dept. of Computational & Houston, Texas +c Applied Mathematics +c Rice University +c Houston, Texas +c +c\Revision history: +c xx/xx/92: Version ' 2.4' +c Modified from the LAPACK routine dlahqr so that only the +c last component of the Schur vectors are computed. +c +c\SCCS Information: @(#) +c FILE: laqrb.F SID: 2.2 DATE OF SID: 8/27/96 RELEASE: 2 +c +c\Remarks +c 1. None +c +c\EndLib +c +c----------------------------------------------------------------------- +c + subroutine igraphdlaqrb ( wantt, n, ilo, ihi, h, ldh, wr, wi, + & z, info ) +c +c %------------------% +c | Scalar Arguments | +c %------------------% +c + logical wantt + integer ihi, ilo, info, ldh, n +c +c %-----------------% +c | Array Arguments | +c %-----------------% +c + Double precision + & h( ldh, * ), wi( * ), wr( * ), z( * ) +c +c %------------% +c | Parameters | +c %------------% +c + Double precision + & zero, one, dat1, dat2 + parameter (zero = 0.0D+0, one = 1.0D+0, dat1 = 7.5D-1, + & dat2 = -4.375D-1) +c +c %------------------------% +c | Local Scalars & Arrays | +c %------------------------% +c + integer i, i1, i2, itn, its, j, k, l, m, nh, nr + Double precision + & cs, h00, h10, h11, h12, h21, h22, h33, h33s, + & h43h34, h44, h44s, ovfl, s, smlnum, sn, sum, + & t1, t2, t3, tst1, ulp, unfl, v1, v2, v3 + Double precision + & v( 3 ), work( 1 ) +c +c %--------------------% +c | External Functions | +c %--------------------% +c + Double precision + & dlamch, dlanhs + external dlamch, dlanhs +c +c %----------------------% +c | External Subroutines | +c %----------------------% +c + external dcopy, dlabad, dlanv2, dlarfg, drot +c +c %-----------------------% +c | Executable Statements | +c %-----------------------% +c + info = 0 +c +c %--------------------------% +c | Quick return if possible | +c %--------------------------% +c + if( n.eq.0 ) + & return + if( ilo.eq.ihi ) then + wr( ilo ) = h( ilo, ilo ) + wi( ilo ) = zero + return + end if +c +c %---------------------------------------------% +c | Initialize the vector of last components of | +c | the Schur vectors for accumulation. | +c %---------------------------------------------% +c + do 5 j = 1, n-1 + z(j) = zero + 5 continue + z(n) = one +c + nh = ihi - ilo + 1 +c +c %-------------------------------------------------------------% +c | Set machine-dependent constants for the stopping criterion. | +c | If norm(H) <= sqrt(OVFL), overflow should not occur. | +c %-------------------------------------------------------------% +c + unfl = dlamch( 'safe minimum' ) + ovfl = one / unfl + call dlabad( unfl, ovfl ) + ulp = dlamch( 'precision' ) + smlnum = unfl*( nh / ulp ) +c +c %---------------------------------------------------------------% +c | I1 and I2 are the indices of the first row and last column | +c | of H to which transformations must be applied. If eigenvalues | +c | only are computed, I1 and I2 are set inside the main loop. | +c | Zero out H(J+2,J) = ZERO for J=1:N if WANTT = .TRUE. | +c | else H(J+2,J) for J=ILO:IHI-ILO-1 if WANTT = .FALSE. | +c %---------------------------------------------------------------% +c + if( wantt ) then + i1 = 1 + i2 = n + do 8 i=1,i2-2 + h(i1+i+1,i) = zero + 8 continue + else + do 9 i=1, ihi-ilo-1 + h(ilo+i+1,ilo+i-1) = zero + 9 continue + end if +c +c %---------------------------------------------------% +c | ITN is the total number of QR iterations allowed. | +c %---------------------------------------------------% +c + itn = 30*nh +c +c ------------------------------------------------------------------ +c The main loop begins here. I is the loop index and decreases from +c IHI to ILO in steps of 1 or 2. Each iteration of the loop works +c with the active submatrix in rows and columns L to I. +c Eigenvalues I+1 to IHI have already converged. Either L = ILO or +c H(L,L-1) is negligible so that the matrix splits. +c ------------------------------------------------------------------ +c + i = ihi + 10 continue + l = ilo + if( i.lt.ilo ) + & go to 150 + +c %--------------------------------------------------------------% +c | Perform QR iterations on rows and columns ILO to I until a | +c | submatrix of order 1 or 2 splits off at the bottom because a | +c | subdiagonal element has become negligible. | +c %--------------------------------------------------------------% + + do 130 its = 0, itn +c +c %----------------------------------------------% +c | Look for a single small subdiagonal element. | +c %----------------------------------------------% +c + do 20 k = i, l + 1, -1 + tst1 = abs( h( k-1, k-1 ) ) + abs( h( k, k ) ) + if( tst1.eq.zero ) + & tst1 = dlanhs( '1', i-l+1, h( l, l ), ldh, work ) + if( abs( h( k, k-1 ) ).le.max( ulp*tst1, smlnum ) ) + & go to 30 + 20 continue + 30 continue + l = k + if( l.gt.ilo ) then +c +c %------------------------% +c | H(L,L-1) is negligible | +c %------------------------% +c + h( l, l-1 ) = zero + end if +c +c %-------------------------------------------------------------% +c | Exit from loop if a submatrix of order 1 or 2 has split off | +c %-------------------------------------------------------------% +c + if( l.ge.i-1 ) + & go to 140 +c +c %---------------------------------------------------------% +c | Now the active submatrix is in rows and columns L to I. | +c | If eigenvalues only are being computed, only the active | +c | submatrix need be transformed. | +c %---------------------------------------------------------% +c + if( .not.wantt ) then + i1 = l + i2 = i + end if +c + if( its.eq.10 .or. its.eq.20 ) then +c +c %-------------------% +c | Exceptional shift | +c %-------------------% +c + s = abs( h( i, i-1 ) ) + abs( h( i-1, i-2 ) ) + h44 = dat1*s + h33 = h44 + h43h34 = dat2*s*s +c + else +c +c %-----------------------------------------% +c | Prepare to use Wilkinson's double shift | +c %-----------------------------------------% +c + h44 = h( i, i ) + h33 = h( i-1, i-1 ) + h43h34 = h( i, i-1 )*h( i-1, i ) + end if +c +c %-----------------------------------------------------% +c | Look for two consecutive small subdiagonal elements | +c %-----------------------------------------------------% +c + do 40 m = i - 2, l, -1 +c +c %---------------------------------------------------------% +c | Determine the effect of starting the double-shift QR | +c | iteration at row M, and see if this would make H(M,M-1) | +c | negligible. | +c %---------------------------------------------------------% +c + h11 = h( m, m ) + h22 = h( m+1, m+1 ) + h21 = h( m+1, m ) + h12 = h( m, m+1 ) + h44s = h44 - h11 + h33s = h33 - h11 + v1 = ( h33s*h44s-h43h34 ) / h21 + h12 + v2 = h22 - h11 - h33s - h44s + v3 = h( m+2, m+1 ) + s = abs( v1 ) + abs( v2 ) + abs( v3 ) + v1 = v1 / s + v2 = v2 / s + v3 = v3 / s + v( 1 ) = v1 + v( 2 ) = v2 + v( 3 ) = v3 + if( m.eq.l ) + & go to 50 + h00 = h( m-1, m-1 ) + h10 = h( m, m-1 ) + tst1 = abs( v1 )*( abs( h00 )+abs( h11 )+abs( h22 ) ) + if( abs( h10 )*( abs( v2 )+abs( v3 ) ).le.ulp*tst1 ) + & go to 50 + 40 continue + 50 continue +c +c %----------------------% +c | Double-shift QR step | +c %----------------------% +c + do 120 k = m, i - 1 +c +c ------------------------------------------------------------ +c The first iteration of this loop determines a reflection G +c from the vector V and applies it from left and right to H, +c thus creating a nonzero bulge below the subdiagonal. +c +c Each subsequent iteration determines a reflection G to +c restore the Hessenberg form in the (K-1)th column, and thus +c chases the bulge one step toward the bottom of the active +c submatrix. NR is the order of G. +c ------------------------------------------------------------ +c + nr = min( 3, i-k+1 ) + if( k.gt.m ) + & call dcopy( nr, h( k, k-1 ), 1, v, 1 ) + call dlarfg( nr, v( 1 ), v( 2 ), 1, t1 ) + if( k.gt.m ) then + h( k, k-1 ) = v( 1 ) + h( k+1, k-1 ) = zero + if( k.lt.i-1 ) + & h( k+2, k-1 ) = zero + else if( m.gt.l ) then + h( k, k-1 ) = -h( k, k-1 ) + end if + v2 = v( 2 ) + t2 = t1*v2 + if( nr.eq.3 ) then + v3 = v( 3 ) + t3 = t1*v3 +c +c %------------------------------------------------% +c | Apply G from the left to transform the rows of | +c | the matrix in columns K to I2. | +c %------------------------------------------------% +c + do 60 j = k, i2 + sum = h( k, j ) + v2*h( k+1, j ) + v3*h( k+2, j ) + h( k, j ) = h( k, j ) - sum*t1 + h( k+1, j ) = h( k+1, j ) - sum*t2 + h( k+2, j ) = h( k+2, j ) - sum*t3 + 60 continue +c +c %----------------------------------------------------% +c | Apply G from the right to transform the columns of | +c | the matrix in rows I1 to min(K+3,I). | +c %----------------------------------------------------% +c + do 70 j = i1, min( k+3, i ) + sum = h( j, k ) + v2*h( j, k+1 ) + v3*h( j, k+2 ) + h( j, k ) = h( j, k ) - sum*t1 + h( j, k+1 ) = h( j, k+1 ) - sum*t2 + h( j, k+2 ) = h( j, k+2 ) - sum*t3 + 70 continue +c +c %----------------------------------% +c | Accumulate transformations for Z | +c %----------------------------------% +c + sum = z( k ) + v2*z( k+1 ) + v3*z( k+2 ) + z( k ) = z( k ) - sum*t1 + z( k+1 ) = z( k+1 ) - sum*t2 + z( k+2 ) = z( k+2 ) - sum*t3 + + else if( nr.eq.2 ) then +c +c %------------------------------------------------% +c | Apply G from the left to transform the rows of | +c | the matrix in columns K to I2. | +c %------------------------------------------------% +c + do 90 j = k, i2 + sum = h( k, j ) + v2*h( k+1, j ) + h( k, j ) = h( k, j ) - sum*t1 + h( k+1, j ) = h( k+1, j ) - sum*t2 + 90 continue +c +c %----------------------------------------------------% +c | Apply G from the right to transform the columns of | +c | the matrix in rows I1 to min(K+3,I). | +c %----------------------------------------------------% +c + do 100 j = i1, i + sum = h( j, k ) + v2*h( j, k+1 ) + h( j, k ) = h( j, k ) - sum*t1 + h( j, k+1 ) = h( j, k+1 ) - sum*t2 + 100 continue +c +c %----------------------------------% +c | Accumulate transformations for Z | +c %----------------------------------% +c + sum = z( k ) + v2*z( k+1 ) + z( k ) = z( k ) - sum*t1 + z( k+1 ) = z( k+1 ) - sum*t2 + end if + 120 continue + + 130 continue +c +c %-------------------------------------------------------% +c | Failure to converge in remaining number of iterations | +c %-------------------------------------------------------% +c + info = i + return + + 140 continue + + if( l.eq.i ) then +c +c %------------------------------------------------------% +c | H(I,I-1) is negligible: one eigenvalue has converged | +c %------------------------------------------------------% +c + wr( i ) = h( i, i ) + wi( i ) = zero + + else if( l.eq.i-1 ) then +c +c %--------------------------------------------------------% +c | H(I-1,I-2) is negligible; | +c | a pair of eigenvalues have converged. | +c | | +c | Transform the 2-by-2 submatrix to standard Schur form, | +c | and compute and store the eigenvalues. | +c %--------------------------------------------------------% +c + call dlanv2( h( i-1, i-1 ), h( i-1, i ), h( i, i-1 ), + & h( i, i ), wr( i-1 ), wi( i-1 ), wr( i ), wi( i ), + & cs, sn ) + + if( wantt ) then +c +c %-----------------------------------------------------% +c | Apply the transformation to the rest of H and to Z, | +c | as required. | +c %-----------------------------------------------------% +c + if( i2.gt.i ) + & call drot( i2-i, h( i-1, i+1 ), ldh, h( i, i+1 ), ldh, + & cs, sn ) + call drot( i-i1-1, h( i1, i-1 ), 1, h( i1, i ), 1, cs, sn ) + sum = cs*z( i-1 ) + sn*z( i ) + z( i ) = cs*z( i ) - sn*z( i-1 ) + z( i-1 ) = sum + end if + end if +c +c %---------------------------------------------------------% +c | Decrement number of remaining iterations, and return to | +c | start of the main loop with new value of I. | +c %---------------------------------------------------------% +c + itn = itn - its + i = l - 1 + go to 10 + + 150 continue + return +c +c %---------------% +c | End of igraphdlaqrb | +c %---------------% +c + end diff --git a/src/rigraph/vendor/arpack/dmout.f b/src/rigraph/vendor/arpack/dmout.f new file mode 100644 index 0000000..6204d64 --- /dev/null +++ b/src/rigraph/vendor/arpack/dmout.f @@ -0,0 +1,167 @@ +*----------------------------------------------------------------------- +* 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 IGRAPHDMOUT( LOUT, M, N, A, LDA, IDIGIT, IFMT ) +* ... +* ... SPECIFICATIONS FOR ARGUMENTS +* ... +* ... SPECIFICATIONS FOR LOCAL VARIABLES +* .. Scalar Arguments .. + CHARACTER*( * ) IFMT + INTEGER IDIGIT, LDA, LOUT, M, N +* .. +* .. Array Arguments .. + DOUBLE PRECISION A( LDA, * ) +* .. +* .. Local Scalars .. + CHARACTER*80 LINE + INTEGER I, J, K1, K2, LLL, NDIGIT +* .. +* .. Local Arrays .. + CHARACTER ICOL( 3 ) +* .. +* .. Intrinsic Functions .. + INTRINSIC LEN, MIN, MIN0 +* .. +* .. Data statements .. + DATA ICOL( 1 ), ICOL( 2 ), ICOL( 3 ) / 'C', 'o', + $ 'l' / +* .. +* .. Executable Statements .. +* ... +* ... FIRST EXECUTABLE STATEMENT +* +c$$$ LLL = MIN( LEN( IFMT ), 80 ) +c$$$ DO 10 I = 1, LLL +c$$$ LINE( I: I ) = '-' +c$$$ 10 CONTINUE +c$$$* +c$$$ DO 20 I = LLL + 1, 80 +c$$$ LINE( I: I ) = ' ' +c$$$ 20 CONTINUE +c$$$* +c$$$ WRITE( LOUT, FMT = 9999 )IFMT, LINE( 1: LLL ) +c$$$ 9999 FORMAT( / 1X, A, / 1X, A ) +c$$$* +c$$$ IF( M.LE.0 .OR. N.LE.0 .OR. LDA.LE.0 ) +c$$$ $ RETURN +c$$$ NDIGIT = IDIGIT +c$$$ IF( IDIGIT.EQ.0 ) +c$$$ $ NDIGIT = 4 +c$$$* +c$$$*======================================================================= +c$$$* CODE FOR OUTPUT USING 72 COLUMNS FORMAT +c$$$*======================================================================= +c$$$* +c$$$ IF( IDIGIT.LT.0 ) THEN +c$$$ NDIGIT = -IDIGIT +c$$$ IF( NDIGIT.LE.4 ) THEN +c$$$ DO 40 K1 = 1, N, 5 +c$$$ K2 = MIN0( N, K1+4 ) +c$$$ WRITE( LOUT, FMT = 9998 )( ICOL, I, I = K1, K2 ) +c$$$ DO 30 I = 1, M +c$$$ WRITE( LOUT, FMT = 9994 )I, ( A( I, J ), J = K1, K2 ) +c$$$ 30 CONTINUE +c$$$ 40 CONTINUE +c$$$* +c$$$ ELSE IF( NDIGIT.LE.6 ) THEN +c$$$ DO 60 K1 = 1, N, 4 +c$$$ K2 = MIN0( N, K1+3 ) +c$$$ WRITE( LOUT, FMT = 9997 )( ICOL, I, I = K1, K2 ) +c$$$ DO 50 I = 1, M +c$$$ WRITE( LOUT, FMT = 9993 )I, ( A( I, J ), J = K1, K2 ) +c$$$ 50 CONTINUE +c$$$ 60 CONTINUE +c$$$* +c$$$ ELSE IF( NDIGIT.LE.10 ) THEN +c$$$ DO 80 K1 = 1, N, 3 +c$$$ K2 = MIN0( N, K1+2 ) +c$$$ WRITE( LOUT, FMT = 9996 )( ICOL, I, I = K1, K2 ) +c$$$ DO 70 I = 1, M +c$$$ WRITE( LOUT, FMT = 9992 )I, ( A( I, J ), J = K1, K2 ) +c$$$ 70 CONTINUE +c$$$ 80 CONTINUE +c$$$* +c$$$ ELSE +c$$$ DO 100 K1 = 1, N, 2 +c$$$ K2 = MIN0( N, K1+1 ) +c$$$ WRITE( LOUT, FMT = 9995 )( ICOL, I, I = K1, K2 ) +c$$$ DO 90 I = 1, M +c$$$ WRITE( LOUT, FMT = 9991 )I, ( A( I, J ), J = K1, K2 ) +c$$$ 90 CONTINUE +c$$$ 100 CONTINUE +c$$$ END IF +c$$$* +c$$$*======================================================================= +c$$$* CODE FOR OUTPUT USING 132 COLUMNS FORMAT +c$$$*======================================================================= +c$$$* +c$$$ ELSE +c$$$ IF( NDIGIT.LE.4 ) THEN +c$$$ DO 120 K1 = 1, N, 10 +c$$$ K2 = MIN0( N, K1+9 ) +c$$$ WRITE( LOUT, FMT = 9998 )( ICOL, I, I = K1, K2 ) +c$$$ DO 110 I = 1, M +c$$$ WRITE( LOUT, FMT = 9994 )I, ( A( I, J ), J = K1, K2 ) +c$$$ 110 CONTINUE +c$$$ 120 CONTINUE +c$$$* +c$$$ ELSE IF( NDIGIT.LE.6 ) THEN +c$$$ DO 140 K1 = 1, N, 8 +c$$$ K2 = MIN0( N, K1+7 ) +c$$$ WRITE( LOUT, FMT = 9997 )( ICOL, I, I = K1, K2 ) +c$$$ DO 130 I = 1, M +c$$$ WRITE( LOUT, FMT = 9993 )I, ( A( I, J ), J = K1, K2 ) +c$$$ 130 CONTINUE +c$$$ 140 CONTINUE +c$$$* +c$$$ ELSE IF( NDIGIT.LE.10 ) THEN +c$$$ DO 160 K1 = 1, N, 6 +c$$$ K2 = MIN0( N, K1+5 ) +c$$$ WRITE( LOUT, FMT = 9996 )( ICOL, I, I = K1, K2 ) +c$$$ DO 150 I = 1, M +c$$$ WRITE( LOUT, FMT = 9992 )I, ( A( I, J ), J = K1, K2 ) +c$$$ 150 CONTINUE +c$$$ 160 CONTINUE +c$$$* +c$$$ ELSE +c$$$ DO 180 K1 = 1, N, 5 +c$$$ K2 = MIN0( N, K1+4 ) +c$$$ WRITE( LOUT, FMT = 9995 )( ICOL, I, I = K1, K2 ) +c$$$ DO 170 I = 1, M +c$$$ WRITE( LOUT, FMT = 9991 )I, ( A( I, J ), J = K1, K2 ) +c$$$ 170 CONTINUE +c$$$ 180 CONTINUE +c$$$ END IF +c$$$ END IF +c$$$ WRITE( LOUT, FMT = 9990 ) +c$$$* +c$$$ 9998 FORMAT( 10X, 10( 4X, 3A1, I4, 1X ) ) +c$$$ 9997 FORMAT( 10X, 8( 5X, 3A1, I4, 2X ) ) +c$$$ 9996 FORMAT( 10X, 6( 7X, 3A1, I4, 4X ) ) +c$$$ 9995 FORMAT( 10X, 5( 9X, 3A1, I4, 6X ) ) +c$$$ 9994 FORMAT( 1X, ' Row', I4, ':', 1X, 1P, 10D12.3 ) +c$$$ 9993 FORMAT( 1X, ' Row', I4, ':', 1X, 1P, 8D14.5 ) +c$$$ 9992 FORMAT( 1X, ' Row', I4, ':', 1X, 1P, 6D18.9 ) +c$$$ 9991 FORMAT( 1X, ' Row', I4, ':', 1X, 1P, 5D22.13 ) +c$$$ 9990 FORMAT( 1X, ' ' ) +* + RETURN + END diff --git a/src/rigraph/vendor/arpack/dnaitr.f b/src/rigraph/vendor/arpack/dnaitr.f new file mode 100644 index 0000000..8ec2569 --- /dev/null +++ b/src/rigraph/vendor/arpack/dnaitr.f @@ -0,0 +1,840 @@ +c----------------------------------------------------------------------- +c\BeginDoc +c +c\Name: igraphdnaitr +c +c\Description: +c Reverse communication interface for applying NP additional steps to +c a K step nonsymmetric Arnoldi factorization. +c +c Input: OP*V_{k} - V_{k}*H = r_{k}*e_{k}^T +c +c with (V_{k}^T)*B*V_{k} = I, (V_{k}^T)*B*r_{k} = 0. +c +c Output: OP*V_{k+p} - V_{k+p}*H = r_{k+p}*e_{k+p}^T +c +c with (V_{k+p}^T)*B*V_{k+p} = I, (V_{k+p}^T)*B*r_{k+p} = 0. +c +c where OP and B are as in igraphdnaupd. The B-norm of r_{k+p} is also +c computed and returned. +c +c\Usage: +c call igraphdnaitr +c ( IDO, BMAT, N, K, NP, NB, RESID, RNORM, V, LDV, H, LDH, +c IPNTR, WORKD, INFO ) +c +c\Arguments +c IDO Integer. (INPUT/OUTPUT) +c Reverse communication flag. +c ------------------------------------------------------------- +c IDO = 0: first call to the reverse communication interface +c IDO = -1: compute Y = OP * X where +c IPNTR(1) is the pointer into WORK for X, +c IPNTR(2) is the pointer into WORK for Y. +c This is for the restart phase to force the new +c starting vector into the range of OP. +c IDO = 1: compute Y = OP * X where +c IPNTR(1) is the pointer into WORK for X, +c IPNTR(2) is the pointer into WORK for Y, +c IPNTR(3) is the pointer into WORK for B * X. +c IDO = 2: compute Y = B * X where +c IPNTR(1) is the pointer into WORK for X, +c IPNTR(2) is the pointer into WORK for Y. +c IDO = 99: done +c ------------------------------------------------------------- +c When the routine is used in the "shift-and-invert" mode, the +c vector B * Q is already available and do not need to be +c recompute in forming OP * Q. +c +c BMAT Character*1. (INPUT) +c BMAT specifies the type of the matrix B that defines the +c semi-inner product for the operator OP. See igraphdnaupd. +c B = 'I' -> standard eigenvalue problem A*x = lambda*x +c B = 'G' -> generalized eigenvalue problem A*x = lambda*M**x +c +c N Integer. (INPUT) +c Dimension of the eigenproblem. +c +c K Integer. (INPUT) +c Current size of V and H. +c +c NP Integer. (INPUT) +c Number of additional Arnoldi steps to take. +c +c NB Integer. (INPUT) +c Blocksize to be used in the recurrence. +c Only work for NB = 1 right now. The goal is to have a +c program that implement both the block and non-block method. +c +c RESID Double precision array of length N. (INPUT/OUTPUT) +c On INPUT: RESID contains the residual vector r_{k}. +c On OUTPUT: RESID contains the residual vector r_{k+p}. +c +c RNORM Double precision scalar. (INPUT/OUTPUT) +c B-norm of the starting residual on input. +c B-norm of the updated residual r_{k+p} on output. +c +c V Double precision N by K+NP array. (INPUT/OUTPUT) +c On INPUT: V contains the Arnoldi vectors in the first K +c columns. +c On OUTPUT: V contains the new NP Arnoldi vectors in the next +c NP columns. The first K columns are unchanged. +c +c LDV Integer. (INPUT) +c Leading dimension of V exactly as declared in the calling +c program. +c +c H Double precision (K+NP) by (K+NP) array. (INPUT/OUTPUT) +c H is used to store the generated upper Hessenberg matrix. +c +c LDH Integer. (INPUT) +c Leading dimension of H exactly as declared in the calling +c program. +c +c IPNTR Integer array of length 3. (OUTPUT) +c Pointer to mark the starting locations in the WORK for +c vectors used by the Arnoldi iteration. +c ------------------------------------------------------------- +c IPNTR(1): pointer to the current operand vector X. +c IPNTR(2): pointer to the current result vector Y. +c IPNTR(3): pointer to the vector B * X when used in the +c shift-and-invert mode. X is the current operand. +c ------------------------------------------------------------- +c +c WORKD Double precision work array of length 3*N. (REVERSE COMMUNICATION) +c Distributed array to be used in the basic Arnoldi iteration +c for reverse communication. The calling program should not +c use WORKD as temporary workspace during the iteration !!!!!! +c On input, WORKD(1:N) = B*RESID and is used to save some +c computation at the first step. +c +c INFO Integer. (OUTPUT) +c = 0: Normal exit. +c > 0: Size of the spanning invariant subspace of OP found. +c +c\EndDoc +c +c----------------------------------------------------------------------- +c +c\BeginLib +c +c\Local variables: +c xxxxxx real +c +c\References: +c 1. D.C. Sorensen, "Implicit Application of Polynomial Filters in +c a k-Step Arnoldi Method", SIAM J. Matr. Anal. Apps., 13 (1992), +c pp 357-385. +c 2. R.B. Lehoucq, "Analysis and Implementation of an Implicitly +c Restarted Arnoldi Iteration", Rice University Technical Report +c TR95-13, Department of Computational and Applied Mathematics. +c +c\Routines called: +c igraphdgetv0 ARPACK routine to generate the initial vector. +c igraphivout ARPACK utility routine that prints integers. +c igraphsecond ARPACK utility routine for timing. +c igraphdmout ARPACK utility routine that prints matrices +c igraphdvout ARPACK utility routine that prints vectors. +c dlabad LAPACK routine that computes machine constants. +c dlamch LAPACK routine that determines machine constants. +c dlascl LAPACK routine for careful scaling of a matrix. +c dlanhs LAPACK routine that computes various norms of a matrix. +c dgemv Level 2 BLAS routine for matrix vector multiplication. +c daxpy Level 1 BLAS that computes a vector triad. +c dscal Level 1 BLAS that scales a vector. +c dcopy Level 1 BLAS that copies one vector to another . +c ddot Level 1 BLAS that computes the scalar product of two vectors. +c dnrm2 Level 1 BLAS that computes the norm of a vector. +c +c\Author +c Danny Sorensen Phuong Vu +c Richard Lehoucq CRPC / Rice University +c Dept. of Computational & Houston, Texas +c Applied Mathematics +c Rice University +c Houston, Texas +c +c\Revision history: +c xx/xx/92: Version ' 2.4' +c +c\SCCS Information: @(#) +c FILE: naitr.F SID: 2.4 DATE OF SID: 8/27/96 RELEASE: 2 +c +c\Remarks +c The algorithm implemented is: +c +c restart = .false. +c Given V_{k} = [v_{1}, ..., v_{k}], r_{k}; +c r_{k} contains the initial residual vector even for k = 0; +c Also assume that rnorm = || B*r_{k} || and B*r_{k} are already +c computed by the calling program. +c +c betaj = rnorm ; p_{k+1} = B*r_{k} ; +c For j = k+1, ..., k+np Do +c 1) if ( betaj < tol ) stop or restart depending on j. +c ( At present tol is zero ) +c if ( restart ) generate a new starting vector. +c 2) v_{j} = r(j-1)/betaj; V_{j} = [V_{j-1}, v_{j}]; +c p_{j} = p_{j}/betaj +c 3) r_{j} = OP*v_{j} where OP is defined as in igraphdnaupd +c For shift-invert mode p_{j} = B*v_{j} is already available. +c wnorm = || OP*v_{j} || +c 4) Compute the j-th step residual vector. +c w_{j} = V_{j}^T * B * OP * v_{j} +c r_{j} = OP*v_{j} - V_{j} * w_{j} +c H(:,j) = w_{j}; +c H(j,j-1) = rnorm +c rnorm = || r_(j) || +c If (rnorm > 0.717*wnorm) accept step and go back to 1) +c 5) Re-orthogonalization step: +c s = V_{j}'*B*r_{j} +c r_{j} = r_{j} - V_{j}*s; rnorm1 = || r_{j} || +c alphaj = alphaj + s_{j}; +c 6) Iterative refinement step: +c If (rnorm1 > 0.717*rnorm) then +c rnorm = rnorm1 +c accept step and go back to 1) +c Else +c rnorm = rnorm1 +c If this is the first time in step 6), go to 5) +c Else r_{j} lies in the span of V_{j} numerically. +c Set r_{j} = 0 and rnorm = 0; go to 1) +c EndIf +c End Do +c +c\EndLib +c +c----------------------------------------------------------------------- +c + subroutine igraphdnaitr + & (ido, bmat, n, k, np, nb, resid, rnorm, v, ldv, h, ldh, + & ipntr, workd, info) +c +c %----------------------------------------------------% +c | Include files for debugging and timing information | +c %----------------------------------------------------% +c + include 'debug.h' + include 'stat.h' +c +c %------------------% +c | Scalar Arguments | +c %------------------% +c + character bmat*1 + integer ido, info, k, ldh, ldv, n, nb, np + Double precision + & rnorm +c +c %-----------------% +c | Array Arguments | +c %-----------------% +c + integer ipntr(3) + Double precision + & h(ldh,k+np), resid(n), v(ldv,k+np), workd(3*n) +c +c %------------% +c | Parameters | +c %------------% +c + Double precision + & one, zero + parameter (one = 1.0D+0, zero = 0.0D+0) +c +c %---------------% +c | Local Scalars | +c %---------------% +c + logical first, orth1, orth2, rstart, step3, step4 + integer ierr, i, infol, ipj, irj, ivj, iter, itry, j, msglvl, + & jj + Double precision + & betaj, ovfl, temp1, rnorm1, smlnum, tst1, ulp, unfl, + & wnorm + save first, orth1, orth2, rstart, step3, step4, + & ierr, ipj, irj, ivj, iter, itry, j, msglvl, ovfl, + & betaj, rnorm1, smlnum, ulp, unfl, wnorm +c +c %-----------------------% +c | Local Array Arguments | +c %-----------------------% +c + Double precision + & xtemp(2) +c +c %----------------------% +c | External Subroutines | +c %----------------------% +c + external daxpy, dcopy, dscal, dgemv, igraphdgetv0, dlabad, + & igraphdvout, igraphdmout, igraphivout, igraphsecond +c +c %--------------------% +c | External Functions | +c %--------------------% +c + Double precision + & ddot, dnrm2, dlanhs, dlamch + external ddot, dnrm2, dlanhs, dlamch +c +c %---------------------% +c | Intrinsic Functions | +c %---------------------% +c + intrinsic abs, sqrt +c +c %-----------------% +c | Data statements | +c %-----------------% +c + data first / .true. / +c +c %-----------------------% +c | Executable Statements | +c %-----------------------% +c + if (first) then +c +c %-----------------------------------------% +c | Set machine-dependent constants for the | +c | the splitting and deflation criterion. | +c | If norm(H) <= sqrt(OVFL), | +c | overflow should not occur. | +c | REFERENCE: LAPACK subroutine dlahqr | +c %-----------------------------------------% +c + unfl = dlamch( 'safe minimum' ) + ovfl = one / unfl + call dlabad( unfl, ovfl ) + ulp = dlamch( 'precision' ) + smlnum = unfl*( n / ulp ) + first = .false. + end if +c + if (ido .eq. 0) then +c +c %-------------------------------% +c | Initialize timing statistics | +c | & message level for debugging | +c %-------------------------------% +c + call igraphsecond (t0) + msglvl = mnaitr +c +c %------------------------------% +c | Initial call to this routine | +c %------------------------------% +c + info = 0 + step3 = .false. + step4 = .false. + rstart = .false. + orth1 = .false. + orth2 = .false. + j = k + 1 + ipj = 1 + irj = ipj + n + ivj = irj + n + end if +c +c %-------------------------------------------------% +c | When in reverse communication mode one of: | +c | STEP3, STEP4, ORTH1, ORTH2, RSTART | +c | will be .true. when .... | +c | STEP3: return from computing OP*v_{j}. | +c | STEP4: return from computing B-norm of OP*v_{j} | +c | ORTH1: return from computing B-norm of r_{j+1} | +c | ORTH2: return from computing B-norm of | +c | correction to the residual vector. | +c | RSTART: return from OP computations needed by | +c | igraphdgetv0. | +c %-------------------------------------------------% +c + if (step3) go to 50 + if (step4) go to 60 + if (orth1) go to 70 + if (orth2) go to 90 + if (rstart) go to 30 +c +c %-----------------------------% +c | Else this is the first step | +c %-----------------------------% +c +c %--------------------------------------------------------------% +c | | +c | A R N O L D I I T E R A T I O N L O O P | +c | | +c | Note: B*r_{j-1} is already in WORKD(1:N)=WORKD(IPJ:IPJ+N-1) | +c %--------------------------------------------------------------% + + 1000 continue +c + if (msglvl .gt. 1) then + call igraphivout (logfil, 1, [j], ndigit, + & '_naitr: generating Arnoldi vector number') + call igraphdvout (logfil, 1, [rnorm], ndigit, + & '_naitr: B-norm of the current residual is') + end if +c +c %---------------------------------------------------% +c | STEP 1: Check if the B norm of j-th residual | +c | vector is zero. Equivalent to determing whether | +c | an exact j-step Arnoldi factorization is present. | +c %---------------------------------------------------% +c + betaj = rnorm + if (rnorm .gt. zero) go to 40 +c +c %---------------------------------------------------% +c | Invariant subspace found, generate a new starting | +c | vector which is orthogonal to the current Arnoldi | +c | basis and continue the iteration. | +c %---------------------------------------------------% +c + if (msglvl .gt. 0) then + call igraphivout (logfil, 1, [j], ndigit, + & '_naitr: ****** RESTART AT STEP ******') + end if +c +c %---------------------------------------------% +c | ITRY is the loop variable that controls the | +c | maximum amount of times that a restart is | +c | attempted. NRSTRT is used by stat.h | +c %---------------------------------------------% +c + betaj = zero + nrstrt = nrstrt + 1 + itry = 1 + 20 continue + rstart = .true. + ido = 0 + 30 continue +c +c %--------------------------------------% +c | If in reverse communication mode and | +c | RSTART = .true. flow returns here. | +c %--------------------------------------% +c + call igraphdgetv0 (ido, bmat, itry, .false., n, j, v, ldv, + & resid, rnorm, ipntr, workd, ierr) + if (ido .ne. 99) go to 9000 + if (ierr .lt. 0) then + itry = itry + 1 + if (itry .le. 3) go to 20 +c +c %------------------------------------------------% +c | Give up after several restart attempts. | +c | Set INFO to the size of the invariant subspace | +c | which spans OP and exit. | +c %------------------------------------------------% +c + info = j - 1 + call igraphsecond (t1) + tnaitr = tnaitr + (t1 - t0) + ido = 99 + go to 9000 + end if +c + 40 continue +c +c %---------------------------------------------------------% +c | STEP 2: v_{j} = r_{j-1}/rnorm and p_{j} = p_{j}/rnorm | +c | Note that p_{j} = B*r_{j-1}. In order to avoid overflow | +c | when reciprocating a small RNORM, test against lower | +c | machine bound. | +c %---------------------------------------------------------% +c + call dcopy (n, resid, 1, v(1,j), 1) + if (rnorm .ge. unfl) then + temp1 = one / rnorm + call dscal (n, temp1, v(1,j), 1) + call dscal (n, temp1, workd(ipj), 1) + else +c +c %-----------------------------------------% +c | To scale both v_{j} and p_{j} carefully | +c | use LAPACK routine SLASCL | +c %-----------------------------------------% +c + call dlascl ('General', i, i, rnorm, one, n, 1, + & v(1,j), n, infol) + call dlascl ('General', i, i, rnorm, one, n, 1, + & workd(ipj), n, infol) + end if +c +c %------------------------------------------------------% +c | STEP 3: r_{j} = OP*v_{j}; Note that p_{j} = B*v_{j} | +c | Note that this is not quite yet r_{j}. See STEP 4 | +c %------------------------------------------------------% +c + step3 = .true. + nopx = nopx + 1 + call igraphsecond (t2) + call dcopy (n, v(1,j), 1, workd(ivj), 1) + ipntr(1) = ivj + ipntr(2) = irj + ipntr(3) = ipj + ido = 1 +c +c %-----------------------------------% +c | Exit in order to compute OP*v_{j} | +c %-----------------------------------% +c + go to 9000 + 50 continue +c +c %----------------------------------% +c | Back from reverse communication; | +c | WORKD(IRJ:IRJ+N-1) := OP*v_{j} | +c | if step3 = .true. | +c %----------------------------------% +c + call igraphsecond (t3) + tmvopx = tmvopx + (t3 - t2) + + step3 = .false. +c +c %------------------------------------------% +c | Put another copy of OP*v_{j} into RESID. | +c %------------------------------------------% +c + call dcopy (n, workd(irj), 1, resid, 1) +c +c %---------------------------------------% +c | STEP 4: Finish extending the Arnoldi | +c | factorization to length j. | +c %---------------------------------------% +c + call igraphsecond (t2) + if (bmat .eq. 'G') then + nbx = nbx + 1 + step4 = .true. + ipntr(1) = irj + ipntr(2) = ipj + ido = 2 +c +c %-------------------------------------% +c | Exit in order to compute B*OP*v_{j} | +c %-------------------------------------% +c + go to 9000 + else if (bmat .eq. 'I') then + call dcopy (n, resid, 1, workd(ipj), 1) + end if + 60 continue +c +c %----------------------------------% +c | Back from reverse communication; | +c | WORKD(IPJ:IPJ+N-1) := B*OP*v_{j} | +c | if step4 = .true. | +c %----------------------------------% +c + if (bmat .eq. 'G') then + call igraphsecond (t3) + tmvbx = tmvbx + (t3 - t2) + end if +c + step4 = .false. +c +c %-------------------------------------% +c | The following is needed for STEP 5. | +c | Compute the B-norm of OP*v_{j}. | +c %-------------------------------------% +c + if (bmat .eq. 'G') then + wnorm = ddot (n, resid, 1, workd(ipj), 1) + wnorm = sqrt(abs(wnorm)) + else if (bmat .eq. 'I') then + wnorm = dnrm2(n, resid, 1) + end if +c +c %-----------------------------------------% +c | Compute the j-th residual corresponding | +c | to the j step factorization. | +c | Use Classical Gram Schmidt and compute: | +c | w_{j} <- V_{j}^T * B * OP * v_{j} | +c | r_{j} <- OP*v_{j} - V_{j} * w_{j} | +c %-----------------------------------------% +c +c +c %------------------------------------------% +c | Compute the j Fourier coefficients w_{j} | +c | WORKD(IPJ:IPJ+N-1) contains B*OP*v_{j}. | +c %------------------------------------------% +c + call dgemv ('T', n, j, one, v, ldv, workd(ipj), 1, + & zero, h(1,j), 1) +c +c %--------------------------------------% +c | Orthogonalize r_{j} against V_{j}. | +c | RESID contains OP*v_{j}. See STEP 3. | +c %--------------------------------------% +c + call dgemv ('N', n, j, -one, v, ldv, h(1,j), 1, + & one, resid, 1) +c + if (j .gt. 1) h(j,j-1) = betaj +c + call igraphsecond (t4) +c + orth1 = .true. +c + call igraphsecond (t2) + if (bmat .eq. 'G') then + nbx = nbx + 1 + call dcopy (n, resid, 1, workd(irj), 1) + ipntr(1) = irj + ipntr(2) = ipj + ido = 2 +c +c %----------------------------------% +c | Exit in order to compute B*r_{j} | +c %----------------------------------% +c + go to 9000 + else if (bmat .eq. 'I') then + call dcopy (n, resid, 1, workd(ipj), 1) + end if + 70 continue +c +c %---------------------------------------------------% +c | Back from reverse communication if ORTH1 = .true. | +c | WORKD(IPJ:IPJ+N-1) := B*r_{j}. | +c %---------------------------------------------------% +c + if (bmat .eq. 'G') then + call igraphsecond (t3) + tmvbx = tmvbx + (t3 - t2) + end if +c + orth1 = .false. +c +c %------------------------------% +c | Compute the B-norm of r_{j}. | +c %------------------------------% +c + if (bmat .eq. 'G') then + rnorm = ddot (n, resid, 1, workd(ipj), 1) + rnorm = sqrt(abs(rnorm)) + else if (bmat .eq. 'I') then + rnorm = dnrm2(n, resid, 1) + end if +c +c %-----------------------------------------------------------% +c | STEP 5: Re-orthogonalization / Iterative refinement phase | +c | Maximum NITER_ITREF tries. | +c | | +c | s = V_{j}^T * B * r_{j} | +c | r_{j} = r_{j} - V_{j}*s | +c | alphaj = alphaj + s_{j} | +c | | +c | The stopping criteria used for iterative refinement is | +c | discussed in Parlett's book SEP, page 107 and in Gragg & | +c | Reichel ACM TOMS paper; Algorithm 686, Dec. 1990. | +c | Determine if we need to correct the residual. The goal is | +c | to enforce ||v(:,1:j)^T * r_{j}|| .le. eps * || r_{j} || | +c | The following test determines whether the sine of the | +c | angle between OP*x and the computed residual is less | +c | than or equal to 0.717. | +c %-----------------------------------------------------------% +c + if (rnorm .gt. 0.717*wnorm) go to 100 + iter = 0 + nrorth = nrorth + 1 +c +c %---------------------------------------------------% +c | Enter the Iterative refinement phase. If further | +c | refinement is necessary, loop back here. The loop | +c | variable is ITER. Perform a step of Classical | +c | Gram-Schmidt using all the Arnoldi vectors V_{j} | +c %---------------------------------------------------% +c + 80 continue +c + if (msglvl .gt. 2) then + xtemp(1) = wnorm + xtemp(2) = rnorm + call igraphdvout (logfil, 2, xtemp, ndigit, + & '_naitr: re-orthonalization; wnorm and rnorm are') + call igraphdvout (logfil, j, h(1,j), ndigit, + & '_naitr: j-th column of H') + end if +c +c %----------------------------------------------------% +c | Compute V_{j}^T * B * r_{j}. | +c | WORKD(IRJ:IRJ+J-1) = v(:,1:J)'*WORKD(IPJ:IPJ+N-1). | +c %----------------------------------------------------% +c + call dgemv ('T', n, j, one, v, ldv, workd(ipj), 1, + & zero, workd(irj), 1) +c +c %---------------------------------------------% +c | Compute the correction to the residual: | +c | r_{j} = r_{j} - V_{j} * WORKD(IRJ:IRJ+J-1). | +c | The correction to H is v(:,1:J)*H(1:J,1:J) | +c | + v(:,1:J)*WORKD(IRJ:IRJ+J-1)*e'_j. | +c %---------------------------------------------% +c + call dgemv ('N', n, j, -one, v, ldv, workd(irj), 1, + & one, resid, 1) + call daxpy (j, one, workd(irj), 1, h(1,j), 1) +c + orth2 = .true. + call igraphsecond (t2) + if (bmat .eq. 'G') then + nbx = nbx + 1 + call dcopy (n, resid, 1, workd(irj), 1) + ipntr(1) = irj + ipntr(2) = ipj + ido = 2 +c +c %-----------------------------------% +c | Exit in order to compute B*r_{j}. | +c | r_{j} is the corrected residual. | +c %-----------------------------------% +c + go to 9000 + else if (bmat .eq. 'I') then + call dcopy (n, resid, 1, workd(ipj), 1) + end if + 90 continue +c +c %---------------------------------------------------% +c | Back from reverse communication if ORTH2 = .true. | +c %---------------------------------------------------% +c + if (bmat .eq. 'G') then + call igraphsecond (t3) + tmvbx = tmvbx + (t3 - t2) + end if +c +c %-----------------------------------------------------% +c | Compute the B-norm of the corrected residual r_{j}. | +c %-----------------------------------------------------% +c + if (bmat .eq. 'G') then + rnorm1 = ddot (n, resid, 1, workd(ipj), 1) + rnorm1 = sqrt(abs(rnorm1)) + else if (bmat .eq. 'I') then + rnorm1 = dnrm2(n, resid, 1) + end if +c + if (msglvl .gt. 0 .and. iter .gt. 0) then + call igraphivout (logfil, 1, [j], ndigit, + & '_naitr: Iterative refinement for Arnoldi residual') + if (msglvl .gt. 2) then + xtemp(1) = rnorm + xtemp(2) = rnorm1 + call igraphdvout (logfil, 2, xtemp, ndigit, + & '_naitr: iterative refinement ; rnorm and rnorm1 are') + end if + end if +c +c %-----------------------------------------% +c | Determine if we need to perform another | +c | step of re-orthogonalization. | +c %-----------------------------------------% +c + if (rnorm1 .gt. 0.717*rnorm) then +c +c %---------------------------------------% +c | No need for further refinement. | +c | The cosine of the angle between the | +c | corrected residual vector and the old | +c | residual vector is greater than 0.717 | +c | In other words the corrected residual | +c | and the old residual vector share an | +c | angle of less than arcCOS(0.717) | +c %---------------------------------------% +c + rnorm = rnorm1 +c + else +c +c %-------------------------------------------% +c | Another step of iterative refinement step | +c | is required. NITREF is used by stat.h | +c %-------------------------------------------% +c + nitref = nitref + 1 + rnorm = rnorm1 + iter = iter + 1 + if (iter .le. 1) go to 80 +c +c %-------------------------------------------------% +c | Otherwise RESID is numerically in the span of V | +c %-------------------------------------------------% +c + do 95 jj = 1, n + resid(jj) = zero + 95 continue + rnorm = zero + end if +c +c %----------------------------------------------% +c | Branch here directly if iterative refinement | +c | wasn't necessary or after at most NITER_REF | +c | steps of iterative refinement. | +c %----------------------------------------------% +c + 100 continue +c + rstart = .false. + orth2 = .false. +c + call igraphsecond (t5) + titref = titref + (t5 - t4) +c +c %------------------------------------% +c | STEP 6: Update j = j+1; Continue | +c %------------------------------------% +c + j = j + 1 + if (j .gt. k+np) then + call igraphsecond (t1) + tnaitr = tnaitr + (t1 - t0) + ido = 99 + do 110 i = max(1,k), k+np-1 +c +c %--------------------------------------------% +c | Check for splitting and deflation. | +c | Use a standard test as in the QR algorithm | +c | REFERENCE: LAPACK subroutine dlahqr | +c %--------------------------------------------% +c + tst1 = abs( h( i, i ) ) + abs( h( i+1, i+1 ) ) + if( tst1.eq.zero ) + & tst1 = dlanhs( '1', k+np, h, ldh, workd(n+1) ) + if( abs( h( i+1,i ) ).le.max( ulp*tst1, smlnum ) ) + & h(i+1,i) = zero + 110 continue +c + if (msglvl .gt. 2) then + call igraphdmout (logfil, k+np, k+np, h, ldh, ndigit, + & '_naitr: Final upper Hessenberg matrix H of order K+NP') + end if +c + go to 9000 + end if +c +c %--------------------------------------------------------% +c | Loop back to extend the factorization by another step. | +c %--------------------------------------------------------% +c + go to 1000 +c +c %---------------------------------------------------------------% +c | | +c | E N D O F M A I N I T E R A T I O N L O O P | +c | | +c %---------------------------------------------------------------% +c + 9000 continue + return +c +c %---------------% +c | End of igraphdnaitr | +c %---------------% +c + end diff --git a/src/rigraph/vendor/arpack/dnapps.f b/src/rigraph/vendor/arpack/dnapps.f new file mode 100644 index 0000000..247e66b --- /dev/null +++ b/src/rigraph/vendor/arpack/dnapps.f @@ -0,0 +1,647 @@ +c----------------------------------------------------------------------- +c\BeginDoc +c +c\Name: igraphdnapps +c +c\Description: +c Given the Arnoldi factorization +c +c A*V_{k} - V_{k}*H_{k} = r_{k+p}*e_{k+p}^T, +c +c apply NP implicit shifts resulting in +c +c A*(V_{k}*Q) - (V_{k}*Q)*(Q^T* H_{k}*Q) = r_{k+p}*e_{k+p}^T * Q +c +c where Q is an orthogonal matrix which is the product of rotations +c and reflections resulting from the NP bulge chage sweeps. +c The updated Arnoldi factorization becomes: +c +c A*VNEW_{k} - VNEW_{k}*HNEW_{k} = rnew_{k}*e_{k}^T. +c +c\Usage: +c call igraphdnapps +c ( N, KEV, NP, SHIFTR, SHIFTI, V, LDV, H, LDH, RESID, Q, LDQ, +c WORKL, WORKD ) +c +c\Arguments +c N Integer. (INPUT) +c Problem size, i.e. size of matrix A. +c +c KEV Integer. (INPUT/OUTPUT) +c KEV+NP is the size of the input matrix H. +c KEV is the size of the updated matrix HNEW. KEV is only +c updated on ouput when fewer than NP shifts are applied in +c order to keep the conjugate pair together. +c +c NP Integer. (INPUT) +c Number of implicit shifts to be applied. +c +c SHIFTR, Double precision array of length NP. (INPUT) +c SHIFTI Real and imaginary part of the shifts to be applied. +c Upon, entry to igraphdnapps, the shifts must be sorted so that the +c conjugate pairs are in consecutive locations. +c +c V Double precision N by (KEV+NP) array. (INPUT/OUTPUT) +c On INPUT, V contains the current KEV+NP Arnoldi vectors. +c On OUTPUT, V contains the updated KEV Arnoldi vectors +c in the first KEV columns of V. +c +c LDV Integer. (INPUT) +c Leading dimension of V exactly as declared in the calling +c program. +c +c H Double precision (KEV+NP) by (KEV+NP) array. (INPUT/OUTPUT) +c On INPUT, H contains the current KEV+NP by KEV+NP upper +c Hessenber matrix of the Arnoldi factorization. +c On OUTPUT, H contains the updated KEV by KEV upper Hessenberg +c matrix in the KEV leading submatrix. +c +c LDH Integer. (INPUT) +c Leading dimension of H exactly as declared in the calling +c program. +c +c RESID Double precision array of length N. (INPUT/OUTPUT) +c On INPUT, RESID contains the the residual vector r_{k+p}. +c On OUTPUT, RESID is the update residual vector rnew_{k} +c in the first KEV locations. +c +c Q Double precision KEV+NP by KEV+NP work array. (WORKSPACE) +c Work array used to accumulate the rotations and reflections +c during the bulge chase sweep. +c +c LDQ Integer. (INPUT) +c Leading dimension of Q exactly as declared in the calling +c program. +c +c WORKL Double precision work array of length (KEV+NP). (WORKSPACE) +c Private (replicated) array on each PE or array allocated on +c the front end. +c +c WORKD Double precision work array of length 2*N. (WORKSPACE) +c Distributed array used in the application of the accumulated +c orthogonal matrix Q. +c +c\EndDoc +c +c----------------------------------------------------------------------- +c +c\BeginLib +c +c\Local variables: +c xxxxxx real +c +c\References: +c 1. D.C. Sorensen, "Implicit Application of Polynomial Filters in +c a k-Step Arnoldi Method", SIAM J. Matr. Anal. Apps., 13 (1992), +c pp 357-385. +c +c\Routines called: +c igraphivout ARPACK utility routine that prints integers. +c igraphsecond ARPACK utility routine for timing. +c igraphdmout ARPACK utility routine that prints matrices. +c igraphdvout ARPACK utility routine that prints vectors. +c dlabad LAPACK routine that computes machine constants. +c dlacpy LAPACK matrix copy routine. +c dlamch LAPACK routine that determines machine constants. +c dlanhs LAPACK routine that computes various norms of a matrix. +c dlapy2 LAPACK routine to compute sqrt(x**2+y**2) carefully. +c dlarf LAPACK routine that applies Householder reflection to +c a matrix. +c dlarfg LAPACK Householder reflection construction routine. +c dlartg LAPACK Givens rotation construction routine. +c dlaset LAPACK matrix initialization routine. +c dgemv Level 2 BLAS routine for matrix vector multiplication. +c daxpy Level 1 BLAS that computes a vector triad. +c dcopy Level 1 BLAS that copies one vector to another . +c dscal Level 1 BLAS that scales a vector. +c +c\Author +c Danny Sorensen Phuong Vu +c Richard Lehoucq CRPC / Rice University +c Dept. of Computational & Houston, Texas +c Applied Mathematics +c Rice University +c Houston, Texas +c +c\Revision history: +c xx/xx/92: Version ' 2.1' +c +c\SCCS Information: @(#) +c FILE: napps.F SID: 2.3 DATE OF SID: 4/20/96 RELEASE: 2 +c +c\Remarks +c 1. In this version, each shift is applied to all the sublocks of +c the Hessenberg matrix H and not just to the submatrix that it +c comes from. Deflation as in LAPACK routine dlahqr (QR algorithm +c for upper Hessenberg matrices ) is used. +c The subdiagonals of H are enforced to be non-negative. +c +c\EndLib +c +c----------------------------------------------------------------------- +c + subroutine igraphdnapps + & ( n, kev, np, shiftr, shifti, v, ldv, h, ldh, resid, q, ldq, + & workl, workd ) +c +c %----------------------------------------------------% +c | Include files for debugging and timing information | +c %----------------------------------------------------% +c + include 'debug.h' + include 'stat.h' +c +c %------------------% +c | Scalar Arguments | +c %------------------% +c + integer kev, ldh, ldq, ldv, n, np +c +c %-----------------% +c | Array Arguments | +c %-----------------% +c + Double precision + & h(ldh,kev+np), resid(n), shifti(np), shiftr(np), + & v(ldv,kev+np), q(ldq,kev+np), workd(2*n), workl(kev+np) +c +c %------------% +c | Parameters | +c %------------% +c + Double precision + & one, zero + parameter (one = 1.0D+0, zero = 0.0D+0) +c +c %------------------------% +c | Local Scalars & Arrays | +c %------------------------% +c + integer i, iend, ir, istart, j, jj, kplusp, msglvl, nr + logical cconj, first + Double precision + & c, f, g, h11, h12, h21, h22, h32, ovfl, r, s, sigmai, + & sigmar, smlnum, ulp, unfl, u(3), t, tau, tst1 + save first, ovfl, smlnum, ulp, unfl +c +c %----------------------% +c | External Subroutines | +c %----------------------% +c + external daxpy, dcopy, dscal, dlacpy, dlarfg, dlarf, + & dlaset, dlabad, igraphsecond, dlartg +c +c %--------------------% +c | External Functions | +c %--------------------% +c + Double precision + & dlamch, dlanhs, dlapy2 + external dlamch, dlanhs, dlapy2 +c +c %----------------------% +c | Intrinsics Functions | +c %----------------------% +c + intrinsic abs, max, min +c +c %----------------% +c | Data statments | +c %----------------% +c + data first / .true. / +c +c %-----------------------% +c | Executable Statements | +c %-----------------------% +c + if (first) then +c +c %-----------------------------------------------% +c | Set machine-dependent constants for the | +c | stopping criterion. If norm(H) <= sqrt(OVFL), | +c | overflow should not occur. | +c | REFERENCE: LAPACK subroutine dlahqr | +c %-----------------------------------------------% +c + unfl = dlamch( 'safe minimum' ) + ovfl = one / unfl + call dlabad( unfl, ovfl ) + ulp = dlamch( 'precision' ) + smlnum = unfl*( n / ulp ) + first = .false. + end if +c +c %-------------------------------% +c | Initialize timing statistics | +c | & message level for debugging | +c %-------------------------------% +c + call igraphsecond (t0) + msglvl = mnapps + kplusp = kev + np +c +c %--------------------------------------------% +c | Initialize Q to the identity to accumulate | +c | the rotations and reflections | +c %--------------------------------------------% +c + call dlaset ('All', kplusp, kplusp, zero, one, q, ldq) +c +c %----------------------------------------------% +c | Quick return if there are no shifts to apply | +c %----------------------------------------------% +c + if (np .eq. 0) go to 9000 +c +c %----------------------------------------------% +c | Chase the bulge with the application of each | +c | implicit shift. Each shift is applied to the | +c | whole matrix including each block. | +c %----------------------------------------------% +c + cconj = .false. + do 110 jj = 1, np + sigmar = shiftr(jj) + sigmai = shifti(jj) +c + if (msglvl .gt. 2 ) then + call igraphivout (logfil, 1, [jj], ndigit, + & '_napps: shift number.') + call igraphdvout (logfil, 1, [sigmar], ndigit, + & '_napps: The real part of the shift ') + call igraphdvout (logfil, 1, [sigmai], ndigit, + & '_napps: The imaginary part of the shift ') + end if +c +c %-------------------------------------------------% +c | The following set of conditionals is necessary | +c | in order that complex conjugate pairs of shifts | +c | are applied together or not at all. | +c %-------------------------------------------------% +c + if ( cconj ) then +c +c %-----------------------------------------% +c | cconj = .true. means the previous shift | +c | had non-zero imaginary part. | +c %-----------------------------------------% +c + cconj = .false. + go to 110 + else if ( jj .lt. np .and. abs( sigmai ) .gt. zero ) then +c +c %------------------------------------% +c | Start of a complex conjugate pair. | +c %------------------------------------% +c + cconj = .true. + else if ( jj .eq. np .and. abs( sigmai ) .gt. zero ) then +c +c %----------------------------------------------% +c | The last shift has a nonzero imaginary part. | +c | Don't apply it; thus the order of the | +c | compressed H is order KEV+1 since only np-1 | +c | were applied. | +c %----------------------------------------------% +c + kev = kev + 1 + go to 110 + end if + istart = 1 + 20 continue +c +c %--------------------------------------------------% +c | if sigmai = 0 then | +c | Apply the jj-th shift ... | +c | else | +c | Apply the jj-th and (jj+1)-th together ... | +c | (Note that jj < np at this point in the code) | +c | end | +c | to the current block of H. The next do loop | +c | determines the current block ; | +c %--------------------------------------------------% +c + do 30 i = istart, kplusp-1 +c +c %----------------------------------------% +c | Check for splitting and deflation. Use | +c | a standard test as in the QR algorithm | +c | REFERENCE: LAPACK subroutine dlahqr | +c %----------------------------------------% +c + tst1 = abs( h( i, i ) ) + abs( h( i+1, i+1 ) ) + if( tst1.eq.zero ) + & tst1 = dlanhs( '1', kplusp-jj+1, h, ldh, workl ) + if( abs( h( i+1,i ) ).le.max( ulp*tst1, smlnum ) ) then + if (msglvl .gt. 0) then + call igraphivout (logfil, 1, [i], ndigit, + & '_napps: matrix splitting at row/column no.') + call igraphivout (logfil, 1, [jj], ndigit, + & '_napps: matrix splitting with shift number.') + call igraphdvout (logfil, 1, h(i+1,i), ndigit, + & '_napps: off diagonal element.') + end if + iend = i + h(i+1,i) = zero + go to 40 + end if + 30 continue + iend = kplusp + 40 continue +c + if (msglvl .gt. 2) then + call igraphivout (logfil, 1, [istart], ndigit, + & '_napps: Start of current block ') + call igraphivout (logfil, 1, [iend], ndigit, + & '_napps: End of current block ') + end if +c +c %------------------------------------------------% +c | No reason to apply a shift to block of order 1 | +c %------------------------------------------------% +c + if ( istart .eq. iend ) go to 100 +c +c %------------------------------------------------------% +c | If istart + 1 = iend then no reason to apply a | +c | complex conjugate pair of shifts on a 2 by 2 matrix. | +c %------------------------------------------------------% +c + if ( istart + 1 .eq. iend .and. abs( sigmai ) .gt. zero ) + & go to 100 +c + h11 = h(istart,istart) + h21 = h(istart+1,istart) + if ( abs( sigmai ) .le. zero ) then +c +c %---------------------------------------------% +c | Real-valued shift ==> apply single shift QR | +c %---------------------------------------------% +c + f = h11 - sigmar + g = h21 +c + do 80 i = istart, iend-1 +c +c %-----------------------------------------------------% +c | Contruct the plane rotation G to zero out the bulge | +c %-----------------------------------------------------% +c + call dlartg (f, g, c, s, r) + if (i .gt. istart) then +c +c %-------------------------------------------% +c | The following ensures that h(1:iend-1,1), | +c | the first iend-2 off diagonal of elements | +c | H, remain non negative. | +c %-------------------------------------------% +c + if (r .lt. zero) then + r = -r + c = -c + s = -s + end if + h(i,i-1) = r + h(i+1,i-1) = zero + end if +c +c %---------------------------------------------% +c | Apply rotation to the left of H; H <- G'*H | +c %---------------------------------------------% +c + do 50 j = i, kplusp + t = c*h(i,j) + s*h(i+1,j) + h(i+1,j) = -s*h(i,j) + c*h(i+1,j) + h(i,j) = t + 50 continue +c +c %---------------------------------------------% +c | Apply rotation to the right of H; H <- H*G | +c %---------------------------------------------% +c + do 60 j = 1, min(i+2,iend) + t = c*h(j,i) + s*h(j,i+1) + h(j,i+1) = -s*h(j,i) + c*h(j,i+1) + h(j,i) = t + 60 continue +c +c %----------------------------------------------------% +c | Accumulate the rotation in the matrix Q; Q <- Q*G | +c %----------------------------------------------------% +c + do 70 j = 1, min( j+jj, kplusp ) + t = c*q(j,i) + s*q(j,i+1) + q(j,i+1) = - s*q(j,i) + c*q(j,i+1) + q(j,i) = t + 70 continue +c +c %---------------------------% +c | Prepare for next rotation | +c %---------------------------% +c + if (i .lt. iend-1) then + f = h(i+1,i) + g = h(i+2,i) + end if + 80 continue +c +c %-----------------------------------% +c | Finished applying the real shift. | +c %-----------------------------------% +c + else +c +c %----------------------------------------------------% +c | Complex conjugate shifts ==> apply double shift QR | +c %----------------------------------------------------% +c + h12 = h(istart,istart+1) + h22 = h(istart+1,istart+1) + h32 = h(istart+2,istart+1) +c +c %---------------------------------------------------------% +c | Compute 1st column of (H - shift*I)*(H - conj(shift)*I) | +c %---------------------------------------------------------% +c + s = 2.0*sigmar + t = dlapy2 ( sigmar, sigmai ) + u(1) = ( h11 * (h11 - s) + t * t ) / h21 + h12 + u(2) = h11 + h22 - s + u(3) = h32 +c + do 90 i = istart, iend-1 +c + nr = min ( 3, iend-i+1 ) +c +c %-----------------------------------------------------% +c | Construct Householder reflector G to zero out u(1). | +c | G is of the form I - tau*( 1 u )' * ( 1 u' ). | +c %-----------------------------------------------------% +c + call dlarfg ( nr, u(1), u(2), 1, tau ) +c + if (i .gt. istart) then + h(i,i-1) = u(1) + h(i+1,i-1) = zero + if (i .lt. iend-1) h(i+2,i-1) = zero + end if + u(1) = one +c +c %--------------------------------------% +c | Apply the reflector to the left of H | +c %--------------------------------------% +c + call dlarf ('Left', nr, kplusp-i+1, u, 1, tau, + & h(i,i), ldh, workl) +c +c %---------------------------------------% +c | Apply the reflector to the right of H | +c %---------------------------------------% +c + ir = min ( i+3, iend ) + call dlarf ('Right', ir, nr, u, 1, tau, + & h(1,i), ldh, workl) +c +c %-----------------------------------------------------% +c | Accumulate the reflector in the matrix Q; Q <- Q*G | +c %-----------------------------------------------------% +c + call dlarf ('Right', kplusp, nr, u, 1, tau, + & q(1,i), ldq, workl) +c +c %----------------------------% +c | Prepare for next reflector | +c %----------------------------% +c + if (i .lt. iend-1) then + u(1) = h(i+1,i) + u(2) = h(i+2,i) + if (i .lt. iend-2) u(3) = h(i+3,i) + end if +c + 90 continue +c +c %--------------------------------------------% +c | Finished applying a complex pair of shifts | +c | to the current block | +c %--------------------------------------------% +c + end if +c + 100 continue +c +c %---------------------------------------------------------% +c | Apply the same shift to the next block if there is any. | +c %---------------------------------------------------------% +c + istart = iend + 1 + if (iend .lt. kplusp) go to 20 +c +c %---------------------------------------------% +c | Loop back to the top to get the next shift. | +c %---------------------------------------------% +c + 110 continue +c +c %--------------------------------------------------% +c | Perform a similarity transformation that makes | +c | sure that H will have non negative sub diagonals | +c %--------------------------------------------------% +c + do 120 j=1,kev + if ( h(j+1,j) .lt. zero ) then + call dscal( kplusp-j+1, -one, h(j+1,j), ldh ) + call dscal( min(j+2, kplusp), -one, h(1,j+1), 1 ) + call dscal( min(j+np+1,kplusp), -one, q(1,j+1), 1 ) + end if + 120 continue +c + do 130 i = 1, kev +c +c %--------------------------------------------% +c | Final check for splitting and deflation. | +c | Use a standard test as in the QR algorithm | +c | REFERENCE: LAPACK subroutine dlahqr | +c %--------------------------------------------% +c + tst1 = abs( h( i, i ) ) + abs( h( i+1, i+1 ) ) + if( tst1.eq.zero ) + & tst1 = dlanhs( '1', kev, h, ldh, workl ) + if( h( i+1,i ) .le. max( ulp*tst1, smlnum ) ) + & h(i+1,i) = zero + 130 continue +c +c %-------------------------------------------------% +c | Compute the (kev+1)-st column of (V*Q) and | +c | temporarily store the result in WORKD(N+1:2*N). | +c | This is needed in the residual update since we | +c | cannot GUARANTEE that the corresponding entry | +c | of H would be zero as in exact arithmetic. | +c %-------------------------------------------------% +c + if (h(kev+1,kev) .gt. zero) + & call dgemv ('N', n, kplusp, one, v, ldv, q(1,kev+1), 1, zero, + & workd(n+1), 1) +c +c %----------------------------------------------------------% +c | Compute column 1 to kev of (V*Q) in backward order | +c | taking advantage of the upper Hessenberg structure of Q. | +c %----------------------------------------------------------% +c + do 140 i = 1, kev + call dgemv ('N', n, kplusp-i+1, one, v, ldv, + & q(1,kev-i+1), 1, zero, workd, 1) + call dcopy (n, workd, 1, v(1,kplusp-i+1), 1) + 140 continue +c +c %-------------------------------------------------% +c | Move v(:,kplusp-kev+1:kplusp) into v(:,1:kev). | +c %-------------------------------------------------% +c + call dlacpy ('A', n, kev, v(1,kplusp-kev+1), ldv, v, ldv) +c +c %--------------------------------------------------------------% +c | Copy the (kev+1)-st column of (V*Q) in the appropriate place | +c %--------------------------------------------------------------% +c + if (h(kev+1,kev) .gt. zero) + & call dcopy (n, workd(n+1), 1, v(1,kev+1), 1) +c +c %-------------------------------------% +c | Update the residual vector: | +c | r <- sigmak*r + betak*v(:,kev+1) | +c | where | +c | sigmak = (e_{kplusp}'*Q)*e_{kev} | +c | betak = e_{kev+1}'*H*e_{kev} | +c %-------------------------------------% +c + call dscal (n, q(kplusp,kev), resid, 1) + if (h(kev+1,kev) .gt. zero) + & call daxpy (n, h(kev+1,kev), v(1,kev+1), 1, resid, 1) +c + if (msglvl .gt. 1) then + call igraphdvout (logfil, 1, q(kplusp,kev), ndigit, + & '_napps: sigmak = (e_{kev+p}^T*Q)*e_{kev}') + call igraphdvout (logfil, 1, h(kev+1,kev), ndigit, + & '_napps: betak = e_{kev+1}^T*H*e_{kev}') + call igraphivout (logfil, 1, [kev], ndigit, + & '_napps: Order of the final Hessenberg matrix ') + if (msglvl .gt. 2) then + call igraphdmout (logfil, kev, kev, h, ldh, ndigit, + & '_napps: updated Hessenberg matrix H for next iteration') + end if +c + end if +c + 9000 continue + call igraphsecond (t1) + tnapps = tnapps + (t1 - t0) +c + return +c +c %---------------% +c | End of igraphdnapps | +c %---------------% +c + end diff --git a/src/rigraph/vendor/arpack/dnaup2.f b/src/rigraph/vendor/arpack/dnaup2.f new file mode 100644 index 0000000..e060689 --- /dev/null +++ b/src/rigraph/vendor/arpack/dnaup2.f @@ -0,0 +1,838 @@ +c\BeginDoc +c +c\Name: igraphdnaup2 +c +c\Description: +c Intermediate level interface called by igraphdnaupd. +c +c\Usage: +c call igraphdnaup2 +c ( IDO, BMAT, N, WHICH, NEV, NP, TOL, RESID, MODE, IUPD, +c ISHIFT, MXITER, V, LDV, H, LDH, RITZR, RITZI, BOUNDS, +c Q, LDQ, WORKL, IPNTR, WORKD, INFO ) +c +c\Arguments +c +c IDO, BMAT, N, WHICH, NEV, TOL, RESID: same as defined in igraphdnaupd. +c MODE, ISHIFT, MXITER: see the definition of IPARAM in igraphdnaupd. +c +c NP Integer. (INPUT/OUTPUT) +c Contains the number of implicit shifts to apply during +c each Arnoldi iteration. +c If ISHIFT=1, NP is adjusted dynamically at each iteration +c to accelerate convergence and prevent stagnation. +c This is also roughly equal to the number of matrix-vector +c products (involving the operator OP) per Arnoldi iteration. +c The logic for adjusting is contained within the current +c subroutine. +c If ISHIFT=0, NP is the number of shifts the user needs +c to provide via reverse comunication. 0 < NP < NCV-NEV. +c NP may be less than NCV-NEV for two reasons. The first, is +c to keep complex conjugate pairs of "wanted" Ritz values +c together. The igraphsecond, is that a leading block of the current +c upper Hessenberg matrix has split off and contains "unwanted" +c Ritz values. +c Upon termination of the IRA iteration, NP contains the number +c of "converged" wanted Ritz values. +c +c IUPD Integer. (INPUT) +c IUPD .EQ. 0: use explicit restart instead implicit update. +c IUPD .NE. 0: use implicit update. +c +c V Double precision N by (NEV+NP) array. (INPUT/OUTPUT) +c The Arnoldi basis vectors are returned in the first NEV +c columns of V. +c +c LDV Integer. (INPUT) +c Leading dimension of V exactly as declared in the calling +c program. +c +c H Double precision (NEV+NP) by (NEV+NP) array. (OUTPUT) +c H is used to store the generated upper Hessenberg matrix +c +c LDH Integer. (INPUT) +c Leading dimension of H exactly as declared in the calling +c program. +c +c RITZR, Double precision arrays of length NEV+NP. (OUTPUT) +c RITZI RITZR(1:NEV) (resp. RITZI(1:NEV)) contains the real (resp. +c imaginary) part of the computed Ritz values of OP. +c +c BOUNDS Double precision array of length NEV+NP. (OUTPUT) +c BOUNDS(1:NEV) contain the error bounds corresponding to +c the computed Ritz values. +c +c Q Double precision (NEV+NP) by (NEV+NP) array. (WORKSPACE) +c Private (replicated) work array used to accumulate the +c rotation in the shift application step. +c +c LDQ Integer. (INPUT) +c Leading dimension of Q exactly as declared in the calling +c program. +c +c WORKL Double precision work array of length at least +c (NEV+NP)**2 + 3*(NEV+NP). (INPUT/WORKSPACE) +c Private (replicated) array on each PE or array allocated on +c the front end. It is used in shifts calculation, shifts +c application and convergence checking. +c +c On exit, the last 3*(NEV+NP) locations of WORKL contain +c the Ritz values (real,imaginary) and associated Ritz +c estimates of the current Hessenberg matrix. They are +c listed in the same order as returned from igraphdneigh. +c +c If ISHIFT .EQ. O and IDO .EQ. 3, the first 2*NP locations +c of WORKL are used in reverse communication to hold the user +c supplied shifts. +c +c IPNTR Integer array of length 3. (OUTPUT) +c Pointer to mark the starting locations in the WORKD for +c vectors used by the Arnoldi iteration. +c ------------------------------------------------------------- +c IPNTR(1): pointer to the current operand vector X. +c IPNTR(2): pointer to the current result vector Y. +c IPNTR(3): pointer to the vector B * X when used in the +c shift-and-invert mode. X is the current operand. +c ------------------------------------------------------------- +c +c WORKD Double precision work array of length 3*N. (WORKSPACE) +c Distributed array to be used in the basic Arnoldi iteration +c for reverse communication. The user should not use WORKD +c as temporary workspace during the iteration !!!!!!!!!! +c See Data Distribution Note in DNAUPD. +c +c INFO Integer. (INPUT/OUTPUT) +c If INFO .EQ. 0, a randomly initial residual vector is used. +c If INFO .NE. 0, RESID contains the initial residual vector, +c possibly from a previous run. +c Error flag on output. +c = 0: Normal return. +c = 1: Maximum number of iterations taken. +c All possible eigenvalues of OP has been found. +c NP returns the number of converged Ritz values. +c = 2: No shifts could be applied. +c = -8: Error return from LAPACK eigenvalue calculation; +c This should never happen. +c = -9: Starting vector is zero. +c = -9999: Could not build an Arnoldi factorization. +c Size that was built in returned in NP. +c +c\EndDoc +c +c----------------------------------------------------------------------- +c +c\BeginLib +c +c\Local variables: +c xxxxxx real +c +c\References: +c 1. D.C. Sorensen, "Implicit Application of Polynomial Filters in +c a k-Step Arnoldi Method", SIAM J. Matr. Anal. Apps., 13 (1992), +c pp 357-385. +c 2. R.B. Lehoucq, "Analysis and Implementation of an Implicitly +c Restarted Arnoldi Iteration", Rice University Technical Report +c TR95-13, Department of Computational and Applied Mathematics. +c +c\Routines called: +c igraphdgetv0 ARPACK initial vector generation routine. +c igraphdnaitr ARPACK Arnoldi factorization routine. +c igraphdnapps ARPACK application of implicit shifts routine. +c igraphdnconv ARPACK convergence of Ritz values routine. +c igraphdneigh ARPACK compute Ritz values and error bounds routine. +c igraphdngets ARPACK reorder Ritz values and error bounds routine. +c igraphdsortc ARPACK sorting routine. +c igraphivout ARPACK utility routine that prints integers. +c igraphsecond ARPACK utility routine for timing. +c igraphdmout ARPACK utility routine that prints matrices +c igraphdvout ARPACK utility routine that prints vectors. +c dlamch LAPACK routine that determines machine constants. +c dlapy2 LAPACK routine to compute sqrt(x**2+y**2) carefully. +c dcopy Level 1 BLAS that copies one vector to another . +c ddot Level 1 BLAS that computes the scalar product of two vectors. +c dnrm2 Level 1 BLAS that computes the norm of a vector. +c dswap Level 1 BLAS that swaps two vectors. +c +c\Author +c Danny Sorensen Phuong Vu +c Richard Lehoucq CRPC / Rice University +c Dept. of Computational & Houston, Texas +c Applied Mathematics +c Rice University +c Houston, Texas +c +c\SCCS Information: @(#) +c FILE: naup2.F SID: 2.4 DATE OF SID: 7/30/96 RELEASE: 2 +c +c\Remarks +c 1. None +c +c\EndLib +c +c----------------------------------------------------------------------- +c + subroutine igraphdnaup2 + & ( 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 ) +c +c %----------------------------------------------------% +c | Include files for debugging and timing information | +c %----------------------------------------------------% +c + include 'debug.h' + include 'stat.h' +c +c %------------------% +c | Scalar Arguments | +c %------------------% +c + character bmat*1, which*2 + integer ido, info, ishift, iupd, mode, ldh, ldq, ldv, mxiter, + & n, nev, np + Double precision + & tol +c +c %-----------------% +c | Array Arguments | +c %-----------------% +c + integer ipntr(13) + Double precision + & bounds(nev+np), h(ldh,nev+np), q(ldq,nev+np), resid(n), + & ritzi(nev+np), ritzr(nev+np), v(ldv,nev+np), + & workd(3*n), workl( (nev+np)*(nev+np+3) ) +c +c %------------% +c | Parameters | +c %------------% +c + Double precision + & one, zero + parameter (one = 1.0D+0, zero = 0.0D+0) +c +c %---------------% +c | Local Scalars | +c %---------------% +c + character wprime*2 + logical cnorm, getv0, initv, update, ushift + integer ierr, iter, j, kplusp, msglvl, nconv, nevbef, nev0, + & np0, nptemp, numcnv + Double precision + & rnorm, temp, eps23 +c +c %-----------------------% +c | Local array arguments | +c %-----------------------% +c + integer kp(4) + save +c +c %----------------------% +c | External Subroutines | +c %----------------------% +c + external dcopy, igraphdgetv0, igraphdnaitr, igraphdnconv, + & igraphdneigh, igraphdngets, igraphdnapps, + & igraphdvout, igraphivout, igraphsecond +c +c %--------------------% +c | External Functions | +c %--------------------% +c + Double precision + & ddot, dnrm2, dlapy2, dlamch + external ddot, dnrm2, dlapy2, dlamch +c +c %---------------------% +c | Intrinsic Functions | +c %---------------------% +c + intrinsic min, max, abs, sqrt +c +c %-----------------------% +c | Executable Statements | +c %-----------------------% +c + if (ido .eq. 0) then +c + call igraphsecond (t0) +c + msglvl = mnaup2 +c +c %-------------------------------------% +c | Get the machine dependent constant. | +c %-------------------------------------% +c + eps23 = dlamch('Epsilon-Machine') + eps23 = eps23**(2.0D+0 / 3.0D+0) +c + nev0 = nev + np0 = np +c +c %-------------------------------------% +c | kplusp is the bound on the largest | +c | Lanczos factorization built. | +c | nconv is the current number of | +c | "converged" eigenvlues. | +c | iter is the counter on the current | +c | iteration step. | +c %-------------------------------------% +c + kplusp = nev + np + nconv = 0 + iter = 0 +c +c %---------------------------------------% +c | Set flags for computing the first NEV | +c | steps of the Arnoldi factorization. | +c %---------------------------------------% +c + getv0 = .true. + update = .false. + ushift = .false. + cnorm = .false. +c + if (info .ne. 0) then +c +c %--------------------------------------------% +c | User provides the initial residual vector. | +c %--------------------------------------------% +c + initv = .true. + info = 0 + else + initv = .false. + end if + end if +c +c %---------------------------------------------% +c | Get a possibly random starting vector and | +c | force it into the range of the operator OP. | +c %---------------------------------------------% +c + 10 continue +c + if (getv0) then + call igraphdgetv0 (ido, bmat, 1, initv, n, 1, v, ldv, resid, + & rnorm, ipntr, workd, info) +c + if (ido .ne. 99) go to 9000 +c + if (rnorm .eq. zero) then +c +c %-----------------------------------------% +c | The initial vector is zero. Error exit. | +c %-----------------------------------------% +c + info = -9 + go to 1100 + end if + getv0 = .false. + ido = 0 + end if +c +c %-----------------------------------% +c | Back from reverse communication : | +c | continue with update step | +c %-----------------------------------% +c + if (update) go to 20 +c +c %-------------------------------------------% +c | Back from computing user specified shifts | +c %-------------------------------------------% +c + if (ushift) go to 50 +c +c %-------------------------------------% +c | Back from computing residual norm | +c | at the end of the current iteration | +c %-------------------------------------% +c + if (cnorm) go to 100 +c +c %----------------------------------------------------------% +c | Compute the first NEV steps of the Arnoldi factorization | +c %----------------------------------------------------------% +c + call igraphdnaitr (ido, bmat, n, 0, nev, mode, resid, rnorm, v, + & ldv, h, ldh, ipntr, workd, info) +c +c %---------------------------------------------------% +c | ido .ne. 99 implies use of reverse communication | +c | to compute operations involving OP and possibly B | +c %---------------------------------------------------% +c + if (ido .ne. 99) go to 9000 +c + if (info .gt. 0) then + np = info + mxiter = iter + info = -9999 + go to 1200 + end if +c +c %--------------------------------------------------------------% +c | | +c | M A I N ARNOLDI I T E R A T I O N L O O P | +c | Each iteration implicitly restarts the Arnoldi | +c | factorization in place. | +c | | +c %--------------------------------------------------------------% +c + 1000 continue +c + iter = iter + 1 +c + if (msglvl .gt. 0) then + call igraphivout (logfil, 1, [iter], ndigit, + & '_naup2: **** Start of major iteration number ****') + end if +c +c %-----------------------------------------------------------% +c | Compute NP additional steps of the Arnoldi factorization. | +c | Adjust NP since NEV might have been updated by last call | +c | to the shift application routine igraphdnapps. | +c %-----------------------------------------------------------% +c + np = kplusp - nev +c + if (msglvl .gt. 1) then + call igraphivout (logfil, 1, [nev], ndigit, + & '_naup2: The length of the current Arnoldi factorization') + call igraphivout (logfil, 1, [np], ndigit, + & '_naup2: Extend the Arnoldi factorization by') + end if +c +c %-----------------------------------------------------------% +c | Compute NP additional steps of the Arnoldi factorization. | +c %-----------------------------------------------------------% +c + ido = 0 + 20 continue + update = .true. +c + call igraphdnaitr (ido, bmat, n, nev, np, mode, resid, rnorm, + & v, ldv, h, ldh, ipntr, workd, info) +c +c %---------------------------------------------------% +c | ido .ne. 99 implies use of reverse communication | +c | to compute operations involving OP and possibly B | +c %---------------------------------------------------% +c + if (ido .ne. 99) go to 9000 +c + if (info .gt. 0) then + np = info + mxiter = iter + info = -9999 + go to 1200 + end if + update = .false. +c + if (msglvl .gt. 1) then + call igraphdvout (logfil, 1, [rnorm], ndigit, + & '_naup2: Corresponding B-norm of the residual') + end if +c +c %--------------------------------------------------------% +c | Compute the eigenvalues and corresponding error bounds | +c | of the current upper Hessenberg matrix. | +c %--------------------------------------------------------% +c + call igraphdneigh (rnorm, kplusp, h, ldh, ritzr, ritzi, bounds, + & q, ldq, workl, ierr) +c + if (ierr .ne. 0) then + info = -8 + go to 1200 + end if +c +c %----------------------------------------------------% +c | Make a copy of eigenvalues and corresponding error | +c | bounds obtained from igraphdneigh. | +c %----------------------------------------------------% +c + call dcopy(kplusp, ritzr, 1, workl(kplusp**2+1), 1) + call dcopy(kplusp, ritzi, 1, workl(kplusp**2+kplusp+1), 1) + call dcopy(kplusp, bounds, 1, workl(kplusp**2+2*kplusp+1), 1) +c +c %---------------------------------------------------% +c | Select the wanted Ritz values and their bounds | +c | to be used in the convergence test. | +c | The wanted part of the spectrum and corresponding | +c | error bounds are in the last NEV loc. of RITZR, | +c | RITZI and BOUNDS respectively. The variables NEV | +c | and NP may be updated if the NEV-th wanted Ritz | +c | value has a non zero imaginary part. In this case | +c | NEV is increased by one and NP decreased by one. | +c | NOTE: The last two arguments of igraphdngets are no | +c | longer used as of version 2.1. | +c %---------------------------------------------------% +c + nev = nev0 + np = np0 + numcnv = nev + call igraphdngets (ishift, which, nev, np, ritzr, ritzi, + & bounds, workl, workl(np+1)) + if (nev .eq. nev0+1) numcnv = nev0+1 +c +c %-------------------% +c | Convergence test. | +c %-------------------% +c + call dcopy (nev, bounds(np+1), 1, workl(2*np+1), 1) + call igraphdnconv (nev, ritzr(np+1), ritzi(np+1), + & workl(2*np+1), tol, nconv) +c + if (msglvl .gt. 2) then + kp(1) = nev + kp(2) = np + kp(3) = numcnv + kp(4) = nconv + call igraphivout (logfil, 4, kp, ndigit, + & '_naup2: NEV, NP, NUMCNV, NCONV are') + call igraphdvout (logfil, kplusp, ritzr, ndigit, + & '_naup2: Real part of the eigenvalues of H') + call igraphdvout (logfil, kplusp, ritzi, ndigit, + & '_naup2: Imaginary part of the eigenvalues of H') + call igraphdvout (logfil, kplusp, bounds, ndigit, + & '_naup2: Ritz estimates of the current NCV Ritz values') + end if +c +c %---------------------------------------------------------% +c | Count the number of unwanted Ritz values that have zero | +c | Ritz estimates. If any Ritz estimates are equal to zero | +c | then a leading block of H of order equal to at least | +c | the number of Ritz values with zero Ritz estimates has | +c | split off. None of these Ritz values may be removed by | +c | shifting. Decrease NP the number of shifts to apply. If | +c | no shifts may be applied, then prepare to exit | +c %---------------------------------------------------------% +c + nptemp = np + do 30 j=1, nptemp + if (bounds(j) .eq. zero) then + np = np - 1 + nev = nev + 1 + end if + 30 continue +c + if ( (nconv .ge. numcnv) .or. + & (iter .gt. mxiter) .or. + & (np .eq. 0) ) then +c + if (msglvl .gt. 4) then + call igraphdvout(logfil, kplusp, workl(kplusp**2+1), + & ndigit, + & '_naup2: Real part of the eig computed by _neigh:') + call igraphdvout(logfil, kplusp, + & workl(kplusp**2+kplusp+1), ndigit, + & '_naup2: Imag part of the eig computed by _neigh:') + call igraphdvout(logfil, kplusp, + & workl(kplusp**2+kplusp*2+1), ndigit, + & '_naup2: Ritz eistmates computed by _neigh:') + end if +c +c %------------------------------------------------% +c | Prepare to exit. Put the converged Ritz values | +c | and corresponding bounds in RITZ(1:NCONV) and | +c | BOUNDS(1:NCONV) respectively. Then sort. Be | +c | careful when NCONV > NP | +c %------------------------------------------------% +c +c %------------------------------------------% +c | Use h( 3,1 ) as storage to communicate | +c | rnorm to _neupd if needed | +c %------------------------------------------% + + h(3,1) = rnorm +c +c %----------------------------------------------% +c | To be consistent with igraphdngets, we first do a | +c | pre-processing sort in order to keep complex | +c | conjugate pairs together. This is similar | +c | to the pre-processing sort used in igraphdngets | +c | except that the sort is done in the opposite | +c | order. | +c %----------------------------------------------% +c + if (which .eq. 'LM') wprime = 'SR' + if (which .eq. 'SM') wprime = 'LR' + if (which .eq. 'LR') wprime = 'SM' + if (which .eq. 'SR') wprime = 'LM' + if (which .eq. 'LI') wprime = 'SM' + if (which .eq. 'SI') wprime = 'LM' +c + call igraphdsortc (wprime, .true., kplusp, ritzr, ritzi, + & bounds) +c +c %----------------------------------------------% +c | Now sort Ritz values so that converged Ritz | +c | values appear within the first NEV locations | +c | of ritzr, ritzi and bounds, and the most | +c | desired one appears at the front. | +c %----------------------------------------------% +c + if (which .eq. 'LM') wprime = 'SM' + if (which .eq. 'SM') wprime = 'LM' + if (which .eq. 'LR') wprime = 'SR' + if (which .eq. 'SR') wprime = 'LR' + if (which .eq. 'LI') wprime = 'SI' + if (which .eq. 'SI') wprime = 'LI' +c + call igraphdsortc(wprime, .true., kplusp, ritzr, ritzi, + & bounds) +c +c %--------------------------------------------------% +c | Scale the Ritz estimate of each Ritz value | +c | by 1 / max(eps23,magnitude of the Ritz value). | +c %--------------------------------------------------% +c + do 35 j = 1, nev0 + temp = max(eps23,dlapy2(ritzr(j), + & ritzi(j))) + bounds(j) = bounds(j)/temp + 35 continue +c +c %----------------------------------------------------% +c | Sort the Ritz values according to the scaled Ritz | +c | esitmates. This will push all the converged ones | +c | towards the front of ritzr, ritzi, bounds | +c | (in the case when NCONV < NEV.) | +c %----------------------------------------------------% +c + wprime = 'LR' + call igraphdsortc(wprime, .true., nev0, bounds, ritzr, + & ritzi) +c +c %----------------------------------------------% +c | Scale the Ritz estimate back to its original | +c | value. | +c %----------------------------------------------% +c + do 40 j = 1, nev0 + temp = max(eps23, dlapy2(ritzr(j), + & ritzi(j))) + bounds(j) = bounds(j)*temp + 40 continue +c +c %------------------------------------------------% +c | Sort the converged Ritz values again so that | +c | the "threshold" value appears at the front of | +c | ritzr, ritzi and bound. | +c %------------------------------------------------% +c + call igraphdsortc(which, .true., nconv, ritzr, ritzi, + & bounds) +c + if (msglvl .gt. 1) then + call igraphdvout (logfil, kplusp, ritzr, ndigit, + & '_naup2: Sorted real part of the eigenvalues') + call igraphdvout (logfil, kplusp, ritzi, ndigit, + & '_naup2: Sorted imaginary part of the eigenvalues') + call igraphdvout (logfil, kplusp, bounds, ndigit, + & '_naup2: Sorted ritz estimates.') + end if +c +c %------------------------------------% +c | Max iterations have been exceeded. | +c %------------------------------------% +c + if (iter .gt. mxiter .and. nconv .lt. numcnv) info = 1 +c +c %---------------------% +c | No shifts to apply. | +c %---------------------% +c + if (np .eq. 0 .and. nconv .lt. numcnv) info = 2 +c + np = nconv + go to 1100 +c + else if ( (nconv .lt. numcnv) .and. (ishift .eq. 1) ) then +c +c %-------------------------------------------------% +c | Do not have all the requested eigenvalues yet. | +c | To prevent possible stagnation, adjust the size | +c | of NEV. | +c %-------------------------------------------------% +c + nevbef = nev + nev = nev + min(nconv, np/2) + if (nev .eq. 1 .and. kplusp .ge. 6) then + nev = kplusp / 2 + else if (nev .eq. 1 .and. kplusp .gt. 3) then + nev = 2 + end if + np = kplusp - nev +c +c %---------------------------------------% +c | If the size of NEV was just increased | +c | resort the eigenvalues. | +c %---------------------------------------% +c + if (nevbef .lt. nev) + & call igraphdngets (ishift, which, nev, np, ritzr, ritzi, + & bounds, workl, workl(np+1)) +c + end if +c + if (msglvl .gt. 0) then + call igraphivout (logfil, 1, [nconv], ndigit, + & '_naup2: no. of "converged" Ritz values at this iter.') + if (msglvl .gt. 1) then + kp(1) = nev + kp(2) = np + call igraphivout (logfil, 2, kp, ndigit, + & '_naup2: NEV and NP are') + call igraphdvout (logfil, nev, ritzr(np+1), ndigit, + & '_naup2: "wanted" Ritz values -- real part') + call igraphdvout (logfil, nev, ritzi(np+1), ndigit, + & '_naup2: "wanted" Ritz values -- imag part') + call igraphdvout (logfil, nev, bounds(np+1), ndigit, + & '_naup2: Ritz estimates of the "wanted" values ') + end if + end if +c + if (ishift .eq. 0) then +c +c %-------------------------------------------------------% +c | User specified shifts: reverse comminucation to | +c | compute the shifts. They are returned in the first | +c | 2*NP locations of WORKL. | +c %-------------------------------------------------------% +c + ushift = .true. + ido = 3 + go to 9000 + end if +c + 50 continue +c +c %------------------------------------% +c | Back from reverse communication; | +c | User specified shifts are returned | +c | in WORKL(1:2*NP) | +c %------------------------------------% +c + ushift = .false. +c + if ( ishift .eq. 0 ) then +c +c %----------------------------------% +c | Move the NP shifts from WORKL to | +c | RITZR, RITZI to free up WORKL | +c | for non-exact shift case. | +c %----------------------------------% +c + call dcopy (np, workl, 1, ritzr, 1) + call dcopy (np, workl(np+1), 1, ritzi, 1) + end if +c + if (msglvl .gt. 2) then + call igraphivout (logfil, 1, [np], ndigit, + & '_naup2: The number of shifts to apply ') + call igraphdvout (logfil, np, ritzr, ndigit, + & '_naup2: Real part of the shifts') + call igraphdvout (logfil, np, ritzi, ndigit, + & '_naup2: Imaginary part of the shifts') + if ( ishift .eq. 1 ) + & call igraphdvout (logfil, np, bounds, ndigit, + & '_naup2: Ritz estimates of the shifts') + end if +c +c %---------------------------------------------------------% +c | Apply the NP implicit shifts by QR bulge chasing. | +c | Each shift is applied to the whole upper Hessenberg | +c | matrix H. | +c | The first 2*N locations of WORKD are used as workspace. | +c %---------------------------------------------------------% +c + call igraphdnapps (n, nev, np, ritzr, ritzi, v, ldv, + & h, ldh, resid, q, ldq, workl, workd) +c +c %---------------------------------------------% +c | Compute the B-norm of the updated residual. | +c | Keep B*RESID in WORKD(1:N) to be used in | +c | the first step of the next call to igraphdnaitr. | +c %---------------------------------------------% +c + cnorm = .true. + call igraphsecond (t2) + if (bmat .eq. 'G') then + nbx = nbx + 1 + call dcopy (n, resid, 1, workd(n+1), 1) + ipntr(1) = n + 1 + ipntr(2) = 1 + ido = 2 +c +c %----------------------------------% +c | Exit in order to compute B*RESID | +c %----------------------------------% +c + go to 9000 + else if (bmat .eq. 'I') then + call dcopy (n, resid, 1, workd, 1) + end if +c + 100 continue +c +c %----------------------------------% +c | Back from reverse communication; | +c | WORKD(1:N) := B*RESID | +c %----------------------------------% +c + if (bmat .eq. 'G') then + call igraphsecond (t3) + tmvbx = tmvbx + (t3 - t2) + end if +c + if (bmat .eq. 'G') then + rnorm = ddot (n, resid, 1, workd, 1) + rnorm = sqrt(abs(rnorm)) + else if (bmat .eq. 'I') then + rnorm = dnrm2(n, resid, 1) + end if + cnorm = .false. +c + if (msglvl .gt. 2) then + call igraphdvout (logfil, 1, [rnorm], ndigit, + & '_naup2: B-norm of residual for compressed factorization') + call igraphdmout (logfil, nev, nev, h, ldh, ndigit, + & '_naup2: Compressed upper Hessenberg matrix H') + end if +c + go to 1000 +c +c %---------------------------------------------------------------% +c | | +c | E N D O F M A I N I T E R A T I O N L O O P | +c | | +c %---------------------------------------------------------------% +c + 1100 continue +c + mxiter = iter + nev = numcnv +c + 1200 continue + ido = 99 +c +c %------------% +c | Error Exit | +c %------------% +c + call igraphsecond (t1) + tnaup2 = t1 - t0 +c + 9000 continue +c +c %---------------% +c | End of igraphdnaup2 | +c %---------------% +c + return + end diff --git a/src/rigraph/vendor/arpack/dnaupd.f b/src/rigraph/vendor/arpack/dnaupd.f new file mode 100644 index 0000000..4133022 --- /dev/null +++ b/src/rigraph/vendor/arpack/dnaupd.f @@ -0,0 +1,655 @@ +c\BeginDoc +c +c\Name: igraphdnaupd +c +c\Description: +c Reverse communication interface for the Implicitly Restarted Arnoldi +c iteration. This subroutine computes approximations to a few eigenpairs +c of a linear operator "OP" with respect to a semi-inner product defined by +c a symmetric positive semi-definite real matrix B. B may be the identity +c matrix. NOTE: If the linear operator "OP" is real and symmetric +c with respect to the real positive semi-definite symmetric matrix B, +c i.e. B*OP = (OP')*B, then subroutine ssaupd should be used instead. +c +c The computed approximate eigenvalues are called Ritz values and +c the corresponding approximate eigenvectors are called Ritz vectors. +c +c igraphdnaupd is usually called iteratively to solve one of the +c following problems: +c +c Mode 1: A*x = lambda*x. +c ===> OP = A and B = I. +c +c Mode 2: A*x = lambda*M*x, M symmetric positive definite +c ===> OP = inv[M]*A and B = M. +c ===> (If M can be factored see remark 3 below) +c +c Mode 3: A*x = lambda*M*x, M symmetric semi-definite +c ===> OP = Real_Part{ inv[A - sigma*M]*M } and B = M. +c ===> shift-and-invert mode (in real arithmetic) +c If OP*x = amu*x, then +c amu = 1/2 * [ 1/(lambda-sigma) + 1/(lambda-conjg(sigma)) ]. +c Note: If sigma is real, i.e. imaginary part of sigma is zero; +c Real_Part{ inv[A - sigma*M]*M } == inv[A - sigma*M]*M +c amu == 1/(lambda-sigma). +c +c Mode 4: A*x = lambda*M*x, M symmetric semi-definite +c ===> OP = Imaginary_Part{ inv[A - sigma*M]*M } and B = M. +c ===> shift-and-invert mode (in real arithmetic) +c If OP*x = amu*x, then +c amu = 1/2i * [ 1/(lambda-sigma) - 1/(lambda-conjg(sigma)) ]. +c +c Both mode 3 and 4 give the same enhancement to eigenvalues close to +c the (complex) shift sigma. However, as lambda goes to infinity, +c the operator OP in mode 4 dampens the eigenvalues more strongly than +c does OP defined in mode 3. +c +c NOTE: The action of w <- inv[A - sigma*M]*v or w <- inv[M]*v +c should be accomplished either by a direct method +c using a sparse matrix factorization and solving +c +c [A - sigma*M]*w = v or M*w = v, +c +c or through an iterative method for solving these +c systems. If an iterative method is used, the +c convergence test must be more stringent than +c the accuracy requirements for the eigenvalue +c approximations. +c +c\Usage: +c call igraphdnaupd +c ( IDO, BMAT, N, WHICH, NEV, TOL, RESID, NCV, V, LDV, IPARAM, +c IPNTR, WORKD, WORKL, LWORKL, INFO ) +c +c\Arguments +c IDO Integer. (INPUT/OUTPUT) +c Reverse communication flag. IDO must be zero on the first +c call to igraphdnaupd. IDO will be set internally to +c indicate the type of operation to be performed. Control is +c then given back to the calling routine which has the +c responsibility to carry out the requested operation and call +c igraphdnaupd with the result. The operand is given in +c WORKD(IPNTR(1)), the result must be put in WORKD(IPNTR(2)). +c ------------------------------------------------------------- +c IDO = 0: first call to the reverse communication interface +c IDO = -1: compute Y = OP * X where +c IPNTR(1) is the pointer into WORKD for X, +c IPNTR(2) is the pointer into WORKD for Y. +c This is for the initialization phase to force the +c starting vector into the range of OP. +c IDO = 1: compute Y = OP * X where +c IPNTR(1) is the pointer into WORKD for X, +c IPNTR(2) is the pointer into WORKD for Y. +c In mode 3 and 4, the vector B * X is already +c available in WORKD(ipntr(3)). It does not +c need to be recomputed in forming OP * X. +c IDO = 2: compute Y = B * X where +c IPNTR(1) is the pointer into WORKD for X, +c IPNTR(2) is the pointer into WORKD for Y. +c IDO = 3: compute the IPARAM(8) real and imaginary parts +c of the shifts where INPTR(14) is the pointer +c into WORKL for placing the shifts. See Remark +c 5 below. +c IDO = 99: done +c ------------------------------------------------------------- +c +c BMAT Character*1. (INPUT) +c BMAT specifies the type of the matrix B that defines the +c semi-inner product for the operator OP. +c BMAT = 'I' -> standard eigenvalue problem A*x = lambda*x +c BMAT = 'G' -> generalized eigenvalue problem A*x = lambda*B*x +c +c N Integer. (INPUT) +c Dimension of the eigenproblem. +c +c WHICH Character*2. (INPUT) +c 'LM' -> want the NEV eigenvalues of largest magnitude. +c 'SM' -> want the NEV eigenvalues of smallest magnitude. +c 'LR' -> want the NEV eigenvalues of largest real part. +c 'SR' -> want the NEV eigenvalues of smallest real part. +c 'LI' -> want the NEV eigenvalues of largest imaginary part. +c 'SI' -> want the NEV eigenvalues of smallest imaginary part. +c +c NEV Integer. (INPUT) +c Number of eigenvalues of OP to be computed. 0 < NEV < N-1. +c +c TOL Double precision scalar. (INPUT) +c Stopping criterion: the relative accuracy of the Ritz value +c is considered acceptable if BOUNDS(I) .LE. TOL*ABS(RITZ(I)) +c where ABS(RITZ(I)) is the magnitude when RITZ(I) is complex. +c DEFAULT = DLAMCH('EPS') (machine precision as computed +c by the LAPACK auxiliary subroutine DLAMCH). +c +c RESID Double precision array of length N. (INPUT/OUTPUT) +c On INPUT: +c If INFO .EQ. 0, a random initial residual vector is used. +c If INFO .NE. 0, RESID contains the initial residual vector, +c possibly from a previous run. +c On OUTPUT: +c RESID contains the final residual vector. +c +c NCV Integer. (INPUT) +c Number of columns of the matrix V. NCV must satisfy the two +c inequalities 2 <= NCV-NEV and NCV <= N. +c This will indicate how many Arnoldi vectors are generated +c at each iteration. After the startup phase in which NEV +c Arnoldi vectors are generated, the algorithm generates +c approximately NCV-NEV Arnoldi vectors at each subsequent update +c iteration. Most of the cost in generating each Arnoldi vector is +c in the matrix-vector operation OP*x. +c NOTE: 2 <= NCV-NEV in order that complex conjugate pairs of Ritz +c values are kept together. (See remark 4 below) +c +c V Double precision array N by NCV. (OUTPUT) +c Contains the final set of Arnoldi basis vectors. +c +c LDV Integer. (INPUT) +c Leading dimension of V exactly as declared in the calling program. +c +c IPARAM Integer array of length 11. (INPUT/OUTPUT) +c IPARAM(1) = ISHIFT: method for selecting the implicit shifts. +c The shifts selected at each iteration are used to restart +c the Arnoldi iteration in an implicit fashion. +c ------------------------------------------------------------- +c ISHIFT = 0: the shifts are provided by the user via +c reverse communication. The real and imaginary +c parts of the NCV eigenvalues of the Hessenberg +c matrix H are returned in the part of the WORKL +c array corresponding to RITZR and RITZI. See remark +c 5 below. +c ISHIFT = 1: exact shifts with respect to the current +c Hessenberg matrix H. This is equivalent to +c restarting the iteration with a starting vector +c that is a linear combination of approximate Schur +c vectors associated with the "wanted" Ritz values. +c ------------------------------------------------------------- +c +c IPARAM(2) = No longer referenced. +c +c IPARAM(3) = MXITER +c On INPUT: maximum number of Arnoldi update iterations allowed. +c On OUTPUT: actual number of Arnoldi update iterations taken. +c +c IPARAM(4) = NB: blocksize to be used in the recurrence. +c The code currently works only for NB = 1. +c +c IPARAM(5) = NCONV: number of "converged" Ritz values. +c This represents the number of Ritz values that satisfy +c the convergence criterion. +c +c IPARAM(6) = IUPD +c No longer referenced. Implicit restarting is ALWAYS used. +c +c IPARAM(7) = MODE +c On INPUT determines what type of eigenproblem is being solved. +c Must be 1,2,3,4; See under \Description of igraphdnaupd for the +c four modes available. +c +c IPARAM(8) = NP +c When ido = 3 and the user provides shifts through reverse +c communication (IPARAM(1)=0), igraphdnaupd returns NP, the number +c of shifts the user is to provide. 0 < NP <=NCV-NEV. See Remark +c 5 below. +c +c IPARAM(9) = NUMOP, IPARAM(10) = NUMOPB, IPARAM(11) = NUMREO, +c OUTPUT: NUMOP = total number of OP*x operations, +c NUMOPB = total number of B*x operations if BMAT='G', +c NUMREO = total number of steps of re-orthogonalization. +c +c IPNTR Integer array of length 14. (OUTPUT) +c Pointer to mark the starting locations in the WORKD and WORKL +c arrays for matrices/vectors used by the Arnoldi iteration. +c ------------------------------------------------------------- +c IPNTR(1): pointer to the current operand vector X in WORKD. +c IPNTR(2): pointer to the current result vector Y in WORKD. +c IPNTR(3): pointer to the vector B * X in WORKD when used in +c the shift-and-invert mode. +c IPNTR(4): pointer to the next available location in WORKL +c that is untouched by the program. +c IPNTR(5): pointer to the NCV by NCV upper Hessenberg matrix +c H in WORKL. +c IPNTR(6): pointer to the real part of the ritz value array +c RITZR in WORKL. +c IPNTR(7): pointer to the imaginary part of the ritz value array +c RITZI in WORKL. +c IPNTR(8): pointer to the Ritz estimates in array WORKL associated +c with the Ritz values located in RITZR and RITZI in WORKL. +c +c IPNTR(14): pointer to the NP shifts in WORKL. See Remark 5 below. +c +c Note: IPNTR(9:13) is only referenced by igraphdneupd. See Remark 2 below. +c +c IPNTR(9): pointer to the real part of the NCV RITZ values of the +c original system. +c IPNTR(10): pointer to the imaginary part of the NCV RITZ values of +c the original system. +c IPNTR(11): pointer to the NCV corresponding error bounds. +c IPNTR(12): pointer to the NCV by NCV upper quasi-triangular +c Schur matrix for H. +c IPNTR(13): pointer to the NCV by NCV matrix of eigenvectors +c of the upper Hessenberg matrix H. Only referenced by +c igraphdneupd if RVEC = .TRUE. See Remark 2 below. +c ------------------------------------------------------------- +c +c WORKD Double precision work array of length 3*N. (REVERSE COMMUNICATION) +c Distributed array to be used in the basic Arnoldi iteration +c for reverse communication. The user should not use WORKD +c as temporary workspace during the iteration. Upon termination +c WORKD(1:N) contains B*RESID(1:N). If an invariant subspace +c associated with the converged Ritz values is desired, see remark +c 2 below, subroutine igraphdneupd uses this output. +c See Data Distribution Note below. +c +c WORKL Double precision work array of length LWORKL. (OUTPUT/WORKSPACE) +c Private (replicated) array on each PE or array allocated on +c the front end. See Data Distribution Note below. +c +c LWORKL Integer. (INPUT) +c LWORKL must be at least 3*NCV**2 + 6*NCV. +c +c INFO Integer. (INPUT/OUTPUT) +c If INFO .EQ. 0, a randomly initial residual vector is used. +c If INFO .NE. 0, RESID contains the initial residual vector, +c possibly from a previous run. +c Error flag on output. +c = 0: Normal exit. +c = 1: Maximum number of iterations taken. +c All possible eigenvalues of OP has been found. IPARAM(5) +c returns the number of wanted converged Ritz values. +c = 2: No longer an informational error. Deprecated starting +c with release 2 of ARPACK. +c = 3: No shifts could be applied during a cycle of the +c Implicitly restarted Arnoldi iteration. One possibility +c is to increase the size of NCV relative to NEV. +c See remark 4 below. +c = -1: N must be positive. +c = -2: NEV must be positive. +c = -3: NCV-NEV >= 2 and less than or equal to N. +c = -4: The maximum number of Arnoldi update iteration +c must be greater than zero. +c = -5: WHICH must be one of 'LM', 'SM', 'LR', 'SR', 'LI', 'SI' +c = -6: BMAT must be one of 'I' or 'G'. +c = -7: Length of private work array is not sufficient. +c = -8: Error return from LAPACK eigenvalue calculation; +c = -9: Starting vector is zero. +c = -10: IPARAM(7) must be 1,2,3,4. +c = -11: IPARAM(7) = 1 and BMAT = 'G' are incompatable. +c = -12: IPARAM(1) must be equal to 0 or 1. +c = -9999: Could not build an Arnoldi factorization. +c IPARAM(5) returns the size of the current Arnoldi +c factorization. +c +c\Remarks +c 1. The computed Ritz values are approximate eigenvalues of OP. The +c selection of WHICH should be made with this in mind when +c Mode = 3 and 4. After convergence, approximate eigenvalues of the +c original problem may be obtained with the ARPACK subroutine igraphdneupd. +c +c 2. If a basis for the invariant subspace corresponding to the converged Ritz +c values is needed, the user must call igraphdneupd immediately following +c completion of igraphdnaupd. This is new starting with release 2 of ARPACK. +c +c 3. If M can be factored into a Cholesky factorization M = LL' +c then Mode = 2 should not be selected. Instead one should use +c Mode = 1 with OP = inv(L)*A*inv(L'). Appropriate triangular +c linear systems should be solved with L and L' rather +c than computing inverses. After convergence, an approximate +c eigenvector z of the original problem is recovered by solving +c L'z = x where x is a Ritz vector of OP. +c +c 4. At present there is no a-priori analysis to guide the selection +c of NCV relative to NEV. The only formal requrement is that NCV > NEV + 2. +c However, it is recommended that NCV .ge. 2*NEV+1. If many problems of +c the same type are to be solved, one should experiment with increasing +c NCV while keeping NEV fixed for a given test problem. This will +c usually decrease the required number of OP*x operations but it +c also increases the work and storage required to maintain the orthogonal +c basis vectors. The optimal "cross-over" with respect to CPU time +c is problem dependent and must be determined empirically. +c See Chapter 8 of Reference 2 for further information. +c +c 5. When IPARAM(1) = 0, and IDO = 3, the user needs to provide the +c NP = IPARAM(8) real and imaginary parts of the shifts in locations +c real part imaginary part +c ----------------------- -------------- +c 1 WORKL(IPNTR(14)) WORKL(IPNTR(14)+NP) +c 2 WORKL(IPNTR(14)+1) WORKL(IPNTR(14)+NP+1) +c . . +c . . +c . . +c NP WORKL(IPNTR(14)+NP-1) WORKL(IPNTR(14)+2*NP-1). +c +c Only complex conjugate pairs of shifts may be applied and the pairs +c must be placed in consecutive locations. The real part of the +c eigenvalues of the current upper Hessenberg matrix are located in +c WORKL(IPNTR(6)) through WORKL(IPNTR(6)+NCV-1) and the imaginary part +c in WORKL(IPNTR(7)) through WORKL(IPNTR(7)+NCV-1). They are ordered +c according to the order defined by WHICH. The complex conjugate +c pairs are kept together and the associated Ritz estimates are located in +c WORKL(IPNTR(8)), WORKL(IPNTR(8)+1), ... , WORKL(IPNTR(8)+NCV-1). +c +c----------------------------------------------------------------------- +c +c\Data Distribution Note: +c +c Fortran-D syntax: +c ================ +c Double precision resid(n), v(ldv,ncv), workd(3*n), workl(lworkl) +c decompose d1(n), d2(n,ncv) +c align resid(i) with d1(i) +c align v(i,j) with d2(i,j) +c align workd(i) with d1(i) range (1:n) +c align workd(i) with d1(i-n) range (n+1:2*n) +c align workd(i) with d1(i-2*n) range (2*n+1:3*n) +c distribute d1(block), d2(block,:) +c replicated workl(lworkl) +c +c Cray MPP syntax: +c =============== +c Double precision resid(n), v(ldv,ncv), workd(n,3), workl(lworkl) +c shared resid(block), v(block,:), workd(block,:) +c replicated workl(lworkl) +c +c CM2/CM5 syntax: +c ============== +c +c----------------------------------------------------------------------- +c +c include 'ex-nonsym.doc' +c +c----------------------------------------------------------------------- +c +c\BeginLib +c +c\Local variables: +c xxxxxx real +c +c\References: +c 1. D.C. Sorensen, "Implicit Application of Polynomial Filters in +c a k-Step Arnoldi Method", SIAM J. Matr. Anal. Apps., 13 (1992), +c pp 357-385. +c 2. R.B. Lehoucq, "Analysis and Implementation of an Implicitly +c Restarted Arnoldi Iteration", Rice University Technical Report +c TR95-13, Department of Computational and Applied Mathematics. +c 3. B.N. Parlett & Y. Saad, "Complex Shift and Invert Strategies for +c Real Matrices", Linear Algebra and its Applications, vol 88/89, +c pp 575-595, (1987). +c +c\Routines called: +c igraphdnaup2 ARPACK routine that implements the Implicitly Restarted +c Arnoldi Iteration. +c igraphivout ARPACK utility routine that prints integers. +c igraphsecond ARPACK utility routine for timing. +c igraphdvout ARPACK utility routine that prints vectors. +c dlamch LAPACK routine that determines machine constants. +c +c\Author +c Danny Sorensen Phuong Vu +c Richard Lehoucq CRPC / Rice University +c Dept. of Computational & Houston, Texas +c Applied Mathematics +c Rice University +c Houston, Texas +c +c\Revision history: +c 12/16/93: Version '1.1' +c +c\SCCS Information: @(#) +c FILE: naupd.F SID: 2.5 DATE OF SID: 8/27/96 RELEASE: 2 +c +c\Remarks +c +c\EndLib +c +c----------------------------------------------------------------------- +c + subroutine igraphdnaupd + & ( ido, bmat, n, which, nev, tol, resid, ncv, v, ldv, iparam, + & ipntr, workd, workl, lworkl, info ) +c +c %----------------------------------------------------% +c | Include files for debugging and timing information | +c %----------------------------------------------------% +c + include 'debug.h' + include 'stat.h' +c +c %------------------% +c | Scalar Arguments | +c %------------------% +c + character bmat*1, which*2 + integer ido, info, ldv, lworkl, n, ncv, nev + Double precision + & tol +c +c %-----------------% +c | Array Arguments | +c %-----------------% +c + integer iparam(11), ipntr(14) + Double precision + & resid(n), v(ldv,ncv), workd(3*n), workl(lworkl) +c +c %------------% +c | Parameters | +c %------------% +c + Double precision + & one, zero + parameter (one = 1.0D+0, zero = 0.0D+0) +c +c %---------------% +c | Local Scalars | +c %---------------% +c + integer bounds, ierr, ih, iq, ishift, iupd, iw, + & ldh, ldq, levec, mode, msglvl, mxiter, nb, + & nev0, next, np, ritzi, ritzr, j + save bounds, ih, iq, ishift, iupd, iw, ldh, ldq, + & levec, mode, msglvl, mxiter, nb, nev0, next, + & np, ritzi, ritzr +c +c %----------------------% +c | External Subroutines | +c %----------------------% +c + external igraphdnaup2, igraphdvout, igraphivout, + & igraphsecond, igraphdstatn +c +c %--------------------% +c | External Functions | +c %--------------------% +c + Double precision + & dlamch + external dlamch +c +c %-----------------------% +c | Executable Statements | +c %-----------------------% +c + if (ido .eq. 0) then +c +c %-------------------------------% +c | Initialize timing statistics | +c | & message level for debugging | +c %-------------------------------% +c + call igraphdstatn + call igraphsecond (t0) + msglvl = mnaupd +c +c %----------------% +c | Error checking | +c %----------------% +c + ierr = 0 + ishift = iparam(1) + levec = iparam(2) + mxiter = iparam(3) + nb = iparam(4) +c +c %--------------------------------------------% +c | Revision 2 performs only implicit restart. | +c %--------------------------------------------% +c + iupd = 1 + mode = iparam(7) +c + if (n .le. 0) then + ierr = -1 + else if (nev .le. 0) then + ierr = -2 + else if (ncv .le. nev+1 .or. ncv .gt. n) then + ierr = -3 + else if (mxiter .le. 0) then + ierr = -4 + else if (which .ne. 'LM' .and. + & which .ne. 'SM' .and. + & which .ne. 'LR' .and. + & which .ne. 'SR' .and. + & which .ne. 'LI' .and. + & which .ne. 'SI') then + ierr = -5 + else if (bmat .ne. 'I' .and. bmat .ne. 'G') then + ierr = -6 + else if (lworkl .lt. 3*ncv**2 + 6*ncv) then + ierr = -7 + else if (mode .lt. 1 .or. mode .gt. 5) then + ierr = -10 + else if (mode .eq. 1 .and. bmat .eq. 'G') then + ierr = -11 + else if (ishift .lt. 0 .or. ishift .gt. 1) then + ierr = -12 + end if +c +c %------------% +c | Error Exit | +c %------------% +c + if (ierr .ne. 0) then + info = ierr + ido = 99 + go to 9000 + end if +c +c %------------------------% +c | Set default parameters | +c %------------------------% +c + if (nb .le. 0) nb = 1 + if (tol .le. zero) tol = dlamch('EpsMach') +c +c %----------------------------------------------% +c | NP is the number of additional steps to | +c | extend the length NEV Lanczos factorization. | +c | NEV0 is the local variable designating the | +c | size of the invariant subspace desired. | +c %----------------------------------------------% +c + np = ncv - nev + nev0 = nev +c +c %-----------------------------% +c | Zero out internal workspace | +c %-----------------------------% +c + do 10 j = 1, 3*ncv**2 + 6*ncv + workl(j) = zero + 10 continue +c +c %-------------------------------------------------------------% +c | Pointer into WORKL for address of H, RITZ, BOUNDS, Q | +c | etc... and the remaining workspace. | +c | Also update pointer to be used on output. | +c | Memory is laid out as follows: | +c | workl(1:ncv*ncv) := generated Hessenberg matrix | +c | workl(ncv*ncv+1:ncv*ncv+2*ncv) := real and imaginary | +c | parts of ritz values | +c | workl(ncv*ncv+2*ncv+1:ncv*ncv+3*ncv) := error bounds | +c | workl(ncv*ncv+3*ncv+1:2*ncv*ncv+3*ncv) := rotation matrix Q | +c | workl(2*ncv*ncv+3*ncv+1:3*ncv*ncv+6*ncv) := workspace | +c | The final workspace is needed by subroutine igraphdneigh called | +c | by igraphdnaup2. Subroutine igraphdneigh calls LAPACK routines for | +c | calculating eigenvalues and the last row of the eigenvector | +c | matrix. | +c %-------------------------------------------------------------% +c + ldh = ncv + ldq = ncv + ih = 1 + ritzr = ih + ldh*ncv + ritzi = ritzr + ncv + bounds = ritzi + ncv + iq = bounds + ncv + iw = iq + ldq*ncv + next = iw + ncv**2 + 3*ncv +c + ipntr(4) = next + ipntr(5) = ih + ipntr(6) = ritzr + ipntr(7) = ritzi + ipntr(8) = bounds + ipntr(14) = iw +c + end if +c +c %-------------------------------------------------------% +c | Carry out the Implicitly restarted Arnoldi Iteration. | +c %-------------------------------------------------------% +c + call igraphdnaup2 + & ( ido, bmat, n, which, nev0, np, tol, resid, mode, iupd, + & ishift, mxiter, v, ldv, workl(ih), ldh, workl(ritzr), + & workl(ritzi), workl(bounds), workl(iq), ldq, workl(iw), + & ipntr, workd, info ) +c +c %--------------------------------------------------% +c | ido .ne. 99 implies use of reverse communication | +c | to compute operations involving OP or shifts. | +c %--------------------------------------------------% +c + if (ido .eq. 3) iparam(8) = np + if (ido .ne. 99) go to 9000 +c + iparam(3) = mxiter + iparam(5) = np + iparam(9) = nopx + iparam(10) = nbx + iparam(11) = nrorth +c +c %------------------------------------% +c | Exit if there was an informational | +c | error within igraphdnaup2. | +c %------------------------------------% +c + if (info .lt. 0) go to 9000 + if (info .eq. 2) info = 3 +c + if (msglvl .gt. 0) then + call igraphivout (logfil, 1, [mxiter], ndigit, + & '_naupd: Number of update iterations taken') + call igraphivout (logfil, 1, [np], ndigit, + & '_naupd: Number of wanted "converged" Ritz values') + call igraphdvout (logfil, np, workl(ritzr), ndigit, + & '_naupd: Real part of the final Ritz values') + call igraphdvout (logfil, np, workl(ritzi), ndigit, + & '_naupd: Imaginary part of the final Ritz values') + call igraphdvout (logfil, np, workl(bounds), ndigit, + & '_naupd: Associated Ritz estimates') + end if +c + call igraphsecond (t1) + tnaupd = t1 - t0 +c +c + 9000 continue +c + return +c +c %---------------% +c | End of igraphdnaupd | +c %---------------% +c + end diff --git a/src/rigraph/vendor/arpack/dnconv.f b/src/rigraph/vendor/arpack/dnconv.f new file mode 100644 index 0000000..4735159 --- /dev/null +++ b/src/rigraph/vendor/arpack/dnconv.f @@ -0,0 +1,146 @@ +c----------------------------------------------------------------------- +c\BeginDoc +c +c\Name: igraphdnconv +c +c\Description: +c Convergence testing for the nonsymmetric Arnoldi eigenvalue routine. +c +c\Usage: +c call igraphdnconv +c ( N, RITZR, RITZI, BOUNDS, TOL, NCONV ) +c +c\Arguments +c N Integer. (INPUT) +c Number of Ritz values to check for convergence. +c +c RITZR, Double precision arrays of length N. (INPUT) +c RITZI Real and imaginary parts of the Ritz values to be checked +c for convergence. + +c BOUNDS Double precision array of length N. (INPUT) +c Ritz estimates for the Ritz values in RITZR and RITZI. +c +c TOL Double precision scalar. (INPUT) +c Desired backward error for a Ritz value to be considered +c "converged". +c +c NCONV Integer scalar. (OUTPUT) +c Number of "converged" Ritz values. +c +c\EndDoc +c +c----------------------------------------------------------------------- +c +c\BeginLib +c +c\Local variables: +c xxxxxx real +c +c\Routines called: +c igraphsecond ARPACK utility routine for timing. +c dlamch LAPACK routine that determines machine constants. +c dlapy2 LAPACK routine to compute sqrt(x**2+y**2) carefully. +c +c\Author +c Danny Sorensen Phuong Vu +c Richard Lehoucq CRPC / Rice University +c Dept. of Computational & Houston, Texas +c Applied Mathematics +c Rice University +c Houston, Texas +c +c\Revision history: +c xx/xx/92: Version ' 2.1' +c +c\SCCS Information: @(#) +c FILE: nconv.F SID: 2.3 DATE OF SID: 4/20/96 RELEASE: 2 +c +c\Remarks +c 1. xxxx +c +c\EndLib +c +c----------------------------------------------------------------------- +c + subroutine igraphdnconv (n, ritzr, ritzi, bounds, tol, nconv) +c +c %----------------------------------------------------% +c | Include files for debugging and timing information | +c %----------------------------------------------------% +c + include 'debug.h' + include 'stat.h' +c +c %------------------% +c | Scalar Arguments | +c %------------------% +c + integer n, nconv + Double precision + & tol +c +c %-----------------% +c | Array Arguments | +c %-----------------% + + Double precision + & ritzr(n), ritzi(n), bounds(n) +c +c %---------------% +c | Local Scalars | +c %---------------% +c + integer i + Double precision + & temp, eps23 +c +c %--------------------% +c | External Functions | +c %--------------------% +c + Double precision + & dlapy2, dlamch + external dlapy2, dlamch + +c %-----------------------% +c | Executable Statements | +c %-----------------------% +c +c %-------------------------------------------------------------% +c | Convergence test: unlike in the symmetric code, I am not | +c | using things like refined error bounds and gap condition | +c | because I don't know the exact equivalent concept. | +c | | +c | Instead the i-th Ritz value is considered "converged" when: | +c | | +c | bounds(i) .le. ( TOL * | ritz | ) | +c | | +c | for some appropriate choice of norm. | +c %-------------------------------------------------------------% +c + call igraphsecond (t0) +c +c %---------------------------------% +c | Get machine dependent constant. | +c %---------------------------------% +c + eps23 = dlamch('Epsilon-Machine') + eps23 = eps23**(2.0D+0 / 3.0D+0) +c + nconv = 0 + do 20 i = 1, n + temp = max( eps23, dlapy2( ritzr(i), ritzi(i) ) ) + if (bounds(i) .le. tol*temp) nconv = nconv + 1 + 20 continue +c + call igraphsecond (t1) + tnconv = tnconv + (t1 - t0) +c + return +c +c %---------------% +c | End of igraphdnconv | +c %---------------% +c + end diff --git a/src/rigraph/vendor/arpack/dneigh.f b/src/rigraph/vendor/arpack/dneigh.f new file mode 100644 index 0000000..53c7c89 --- /dev/null +++ b/src/rigraph/vendor/arpack/dneigh.f @@ -0,0 +1,315 @@ +c----------------------------------------------------------------------- +c\BeginDoc +c +c\Name: igraphdneigh +c +c\Description: +c Compute the eigenvalues of the current upper Hessenberg matrix +c and the corresponding Ritz estimates given the current residual norm. +c +c\Usage: +c call igraphdneigh +c ( RNORM, N, H, LDH, RITZR, RITZI, BOUNDS, Q, LDQ, WORKL, IERR ) +c +c\Arguments +c RNORM Double precision scalar. (INPUT) +c Residual norm corresponding to the current upper Hessenberg +c matrix H. +c +c N Integer. (INPUT) +c Size of the matrix H. +c +c H Double precision N by N array. (INPUT) +c H contains the current upper Hessenberg matrix. +c +c LDH Integer. (INPUT) +c Leading dimension of H exactly as declared in the calling +c program. +c +c RITZR, Double precision arrays of length N. (OUTPUT) +c RITZI On output, RITZR(1:N) (resp. RITZI(1:N)) contains the real +c (respectively imaginary) parts of the eigenvalues of H. +c +c BOUNDS Double precision array of length N. (OUTPUT) +c On output, BOUNDS contains the Ritz estimates associated with +c the eigenvalues RITZR and RITZI. This is equal to RNORM +c times the last components of the eigenvectors corresponding +c to the eigenvalues in RITZR and RITZI. +c +c Q Double precision N by N array. (WORKSPACE) +c Workspace needed to store the eigenvectors of H. +c +c LDQ Integer. (INPUT) +c Leading dimension of Q exactly as declared in the calling +c program. +c +c WORKL Double precision work array of length N**2 + 3*N. (WORKSPACE) +c Private (replicated) array on each PE or array allocated on +c the front end. This is needed to keep the full Schur form +c of H and also in the calculation of the eigenvectors of H. +c +c IERR Integer. (OUTPUT) +c Error exit flag from igraphdlaqrb or dtrevc. +c +c\EndDoc +c +c----------------------------------------------------------------------- +c +c\BeginLib +c +c\Local variables: +c xxxxxx real +c +c\Routines called: +c igraphdlaqrb ARPACK routine to compute the real Schur form of an +c upper Hessenberg matrix and last row of the Schur vectors. +c igraphsecond ARPACK utility routine for timing. +c igraphdmout ARPACK utility routine that prints matrices +c igraphdvout ARPACK utility routine that prints vectors. +c dlacpy LAPACK matrix copy routine. +c dlapy2 LAPACK routine to compute sqrt(x**2+y**2) carefully. +c dtrevc LAPACK routine to compute the eigenvectors of a matrix +c in upper quasi-triangular form +c dgemv Level 2 BLAS routine for matrix vector multiplication. +c dcopy Level 1 BLAS that copies one vector to another . +c dnrm2 Level 1 BLAS that computes the norm of a vector. +c dscal Level 1 BLAS that scales a vector. +c +c +c\Author +c Danny Sorensen Phuong Vu +c Richard Lehoucq CRPC / Rice University +c Dept. of Computational & Houston, Texas +c Applied Mathematics +c Rice University +c Houston, Texas +c +c\Revision history: +c xx/xx/92: Version ' 2.1' +c +c\SCCS Information: @(#) +c FILE: neigh.F SID: 2.3 DATE OF SID: 4/20/96 RELEASE: 2 +c +c\Remarks +c None +c +c\EndLib +c +c----------------------------------------------------------------------- +c + subroutine igraphdneigh (rnorm, n, h, ldh, ritzr, ritzi, bounds, + & q, ldq, workl, ierr) +c +c %----------------------------------------------------% +c | Include files for debugging and timing information | +c %----------------------------------------------------% +c + include 'debug.h' + include 'stat.h' +c +c %------------------% +c | Scalar Arguments | +c %------------------% +c + integer ierr, n, ldh, ldq + Double precision + & rnorm +c +c %-----------------% +c | Array Arguments | +c %-----------------% +c + Double precision + & bounds(n), h(ldh,n), q(ldq,n), ritzi(n), ritzr(n), + & workl(n*(n+3)) +c +c %------------% +c | Parameters | +c %------------% +c + Double precision + & one, zero + parameter (one = 1.0D+0, zero = 0.0D+0) +c +c %------------------------% +c | Local Scalars & Arrays | +c %------------------------% +c + logical select(1) + integer i, iconj, msglvl + Double precision + & temp, vl(1) +c +c %----------------------% +c | External Subroutines | +c %----------------------% +c + external dcopy, dlacpy, igraphdlaqrb, dtrevc, igraphdvout, + & igraphsecond +c +c %--------------------% +c | External Functions | +c %--------------------% +c + Double precision + & dlapy2, dnrm2 + external dlapy2, dnrm2 +c +c %---------------------% +c | Intrinsic Functions | +c %---------------------% +c + intrinsic abs +c +c %-----------------------% +c | Executable Statements | +c %-----------------------% +c +c +c %-------------------------------% +c | Initialize timing statistics | +c | & message level for debugging | +c %-------------------------------% +c + call igraphsecond (t0) + msglvl = mneigh +c + if (msglvl .gt. 2) then + call igraphdmout (logfil, n, n, h, ldh, ndigit, + & '_neigh: Entering upper Hessenberg matrix H ') + end if +c +c %-----------------------------------------------------------% +c | 1. Compute the eigenvalues, the last components of the | +c | corresponding Schur vectors and the full Schur form T | +c | of the current upper Hessenberg matrix H. | +c | igraphdlaqrb returns the full Schur form of H in WORKL(1:N**2) | +c | and the last components of the Schur vectors in BOUNDS. | +c %-----------------------------------------------------------% +c + call dlacpy ('All', n, n, h, ldh, workl, n) + call igraphdlaqrb (.true., n, 1, n, workl, n, ritzr, ritzi, + & bounds, ierr) + if (ierr .ne. 0) go to 9000 +c + if (msglvl .gt. 1) then + call igraphdvout (logfil, n, bounds, ndigit, + & '_neigh: last row of the Schur matrix for H') + end if +c +c %-----------------------------------------------------------% +c | 2. Compute the eigenvectors of the full Schur form T and | +c | apply the last components of the Schur vectors to get | +c | the last components of the corresponding eigenvectors. | +c | Remember that if the i-th and (i+1)-st eigenvalues are | +c | complex conjugate pairs, then the real & imaginary part | +c | of the eigenvector components are split across adjacent | +c | columns of Q. | +c %-----------------------------------------------------------% +c + call dtrevc ('R', 'A', select, n, workl, n, vl, n, q, ldq, + & n, n, workl(n*n+1), ierr) +c + if (ierr .ne. 0) go to 9000 +c +c %------------------------------------------------% +c | Scale the returning eigenvectors so that their | +c | euclidean norms are all one. LAPACK subroutine | +c | dtrevc returns each eigenvector normalized so | +c | that the element of largest magnitude has | +c | magnitude 1; here the magnitude of a complex | +c | number (x,y) is taken to be |x| + |y|. | +c %------------------------------------------------% +c + iconj = 0 + do 10 i=1, n + if ( abs( ritzi(i) ) .le. zero ) then +c +c %----------------------% +c | Real eigenvalue case | +c %----------------------% +c + temp = dnrm2( n, q(1,i), 1 ) + call dscal ( n, one / temp, q(1,i), 1 ) + else +c +c %-------------------------------------------% +c | Complex conjugate pair case. Note that | +c | since the real and imaginary part of | +c | the eigenvector are stored in consecutive | +c | columns, we further normalize by the | +c | square root of two. | +c %-------------------------------------------% +c + if (iconj .eq. 0) then + temp = dlapy2( dnrm2( n, q(1,i), 1 ), + & dnrm2( n, q(1,i+1), 1 ) ) + call dscal ( n, one / temp, q(1,i), 1 ) + call dscal ( n, one / temp, q(1,i+1), 1 ) + iconj = 1 + else + iconj = 0 + end if + end if + 10 continue +c + call dgemv ('T', n, n, one, q, ldq, bounds, 1, zero, workl, 1) +c + if (msglvl .gt. 1) then + call igraphdvout (logfil, n, workl, ndigit, + & '_neigh: Last row of the eigenvector matrix for H') + end if +c +c %----------------------------% +c | Compute the Ritz estimates | +c %----------------------------% +c + iconj = 0 + do 20 i = 1, n + if ( abs( ritzi(i) ) .le. zero ) then +c +c %----------------------% +c | Real eigenvalue case | +c %----------------------% +c + bounds(i) = rnorm * abs( workl(i) ) + else +c +c %-------------------------------------------% +c | Complex conjugate pair case. Note that | +c | since the real and imaginary part of | +c | the eigenvector are stored in consecutive | +c | columns, we need to take the magnitude | +c | of the last components of the two vectors | +c %-------------------------------------------% +c + if (iconj .eq. 0) then + bounds(i) = rnorm * dlapy2( workl(i), workl(i+1) ) + bounds(i+1) = bounds(i) + iconj = 1 + else + iconj = 0 + end if + end if + 20 continue +c + if (msglvl .gt. 2) then + call igraphdvout (logfil, n, ritzr, ndigit, + & '_neigh: Real part of the eigenvalues of H') + call igraphdvout (logfil, n, ritzi, ndigit, + & '_neigh: Imaginary part of the eigenvalues of H') + call igraphdvout (logfil, n, bounds, ndigit, + & '_neigh: Ritz estimates for the eigenvalues of H') + end if +c + call igraphsecond (t1) + tneigh = tneigh + (t1 - t0) +c + 9000 continue + return +c +c %---------------% +c | End of igraphdneigh | +c %---------------% +c + end diff --git a/src/rigraph/vendor/arpack/dneupd.f b/src/rigraph/vendor/arpack/dneupd.f new file mode 100644 index 0000000..92b4bc3 --- /dev/null +++ b/src/rigraph/vendor/arpack/dneupd.f @@ -0,0 +1,1044 @@ +c\BeginDoc +c +c\Name: igraphdneupd +c +c\Description: +c +c This subroutine returns the converged approximations to eigenvalues +c of A*z = lambda*B*z and (optionally): +c +c (1) The corresponding approximate eigenvectors; +c +c (2) An orthonormal basis for the associated approximate +c invariant subspace; +c +c (3) Both. +c +c There is negligible additional cost to obtain eigenvectors. An orthonormal +c basis is always computed. There is an additional storage cost of n*nev +c if both are requested (in this case a separate array Z must be supplied). +c +c The approximate eigenvalues and eigenvectors of A*z = lambda*B*z +c are derived from approximate eigenvalues and eigenvectors of +c of the linear operator OP prescribed by the MODE selection in the +c call to DNAUPD. DNAUPD must be called before this routine is called. +c These approximate eigenvalues and vectors are commonly called Ritz +c values and Ritz vectors respectively. They are referred to as such +c in the comments that follow. The computed orthonormal basis for the +c invariant subspace corresponding to these Ritz values is referred to as a +c Schur basis. +c +c See documentation in the header of the subroutine DNAUPD for +c definition of OP as well as other terms and the relation of computed +c Ritz values and Ritz vectors of OP with respect to the given problem +c A*z = lambda*B*z. For a brief description, see definitions of +c IPARAM(7), MODE and WHICH in the documentation of DNAUPD. +c +c\Usage: +c call igraphdneupd +c ( RVEC, HOWMNY, SELECT, DR, DI, Z, LDZ, SIGMAR, SIGMAI, WORKEV, BMAT, +c N, WHICH, NEV, TOL, RESID, NCV, V, LDV, IPARAM, IPNTR, WORKD, WORKL, +c LWORKL, INFO ) +c +c\Arguments: +c RVEC LOGICAL (INPUT) +c Specifies whether a basis for the invariant subspace corresponding +c to the converged Ritz value approximations for the eigenproblem +c A*z = lambda*B*z is computed. +c +c RVEC = .FALSE. Compute Ritz values only. +c +c RVEC = .TRUE. Compute the Ritz vectors or Schur vectors. +c See Remarks below. +c +c HOWMNY Character*1 (INPUT) +c Specifies the form of the basis for the invariant subspace +c corresponding to the converged Ritz values that is to be computed. +c +c = 'A': Compute NEV Ritz vectors; +c = 'P': Compute NEV Schur vectors; +c = 'S': compute some of the Ritz vectors, specified +c by the logical array SELECT. +c +c SELECT Logical array of dimension NCV. (INPUT) +c If HOWMNY = 'S', SELECT specifies the Ritz vectors to be +c computed. To select the Ritz vector corresponding to a +c Ritz value (DR(j), DI(j)), SELECT(j) must be set to .TRUE.. +c If HOWMNY = 'A' or 'P', SELECT is used as internal workspace. +c +c DR Double precision array of dimension NEV+1. (OUTPUT) +c If IPARAM(7) = 1,2 or 3 and SIGMAI=0.0 then on exit: DR contains +c the real part of the Ritz approximations to the eigenvalues of +c A*z = lambda*B*z. +c If IPARAM(7) = 3, 4 and SIGMAI is not equal to zero, then on exit: +c DR contains the real part of the Ritz values of OP computed by +c DNAUPD. A further computation must be performed by the user +c to transform the Ritz values computed for OP by DNAUPD to those +c of the original system A*z = lambda*B*z. See remark 3 below. +c +c DI Double precision array of dimension NEV+1. (OUTPUT) +c On exit, DI contains the imaginary part of the Ritz value +c approximations to the eigenvalues of A*z = lambda*B*z associated +c with DR. +c +c NOTE: When Ritz values are complex, they will come in complex +c conjugate pairs. If eigenvectors are requested, the +c corresponding Ritz vectors will also come in conjugate +c pairs and the real and imaginary parts of these are +c represented in two consecutive columns of the array Z +c (see below). +c +c Z Double precision N by NEV+1 array if RVEC = .TRUE. and HOWMNY = 'A'. (OUTPUT) +c On exit, if RVEC = .TRUE. and HOWMNY = 'A', then the columns of +c Z represent approximate eigenvectors (Ritz vectors) corresponding +c to the NCONV=IPARAM(5) Ritz values for eigensystem +c A*z = lambda*B*z. +c +c The complex Ritz vector associated with the Ritz value +c with positive imaginary part is stored in two consecutive +c columns. The first column holds the real part of the Ritz +c vector and the igraphsecond column holds the imaginary part. The +c Ritz vector associated with the Ritz value with negative +c imaginary part is simply the complex conjugate of the Ritz vector +c associated with the positive imaginary part. +c +c If RVEC = .FALSE. or HOWMNY = 'P', then Z is not referenced. +c +c NOTE: If if RVEC = .TRUE. and a Schur basis is not required, +c the array Z may be set equal to first NEV+1 columns of the Arnoldi +c basis array V computed by DNAUPD. In this case the Arnoldi basis +c will be destroyed and overwritten with the eigenvector basis. +c +c LDZ Integer. (INPUT) +c The leading dimension of the array Z. If Ritz vectors are +c desired, then LDZ >= max( 1, N ). In any case, LDZ >= 1. +c +c SIGMAR Double precision (INPUT) +c If IPARAM(7) = 3 or 4, represents the real part of the shift. +c Not referenced if IPARAM(7) = 1 or 2. +c +c SIGMAI Double precision (INPUT) +c If IPARAM(7) = 3 or 4, represents the imaginary part of the shift. +c Not referenced if IPARAM(7) = 1 or 2. See remark 3 below. +c +c WORKEV Double precision work array of dimension 3*NCV. (WORKSPACE) +c +c **** The remaining arguments MUST be the same as for the **** +c **** call to DNAUPD that was just completed. **** +c +c NOTE: The remaining arguments +c +c BMAT, N, WHICH, NEV, TOL, RESID, NCV, V, LDV, IPARAM, IPNTR, +c WORKD, WORKL, LWORKL, INFO +c +c must be passed directly to DNEUPD following the last call +c to DNAUPD. These arguments MUST NOT BE MODIFIED between +c the the last call to DNAUPD and the call to DNEUPD. +c +c Three of these parameters (V, WORKL, INFO) are also output parameters: +c +c V Double precision N by NCV array. (INPUT/OUTPUT) +c +c Upon INPUT: the NCV columns of V contain the Arnoldi basis +c vectors for OP as constructed by DNAUPD . +c +c Upon OUTPUT: If RVEC = .TRUE. the first NCONV=IPARAM(5) columns +c contain approximate Schur vectors that span the +c desired invariant subspace. See Remark 2 below. +c +c NOTE: If the array Z has been set equal to first NEV+1 columns +c of the array V and RVEC=.TRUE. and HOWMNY= 'A', then the +c Arnoldi basis held by V has been overwritten by the desired +c Ritz vectors. If a separate array Z has been passed then +c the first NCONV=IPARAM(5) columns of V will contain approximate +c Schur vectors that span the desired invariant subspace. +c +c WORKL Double precision work array of length LWORKL. (OUTPUT/WORKSPACE) +c WORKL(1:ncv*ncv+3*ncv) contains information obtained in +c igraphdnaupd. They are not changed by igraphdneupd. +c WORKL(ncv*ncv+3*ncv+1:3*ncv*ncv+6*ncv) holds the +c real and imaginary part of the untransformed Ritz values, +c the upper quasi-triangular matrix for H, and the +c associated matrix representation of the invariant subspace for H. +c +c Note: IPNTR(9:13) contains the pointer into WORKL for addresses +c of the above information computed by igraphdneupd. +c ------------------------------------------------------------- +c IPNTR(9): pointer to the real part of the NCV RITZ values of the +c original system. +c IPNTR(10): pointer to the imaginary part of the NCV RITZ values of +c the original system. +c IPNTR(11): pointer to the NCV corresponding error bounds. +c IPNTR(12): pointer to the NCV by NCV upper quasi-triangular +c Schur matrix for H. +c IPNTR(13): pointer to the NCV by NCV matrix of eigenvectors +c of the upper Hessenberg matrix H. Only referenced by +c igraphdneupd if RVEC = .TRUE. See Remark 2 below. +c ------------------------------------------------------------- +c +c INFO Integer. (OUTPUT) +c Error flag on output. +c +c = 0: Normal exit. +c +c = 1: The Schur form computed by LAPACK routine dlahqr +c could not be reordered by LAPACK routine dtrsen. +c Re-enter subroutine igraphdneupd with IPARAM(5)=NCV and +c increase the size of the arrays DR and DI to have +c dimension at least dimension NCV and allocate at least NCV +c columns for Z. NOTE: Not necessary if Z and V share +c the same space. Please notify the authors if this error +c occurs. +c +c = -1: N must be positive. +c = -2: NEV must be positive. +c = -3: NCV-NEV >= 2 and less than or equal to N. +c = -5: WHICH must be one of 'LM', 'SM', 'LR', 'SR', 'LI', 'SI' +c = -6: BMAT must be one of 'I' or 'G'. +c = -7: Length of private work WORKL array is not sufficient. +c = -8: Error return from calculation of a real Schur form. +c Informational error from LAPACK routine dlahqr. +c = -9: Error return from calculation of eigenvectors. +c Informational error from LAPACK routine dtrevc. +c = -10: IPARAM(7) must be 1,2,3,4. +c = -11: IPARAM(7) = 1 and BMAT = 'G' are incompatible. +c = -12: HOWMNY = 'S' not yet implemented +c = -13: HOWMNY must be one of 'A' or 'P' if RVEC = .true. +c = -14: DNAUPD did not find any eigenvalues to sufficient +c accuracy. +c +c\BeginLib +c +c\References: +c 1. D.C. Sorensen, "Implicit Application of Polynomial Filters in +c a k-Step Arnoldi Method", SIAM J. Matr. Anal. Apps., 13 (1992), +c pp 357-385. +c 2. R.B. Lehoucq, "Analysis and Implementation of an Implicitly +c Restarted Arnoldi Iteration", Rice University Technical Report +c TR95-13, Department of Computational and Applied Mathematics. +c 3. B.N. Parlett & Y. Saad, "Complex Shift and Invert Strategies for +c Real Matrices", Linear Algebra and its Applications, vol 88/89, +c pp 575-595, (1987). +c +c\Routines called: +c igraphivout ARPACK utility routine that prints integers. +c igraphdmout ARPACK utility routine that prints matrices +c igraphdvout ARPACK utility routine that prints vectors. +c dgeqr2 LAPACK routine that computes the QR factorization of +c a matrix. +c dlacpy LAPACK matrix copy routine. +c dlahqr LAPACK routine to compute the real Schur form of an +c upper Hessenberg matrix. +c dlamch LAPACK routine that determines machine constants. +c dlapy2 LAPACK routine to compute sqrt(x**2+y**2) carefully. +c dlaset LAPACK matrix initialization routine. +c dorm2r LAPACK routine that applies an orthogonal matrix in +c factored form. +c dtrevc LAPACK routine to compute the eigenvectors of a matrix +c in upper quasi-triangular form. +c dtrsen LAPACK routine that re-orders the Schur form. +c dtrmm Level 3 BLAS matrix times an upper triangular matrix. +c dger Level 2 BLAS rank one update to a matrix. +c dcopy Level 1 BLAS that copies one vector to another . +c ddot Level 1 BLAS that computes the scalar product of two vectors. +c dnrm2 Level 1 BLAS that computes the norm of a vector. +c dscal Level 1 BLAS that scales a vector. +c +c\Remarks +c +c 1. Currently only HOWMNY = 'A' and 'P' are implemented. +c +c Let X' denote the transpose of X. +c +c 2. Schur vectors are an orthogonal representation for the basis of +c Ritz vectors. Thus, their numerical properties are often superior. +c If RVEC = .TRUE. then the relationship +c A * V(:,1:IPARAM(5)) = V(:,1:IPARAM(5)) * T, and +c V(:,1:IPARAM(5))' * V(:,1:IPARAM(5)) = I are approximately satisfied. +c Here T is the leading submatrix of order IPARAM(5) of the real +c upper quasi-triangular matrix stored workl(ipntr(12)). That is, +c T is block upper triangular with 1-by-1 and 2-by-2 diagonal blocks; +c each 2-by-2 diagonal block has its diagonal elements equal and its +c off-diagonal elements of opposite sign. Corresponding to each 2-by-2 +c diagonal block is a complex conjugate pair of Ritz values. The real +c Ritz values are stored on the diagonal of T. +c +c 3. If IPARAM(7) = 3 or 4 and SIGMAI is not equal zero, then the user must +c form the IPARAM(5) Rayleigh quotients in order to transform the Ritz +c values computed by DNAUPD for OP to those of A*z = lambda*B*z. +c Set RVEC = .true. and HOWMNY = 'A', and +c compute +c Z(:,I)' * A * Z(:,I) if DI(I) = 0. +c If DI(I) is not equal to zero and DI(I+1) = - D(I), +c then the desired real and imaginary parts of the Ritz value are +c Z(:,I)' * A * Z(:,I) + Z(:,I+1)' * A * Z(:,I+1), +c Z(:,I)' * A * Z(:,I+1) - Z(:,I+1)' * A * Z(:,I), respectively. +c Another possibility is to set RVEC = .true. and HOWMNY = 'P' and +c compute V(:,1:IPARAM(5))' * A * V(:,1:IPARAM(5)) and then an upper +c quasi-triangular matrix of order IPARAM(5) is computed. See remark +c 2 above. +c +c\Authors +c Danny Sorensen Phuong Vu +c Richard Lehoucq CRPC / Rice University +c Chao Yang Houston, Texas +c Dept. of Computational & +c Applied Mathematics +c Rice University +c Houston, Texas +c +c\SCCS Information: @(#) +c FILE: neupd.F SID: 2.5 DATE OF SID: 7/31/96 RELEASE: 2 +c +c\EndLib +c +c----------------------------------------------------------------------- + subroutine igraphdneupd (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) +c +c %----------------------------------------------------% +c | Include files for debugging and timing information | +c %----------------------------------------------------% +c + include 'debug.h' + include 'stat.h' +c +c %------------------% +c | Scalar Arguments | +c %------------------% +c + character bmat, howmny, which*2 + logical rvec + integer info, ldz, ldv, lworkl, n, ncv, nev + Double precision + & sigmar, sigmai, tol +c +c %-----------------% +c | Array Arguments | +c %-----------------% +c + integer iparam(11), ipntr(14) + logical select(ncv) + Double precision + & dr(nev+1), di(nev+1), resid(n), v(ldv,ncv), z(ldz,*), + & workd(3*n), workl(lworkl), workev(3*ncv) +c +c %------------% +c | Parameters | +c %------------% +c + Double precision + & one, zero + parameter (one = 1.0D+0, zero = 0.0D+0) +c +c %---------------% +c | Local Scalars | +c %---------------% +c + character type*6 + integer bounds, ierr, ih, ihbds, iheigr, iheigi, iconj, nconv, + & invsub, iuptri, iwev, iwork(1), j, k, ktrord, + & ldh, ldq, mode, msglvl, outncv, ritzr, ritzi, wri, wrr, + & irr, iri, ibd + logical reord + Double precision + & conds, rnorm, sep, temp, thres, vl(1,1), temp1, eps23 +c +c %----------------------% +c | External Subroutines | +c %----------------------% +c + external dcopy, dger, dgeqr2, dlacpy, dlahqr, dlaset, + & igraphdmout, dorm2r, dtrevc, dtrmm, dtrsen, dscal, + & igraphdvout, igraphivout +c +c %--------------------% +c | External Functions | +c %--------------------% +c + Double precision + & dlapy2, dnrm2, dlamch, ddot + external dlapy2, dnrm2, dlamch, ddot +c +c %---------------------% +c | Intrinsic Functions | +c %---------------------% +c + intrinsic abs, min, sqrt +c +c %-----------------------% +c | Executable Statements | +c %-----------------------% +c +c %------------------------% +c | Set default parameters | +c %------------------------% +c + msglvl = mneupd + mode = iparam(7) + nconv = iparam(5) + info = 0 +c +c %---------------------------------% +c | Get machine dependent constant. | +c %---------------------------------% +c + eps23 = dlamch('Epsilon-Machine') + eps23 = eps23**(2.0D+0 / 3.0D+0) +c +c %--------------% +c | Quick return | +c %--------------% +c + ierr = 0 +c + if (nconv .le. 0) then + ierr = -14 + else if (n .le. 0) then + ierr = -1 + else if (nev .le. 0) then + ierr = -2 + else if (ncv .le. nev+1 .or. ncv .gt. n) then + ierr = -3 + else if (which .ne. 'LM' .and. + & which .ne. 'SM' .and. + & which .ne. 'LR' .and. + & which .ne. 'SR' .and. + & which .ne. 'LI' .and. + & which .ne. 'SI') then + ierr = -5 + else if (bmat .ne. 'I' .and. bmat .ne. 'G') then + ierr = -6 + else if (lworkl .lt. 3*ncv**2 + 6*ncv) then + ierr = -7 + else if ( (howmny .ne. 'A' .and. + & howmny .ne. 'P' .and. + & howmny .ne. 'S') .and. rvec ) then + ierr = -13 + else if (howmny .eq. 'S' ) then + ierr = -12 + end if +c + if (mode .eq. 1 .or. mode .eq. 2) then + type = 'REGULR' + else if (mode .eq. 3 .and. sigmai .eq. zero) then + type = 'SHIFTI' + else if (mode .eq. 3 ) then + type = 'REALPT' + else if (mode .eq. 4 ) then + type = 'IMAGPT' + else + ierr = -10 + end if + if (mode .eq. 1 .and. bmat .eq. 'G') ierr = -11 +c +c %------------% +c | Error Exit | +c %------------% +c + if (ierr .ne. 0) then + info = ierr + go to 9000 + end if +c +c %--------------------------------------------------------% +c | Pointer into WORKL for address of H, RITZ, BOUNDS, Q | +c | etc... and the remaining workspace. | +c | Also update pointer to be used on output. | +c | Memory is laid out as follows: | +c | workl(1:ncv*ncv) := generated Hessenberg matrix | +c | workl(ncv*ncv+1:ncv*ncv+2*ncv) := real and imaginary | +c | parts of ritz values | +c | workl(ncv*ncv+2*ncv+1:ncv*ncv+3*ncv) := error bounds | +c %--------------------------------------------------------% +c +c %-----------------------------------------------------------% +c | The following is used and set by DNEUPD. | +c | workl(ncv*ncv+3*ncv+1:ncv*ncv+4*ncv) := The untransformed | +c | real part of the Ritz values. | +c | workl(ncv*ncv+4*ncv+1:ncv*ncv+5*ncv) := The untransformed | +c | imaginary part of the Ritz values. | +c | workl(ncv*ncv+5*ncv+1:ncv*ncv+6*ncv) := The untransformed | +c | error bounds of the Ritz values | +c | workl(ncv*ncv+6*ncv+1:2*ncv*ncv+6*ncv) := Holds the upper | +c | quasi-triangular matrix for H | +c | workl(2*ncv*ncv+6*ncv+1: 3*ncv*ncv+6*ncv) := Holds the | +c | associated matrix representation of the invariant | +c | subspace for H. | +c | GRAND total of NCV * ( 3 * NCV + 6 ) locations. | +c %-----------------------------------------------------------% +c + 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 +c +c %-----------------------------------------% +c | irr points to the REAL part of the Ritz | +c | values computed by _neigh before | +c | exiting _naup2. | +c | iri points to the IMAGINARY part of the | +c | Ritz values computed by _neigh | +c | before exiting _naup2. | +c | ibd points to the Ritz estimates | +c | computed by _neigh before exiting | +c | _naup2. | +c %-----------------------------------------% +c + irr = ipntr(14)+ncv*ncv + iri = irr+ncv + ibd = iri+ncv +c +c %------------------------------------% +c | RNORM is B-norm of the RESID(1:N). | +c %------------------------------------% +c + rnorm = workl(ih+2) + workl(ih+2) = zero +c + if (rvec) then +c +c %-------------------------------------------% +c | Get converged Ritz value on the boundary. | +c | Note: converged Ritz values have been | +c | placed in the first NCONV locations in | +c | workl(ritzr) and workl(ritzi). They have | +c | been sorted (in _naup2) according to the | +c | WHICH selection criterion. | +c %-------------------------------------------% +c + if (which .eq. 'LM' .or. which .eq. 'SM') then + thres = dlapy2( workl(ritzr), workl(ritzi) ) + else if (which .eq. 'LR' .or. which .eq. 'SR') then + thres = workl(ritzr) + else if (which .eq. 'LI' .or. which .eq. 'SI') then + thres = abs( workl(ritzi) ) + end if +c + if (msglvl .gt. 2) then + call igraphdvout(logfil, 1, [thres], ndigit, + & '_neupd: Threshold eigenvalue used for re-ordering') + end if +c +c %----------------------------------------------------------% +c | Check to see if all converged Ritz values appear at the | +c | top of the upper quasi-triangular matrix computed by | +c | _neigh in _naup2. This is done in the following way: | +c | | +c | 1) For each Ritz value obtained from _neigh, compare it | +c | with the threshold Ritz value computed above to | +c | determine whether it is a wanted one. | +c | | +c | 2) If it is wanted, then check the corresponding Ritz | +c | estimate to see if it has converged. If it has, set | +c | correponding entry in the logical array SELECT to | +c | .TRUE.. | +c | | +c | If SELECT(j) = .TRUE. and j > NCONV, then there is a | +c | converged Ritz value that does not appear at the top of | +c | the upper quasi-triangular matrix computed by _neigh in | +c | _naup2. Reordering is needed. | +c %----------------------------------------------------------% +c + reord = .false. + ktrord = 0 + do 10 j = 0, ncv-1 + select(j+1) = .false. + if (which .eq. 'LM') then + if (dlapy2(workl(irr+j), workl(iri+j)) + & .ge. thres) then + temp1 = max( eps23, + & dlapy2( workl(irr+j), workl(iri+j) ) ) + if (workl(ibd+j) .le. tol*temp1) + & select(j+1) = .true. + end if + else if (which .eq. 'SM') then + if (dlapy2(workl(irr+j), workl(iri+j)) + & .le. thres) then + temp1 = max( eps23, + & dlapy2( workl(irr+j), workl(iri+j) ) ) + if (workl(ibd+j) .le. tol*temp1) + & select(j+1) = .true. + end if + else if (which .eq. 'LR') then + if (workl(irr+j) .ge. thres) then + temp1 = max( eps23, + & dlapy2( workl(irr+j), workl(iri+j) ) ) + if (workl(ibd+j) .le. tol*temp1) + & select(j+1) = .true. + end if + else if (which .eq. 'SR') then + if (workl(irr+j) .le. thres) then + temp1 = max( eps23, + & dlapy2( workl(irr+j), workl(iri+j) ) ) + if (workl(ibd+j) .le. tol*temp1) + & select(j+1) = .true. + end if + else if (which .eq. 'LI') then + if (abs(workl(iri+j)) .ge. thres) then + temp1 = max( eps23, + & dlapy2( workl(irr+j), workl(iri+j) ) ) + if (workl(ibd+j) .le. tol*temp1) + & select(j+1) = .true. + end if + else if (which .eq. 'SI') then + if (abs(workl(iri+j)) .le. thres) then + temp1 = max( eps23, + & dlapy2( workl(irr+j), workl(iri+j) ) ) + if (workl(ibd+j) .le. tol*temp1) + & select(j+1) = .true. + end if + end if + if (j+1 .gt. nconv ) reord = ( select(j+1) .or. reord ) + if (select(j+1)) ktrord = ktrord + 1 + 10 continue +c + if (msglvl .gt. 2) then + call igraphivout(logfil, 1, [ktrord], ndigit, + & '_neupd: Number of specified eigenvalues') + call igraphivout(logfil, 1, [nconv], ndigit, + & '_neupd: Number of "converged" eigenvalues') + end if +c +c %-----------------------------------------------------------% +c | Call LAPACK routine dlahqr to compute the real Schur form | +c | of the upper Hessenberg matrix returned by DNAUPD. | +c | Make a copy of the upper Hessenberg matrix. | +c | Initialize the Schur vector matrix Q to the identity. | +c %-----------------------------------------------------------% +c + call dcopy (ldh*ncv, workl(ih), 1, workl(iuptri), 1) + call dlaset ('All', ncv, ncv, zero, one, workl(invsub), ldq) + call dlahqr (.true., .true., ncv, 1, ncv, workl(iuptri), ldh, + & workl(iheigr), workl(iheigi), 1, ncv, + & workl(invsub), ldq, ierr) + call dcopy (ncv, workl(invsub+ncv-1), ldq, workl(ihbds), 1) +c + if (ierr .ne. 0) then + info = -8 + go to 9000 + end if +c + if (msglvl .gt. 1) then + call igraphdvout (logfil, ncv, workl(iheigr), ndigit, + & '_neupd: Real part of the eigenvalues of H') + call igraphdvout (logfil, ncv, workl(iheigi), ndigit, + & '_neupd: Imaginary part of the Eigenvalues of H') + call igraphdvout (logfil, ncv, workl(ihbds), ndigit, + & '_neupd: Last row of the Schur vector matrix') + if (msglvl .gt. 3) then + call igraphdmout (logfil, ncv, ncv, workl(iuptri), ldh, + & ndigit, + & '_neupd: The upper quasi-triangular matrix ') + end if + end if +c + if (reord) then +c +c %-----------------------------------------------------% +c | Reorder the computed upper quasi-triangular matrix. | +c %-----------------------------------------------------% +c + call dtrsen ('None', 'V', select, ncv, workl(iuptri), ldh, + & workl(invsub), ldq, workl(iheigr), workl(iheigi), + & nconv, conds, sep, workl(ihbds), ncv, iwork, 1, ierr) +c + if (ierr .eq. 1) then + info = 1 + go to 9000 + end if +c + if (msglvl .gt. 2) then + call igraphdvout (logfil, ncv, workl(iheigr), ndigit, + & '_neupd: Real part of the eigenvalues of H--reordered') + call igraphdvout (logfil, ncv, workl(iheigi), ndigit, + & '_neupd: Imag part of the eigenvalues of H--reordered') + if (msglvl .gt. 3) then + call igraphdmout (logfil, ncv, ncv, workl(iuptri), + & ldq, ndigit, + & '_neupd: Quasi-triangular matrix after re-ordering') + end if + end if +c + end if +c +c %---------------------------------------% +c | Copy the last row of the Schur vector | +c | into workl(ihbds). This will be used | +c | to compute the Ritz estimates of | +c | converged Ritz values. | +c %---------------------------------------% +c + call dcopy(ncv, workl(invsub+ncv-1), ldq, workl(ihbds), 1) +c +c %----------------------------------------------------% +c | Place the computed eigenvalues of H into DR and DI | +c | if a spectral transformation was not used. | +c %----------------------------------------------------% +c + if (type .eq. 'REGULR') then + call dcopy (nconv, workl(iheigr), 1, dr, 1) + call dcopy (nconv, workl(iheigi), 1, di, 1) + end if +c +c %----------------------------------------------------------% +c | Compute the QR factorization of the matrix representing | +c | the wanted invariant subspace located in the first NCONV | +c | columns of workl(invsub,ldq). | +c %----------------------------------------------------------% +c + call dgeqr2 (ncv, nconv, workl(invsub), ldq, workev, + & workev(ncv+1), ierr) +c +c %---------------------------------------------------------% +c | * Postmultiply V by Q using dorm2r. | +c | * Copy the first NCONV columns of VQ into Z. | +c | * Postmultiply Z by R. | +c | The N by NCONV matrix Z is now a matrix representation | +c | of the approximate invariant subspace associated with | +c | the Ritz values in workl(iheigr) and workl(iheigi) | +c | The first NCONV columns of V are now approximate Schur | +c | vectors associated with the real upper quasi-triangular | +c | matrix of order NCONV in workl(iuptri) | +c %---------------------------------------------------------% +c + call dorm2r ('Right', 'Notranspose', n, ncv, nconv, + & workl(invsub), ldq, workev, v, ldv, workd(n+1), ierr) + call dlacpy ('All', n, nconv, v, ldv, z, ldz) +c + do 20 j=1, nconv +c +c %---------------------------------------------------% +c | Perform both a column and row scaling if the | +c | diagonal element of workl(invsub,ldq) is negative | +c | I'm lazy and don't take advantage of the upper | +c | quasi-triangular form of workl(iuptri,ldq) | +c | Note that since Q is orthogonal, R is a diagonal | +c | matrix consisting of plus or minus ones | +c %---------------------------------------------------% +c + if (workl(invsub+(j-1)*ldq+j-1) .lt. zero) then + call dscal (nconv, -one, workl(iuptri+j-1), ldq) + call dscal (nconv, -one, workl(iuptri+(j-1)*ldq), 1) + end if +c + 20 continue +c + if (howmny .eq. 'A') then +c +c %--------------------------------------------% +c | Compute the NCONV wanted eigenvectors of T | +c | located in workl(iuptri,ldq). | +c %--------------------------------------------% +c + do 30 j=1, ncv + if (j .le. nconv) then + select(j) = .true. + else + select(j) = .false. + end if + 30 continue +c + call dtrevc ('Right', 'Select', select, ncv, workl(iuptri), + & ldq, vl, 1, workl(invsub), ldq, ncv, outncv, workev, + & ierr) +c + if (ierr .ne. 0) then + info = -9 + go to 9000 + end if +c +c %------------------------------------------------% +c | Scale the returning eigenvectors so that their | +c | Euclidean norms are all one. LAPACK subroutine | +c | dtrevc returns each eigenvector normalized so | +c | that the element of largest magnitude has | +c | magnitude 1; | +c %------------------------------------------------% +c + iconj = 0 + do 40 j=1, nconv +c + if ( workl(iheigi+j-1) .eq. zero ) then +c +c %----------------------% +c | real eigenvalue case | +c %----------------------% +c + temp = dnrm2( ncv, workl(invsub+(j-1)*ldq), 1 ) + call dscal ( ncv, one / temp, + & workl(invsub+(j-1)*ldq), 1 ) +c + else +c +c %-------------------------------------------% +c | Complex conjugate pair case. Note that | +c | since the real and imaginary part of | +c | the eigenvector are stored in consecutive | +c | columns, we further normalize by the | +c | square root of two. | +c %-------------------------------------------% +c + if (iconj .eq. 0) then + temp = dlapy2( dnrm2( ncv, workl(invsub+(j-1)*ldq), + & 1 ), dnrm2( ncv, workl(invsub+j*ldq), 1) ) + call dscal ( ncv, one / temp, + & workl(invsub+(j-1)*ldq), 1 ) + call dscal ( ncv, one / temp, + & workl(invsub+j*ldq), 1 ) + iconj = 1 + else + iconj = 0 + end if +c + end if +c + 40 continue +c + call dgemv('T', ncv, nconv, one, workl(invsub), + & ldq, workl(ihbds), 1, zero, workev, 1) +c + iconj = 0 + do 45 j=1, nconv + if (workl(iheigi+j-1) .ne. zero) then +c +c %-------------------------------------------% +c | Complex conjugate pair case. Note that | +c | since the real and imaginary part of | +c | the eigenvector are stored in consecutive | +c %-------------------------------------------% +c + if (iconj .eq. 0) then + workev(j) = dlapy2(workev(j), workev(j+1)) + workev(j+1) = workev(j) + iconj = 1 + else + iconj = 0 + end if + end if + 45 continue +c + if (msglvl .gt. 2) then + call dcopy(ncv, workl(invsub+ncv-1), ldq, + & workl(ihbds), 1) + call igraphdvout (logfil, ncv, workl(ihbds), ndigit, + & '_neupd: Last row of the eigenvector matrix for T') + if (msglvl .gt. 3) then + call igraphdmout (logfil, ncv, ncv, workl(invsub), + & ldq, ndigit, + & '_neupd: The eigenvector matrix for T') + end if + end if +c +c %---------------------------------------% +c | Copy Ritz estimates into workl(ihbds) | +c %---------------------------------------% +c + call dcopy(nconv, workev, 1, workl(ihbds), 1) +c +c %---------------------------------------------------------% +c | Compute the QR factorization of the eigenvector matrix | +c | associated with leading portion of T in the first NCONV | +c | columns of workl(invsub,ldq). | +c %---------------------------------------------------------% +c + call dgeqr2 (ncv, nconv, workl(invsub), ldq, workev, + & workev(ncv+1), ierr) +c +c %----------------------------------------------% +c | * Postmultiply Z by Q. | +c | * Postmultiply Z by R. | +c | The N by NCONV matrix Z is now contains the | +c | Ritz vectors associated with the Ritz values | +c | in workl(iheigr) and workl(iheigi). | +c %----------------------------------------------% +c + call dorm2r ('Right', 'Notranspose', n, ncv, nconv, + & workl(invsub), ldq, workev, z, ldz, workd(n+1), ierr) +c + call dtrmm ('Right', 'Upper', 'No transpose', 'Non-unit', + & n, nconv, one, workl(invsub), ldq, z, ldz) +c + end if +c + else +c +c %------------------------------------------------------% +c | An approximate invariant subspace is not needed. | +c | Place the Ritz values computed DNAUPD into DR and DI | +c %------------------------------------------------------% +c + call dcopy (nconv, workl(ritzr), 1, dr, 1) + call dcopy (nconv, workl(ritzi), 1, di, 1) + call dcopy (nconv, workl(ritzr), 1, workl(iheigr), 1) + call dcopy (nconv, workl(ritzi), 1, workl(iheigi), 1) + call dcopy (nconv, workl(bounds), 1, workl(ihbds), 1) + end if +c +c %------------------------------------------------% +c | Transform the Ritz values and possibly vectors | +c | and corresponding error bounds of OP to those | +c | of A*x = lambda*B*x. | +c %------------------------------------------------% +c + if (type .eq. 'REGULR') then +c + if (rvec) + & call dscal (ncv, rnorm, workl(ihbds), 1) +c + else +c +c %---------------------------------------% +c | A spectral transformation was used. | +c | * Determine the Ritz estimates of the | +c | Ritz values in the original system. | +c %---------------------------------------% +c + if (type .eq. 'SHIFTI') then +c + if (rvec) + & call dscal (ncv, rnorm, workl(ihbds), 1) +c + do 50 k=1, ncv + temp = dlapy2( workl(iheigr+k-1), + & workl(iheigi+k-1) ) + workl(ihbds+k-1) = abs( workl(ihbds+k-1) ) + & / temp / temp + 50 continue +c + else if (type .eq. 'REALPT') then +c + do 60 k=1, ncv + 60 continue +c + else if (type .eq. 'IMAGPT') then +c + do 70 k=1, ncv + 70 continue +c + end if +c +c %-----------------------------------------------------------% +c | * Transform the Ritz values back to the original system. | +c | For TYPE = 'SHIFTI' the transformation is | +c | lambda = 1/theta + sigma | +c | For TYPE = 'REALPT' or 'IMAGPT' the user must from | +c | Rayleigh quotients or a projection. See remark 3 above.| +c | NOTES: | +c | *The Ritz vectors are not affected by the transformation. | +c %-----------------------------------------------------------% +c + if (type .eq. 'SHIFTI') then +c + do 80 k=1, ncv + temp = dlapy2( 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 + 80 continue +c + call dcopy (nconv, workl(iheigr), 1, dr, 1) + call dcopy (nconv, workl(iheigi), 1, di, 1) +c + else if (type .eq. 'REALPT' .or. type .eq. 'IMAGPT') then +c + call dcopy (nconv, workl(iheigr), 1, dr, 1) + call dcopy (nconv, workl(iheigi), 1, di, 1) +c + end if +c + end if +c + if (type .eq. 'SHIFTI' .and. msglvl .gt. 1) then + call igraphdvout (logfil, nconv, dr, ndigit, + & '_neupd: Untransformed real part of the Ritz valuess.') + call igraphdvout (logfil, nconv, di, ndigit, + & '_neupd: Untransformed imag part of the Ritz valuess.') + call igraphdvout (logfil, nconv, workl(ihbds), ndigit, + & '_neupd: Ritz estimates of untransformed Ritz values.') + else if (type .eq. 'REGULR' .and. msglvl .gt. 1) then + call igraphdvout (logfil, nconv, dr, ndigit, + & '_neupd: Real parts of converged Ritz values.') + call igraphdvout (logfil, nconv, di, ndigit, + & '_neupd: Imag parts of converged Ritz values.') + call igraphdvout (logfil, nconv, workl(ihbds), ndigit, + & '_neupd: Associated Ritz estimates.') + end if +c +c %-------------------------------------------------% +c | Eigenvector Purification step. Formally perform | +c | one of inverse subspace iteration. Only used | +c | for MODE = 2. | +c %-------------------------------------------------% +c + if (rvec .and. howmny .eq. 'A' .and. type .eq. 'SHIFTI') then +c +c %------------------------------------------------% +c | Purify the computed Ritz vectors by adding a | +c | little bit of the residual vector: | +c | T | +c | resid(:)*( e s ) / theta | +c | NCV | +c | where H s = s theta. Remember that when theta | +c | has nonzero imaginary part, the corresponding | +c | Ritz vector is stored across two columns of Z. | +c %------------------------------------------------% +c + iconj = 0 + do 110 j=1, nconv + if (workl(iheigi+j-1) .eq. zero) then + workev(j) = workl(invsub+(j-1)*ldq+ncv-1) / + & workl(iheigr+j-1) + else if (iconj .eq. 0) then + temp = dlapy2( 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 + end if + 110 continue +c +c %---------------------------------------% +c | Perform a rank one update to Z and | +c | purify all the Ritz vectors together. | +c %---------------------------------------% +c + call dger (n, nconv, one, resid, 1, workev, 1, z, ldz) +c + end if +c + 9000 continue +c + return +c +c %---------------% +c | End of DNEUPD | +c %---------------% +c + end diff --git a/src/rigraph/vendor/arpack/dngets.f b/src/rigraph/vendor/arpack/dngets.f new file mode 100644 index 0000000..0202090 --- /dev/null +++ b/src/rigraph/vendor/arpack/dngets.f @@ -0,0 +1,231 @@ +c----------------------------------------------------------------------- +c\BeginDoc +c +c\Name: igraphdngets +c +c\Description: +c Given the eigenvalues of the upper Hessenberg matrix H, +c computes the NP shifts AMU that are zeros of the polynomial of +c degree NP which filters out components of the unwanted eigenvectors +c corresponding to the AMU's based on some given criteria. +c +c NOTE: call this even in the case of user specified shifts in order +c to sort the eigenvalues, and error bounds of H for later use. +c +c\Usage: +c call igraphdngets +c ( ISHIFT, WHICH, KEV, NP, RITZR, RITZI, BOUNDS, SHIFTR, SHIFTI ) +c +c\Arguments +c ISHIFT Integer. (INPUT) +c Method for selecting the implicit shifts at each iteration. +c ISHIFT = 0: user specified shifts +c ISHIFT = 1: exact shift with respect to the matrix H. +c +c WHICH Character*2. (INPUT) +c Shift selection criteria. +c 'LM' -> want the KEV eigenvalues of largest magnitude. +c 'SM' -> want the KEV eigenvalues of smallest magnitude. +c 'LR' -> want the KEV eigenvalues of largest real part. +c 'SR' -> want the KEV eigenvalues of smallest real part. +c 'LI' -> want the KEV eigenvalues of largest imaginary part. +c 'SI' -> want the KEV eigenvalues of smallest imaginary part. +c +c KEV Integer. (INPUT/OUTPUT) +c INPUT: KEV+NP is the size of the matrix H. +c OUTPUT: Possibly increases KEV by one to keep complex conjugate +c pairs together. +c +c NP Integer. (INPUT/OUTPUT) +c Number of implicit shifts to be computed. +c OUTPUT: Possibly decreases NP by one to keep complex conjugate +c pairs together. +c +c RITZR, Double precision array of length KEV+NP. (INPUT/OUTPUT) +c RITZI On INPUT, RITZR and RITZI contain the real and imaginary +c parts of the eigenvalues of H. +c On OUTPUT, RITZR and RITZI are sorted so that the unwanted +c eigenvalues are in the first NP locations and the wanted +c portion is in the last KEV locations. When exact shifts are +c selected, the unwanted part corresponds to the shifts to +c be applied. Also, if ISHIFT .eq. 1, the unwanted eigenvalues +c are further sorted so that the ones with largest Ritz values +c are first. +c +c BOUNDS Double precision array of length KEV+NP. (INPUT/OUTPUT) +c Error bounds corresponding to the ordering in RITZ. +c +c SHIFTR, SHIFTI *** USE deprecated as of version 2.1. *** +c +c +c\EndDoc +c +c----------------------------------------------------------------------- +c +c\BeginLib +c +c\Local variables: +c xxxxxx real +c +c\Routines called: +c igraphdsortc ARPACK sorting routine. +c dcopy Level 1 BLAS that copies one vector to another . +c +c\Author +c Danny Sorensen Phuong Vu +c Richard Lehoucq CRPC / Rice University +c Dept. of Computational & Houston, Texas +c Applied Mathematics +c Rice University +c Houston, Texas +c +c\Revision history: +c xx/xx/92: Version ' 2.1' +c +c\SCCS Information: @(#) +c FILE: ngets.F SID: 2.3 DATE OF SID: 4/20/96 RELEASE: 2 +c +c\Remarks +c 1. xxxx +c +c\EndLib +c +c----------------------------------------------------------------------- +c + subroutine igraphdngets ( ishift, which, kev, np, ritzr, ritzi, + & bounds, shiftr, shifti ) +c +c %----------------------------------------------------% +c | Include files for debugging and timing information | +c %----------------------------------------------------% +c + include 'debug.h' + include 'stat.h' +c +c %------------------% +c | Scalar Arguments | +c %------------------% +c + character*2 which + integer ishift, kev, np +c +c %-----------------% +c | Array Arguments | +c %-----------------% +c + Double precision + & bounds(kev+np), ritzr(kev+np), ritzi(kev+np), + & shiftr(1), shifti(1) +c +c %------------% +c | Parameters | +c %------------% +c + Double precision + & one, zero + parameter (one = 1.0, zero = 0.0) +c +c %---------------% +c | Local Scalars | +c %---------------% +c + integer msglvl +c +c %----------------------% +c | External Subroutines | +c %----------------------% +c + external dcopy, igraphdsortc, igraphsecond +c +c %----------------------% +c | Intrinsics Functions | +c %----------------------% +c + intrinsic abs +c +c %-----------------------% +c | Executable Statements | +c %-----------------------% +c +c %-------------------------------% +c | Initialize timing statistics | +c | & message level for debugging | +c %-------------------------------% +c + call igraphsecond (t0) + msglvl = mngets +c +c %----------------------------------------------------% +c | LM, SM, LR, SR, LI, SI case. | +c | Sort the eigenvalues of H into the desired order | +c | and apply the resulting order to BOUNDS. | +c | The eigenvalues are sorted so that the wanted part | +c | are always in the last KEV locations. | +c | We first do a pre-processing sort in order to keep | +c | complex conjugate pairs together | +c %----------------------------------------------------% +c + if (which .eq. 'LM') then + call igraphdsortc ('LR', .true., kev+np, ritzr, ritzi, bounds) + else if (which .eq. 'SM') then + call igraphdsortc ('SR', .true., kev+np, ritzr, ritzi, bounds) + else if (which .eq. 'LR') then + call igraphdsortc ('LM', .true., kev+np, ritzr, ritzi, bounds) + else if (which .eq. 'SR') then + call igraphdsortc ('SM', .true., kev+np, ritzr, ritzi, bounds) + else if (which .eq. 'LI') then + call igraphdsortc ('LM', .true., kev+np, ritzr, ritzi, bounds) + else if (which .eq. 'SI') then + call igraphdsortc ('SM', .true., kev+np, ritzr, ritzi, bounds) + end if +c + call igraphdsortc (which, .true., kev+np, ritzr, ritzi, bounds) +c +c %-------------------------------------------------------% +c | Increase KEV by one if the ( ritzr(np),ritzi(np) ) | +c | = ( ritzr(np+1),-ritzi(np+1) ) and ritz(np) .ne. zero | +c | Accordingly decrease NP by one. In other words keep | +c | complex conjugate pairs together. | +c %-------------------------------------------------------% +c + if ( ( ritzr(np+1) - ritzr(np) ) .eq. zero + & .and. ( ritzi(np+1) + ritzi(np) ) .eq. zero ) then + np = np - 1 + kev = kev + 1 + end if +c + if ( ishift .eq. 1 ) then +c +c %-------------------------------------------------------% +c | Sort the unwanted Ritz values used as shifts so that | +c | the ones with largest Ritz estimates are first | +c | This will tend to minimize the effects of the | +c | forward instability of the iteration when they shifts | +c | are applied in subroutine igraphdnapps. | +c | Be careful and use 'SR' since we want to sort BOUNDS! | +c %-------------------------------------------------------% +c + call igraphdsortc ( 'SR', .true., np, bounds, ritzr, ritzi ) + end if +c + call igraphsecond (t1) + tngets = tngets + (t1 - t0) +c + if (msglvl .gt. 0) then + call igraphivout (logfil, 1, [kev], ndigit, '_ngets: KEV is') + call igraphivout (logfil, 1, [np], ndigit, '_ngets: NP is') + call igraphdvout (logfil, kev+np, ritzr, ndigit, + & '_ngets: Eigenvalues of current H matrix -- real part') + call igraphdvout (logfil, kev+np, ritzi, ndigit, + & '_ngets: Eigenvalues of current H matrix -- imag part') + call igraphdvout (logfil, kev+np, bounds, ndigit, + & '_ngets: Ritz estimates of the current KEV+NP Ritz values') + end if +c + return +c +c %---------------% +c | End of igraphdngets | +c %---------------% +c + end diff --git a/src/rigraph/vendor/arpack/dsaitr.f b/src/rigraph/vendor/arpack/dsaitr.f new file mode 100644 index 0000000..4a4698c --- /dev/null +++ b/src/rigraph/vendor/arpack/dsaitr.f @@ -0,0 +1,854 @@ +c----------------------------------------------------------------------- +c\BeginDoc +c +c\Name: igraphdsaitr +c +c\Description: +c Reverse communication interface for applying NP additional steps to +c a K step symmetric Arnoldi factorization. +c +c Input: OP*V_{k} - V_{k}*H = r_{k}*e_{k}^T +c +c with (V_{k}^T)*B*V_{k} = I, (V_{k}^T)*B*r_{k} = 0. +c +c Output: OP*V_{k+p} - V_{k+p}*H = r_{k+p}*e_{k+p}^T +c +c with (V_{k+p}^T)*B*V_{k+p} = I, (V_{k+p}^T)*B*r_{k+p} = 0. +c +c where OP and B are as in igraphdsaupd. The B-norm of r_{k+p} is also +c computed and returned. +c +c\Usage: +c call igraphdsaitr +c ( IDO, BMAT, N, K, NP, MODE, RESID, RNORM, V, LDV, H, LDH, +c IPNTR, WORKD, INFO ) +c +c\Arguments +c IDO Integer. (INPUT/OUTPUT) +c Reverse communication flag. +c ------------------------------------------------------------- +c IDO = 0: first call to the reverse communication interface +c IDO = -1: compute Y = OP * X where +c IPNTR(1) is the pointer into WORK for X, +c IPNTR(2) is the pointer into WORK for Y. +c This is for the restart phase to force the new +c starting vector into the range of OP. +c IDO = 1: compute Y = OP * X where +c IPNTR(1) is the pointer into WORK for X, +c IPNTR(2) is the pointer into WORK for Y, +c IPNTR(3) is the pointer into WORK for B * X. +c IDO = 2: compute Y = B * X where +c IPNTR(1) is the pointer into WORK for X, +c IPNTR(2) is the pointer into WORK for Y. +c IDO = 99: done +c ------------------------------------------------------------- +c When the routine is used in the "shift-and-invert" mode, the +c vector B * Q is already available and does not need to be +c recomputed in forming OP * Q. +c +c BMAT Character*1. (INPUT) +c BMAT specifies the type of matrix B that defines the +c semi-inner product for the operator OP. See igraphdsaupd. +c B = 'I' -> standard eigenvalue problem A*x = lambda*x +c B = 'G' -> generalized eigenvalue problem A*x = lambda*M*x +c +c N Integer. (INPUT) +c Dimension of the eigenproblem. +c +c K Integer. (INPUT) +c Current order of H and the number of columns of V. +c +c NP Integer. (INPUT) +c Number of additional Arnoldi steps to take. +c +c MODE Integer. (INPUT) +c Signifies which form for "OP". If MODE=2 then +c a reduction in the number of B matrix vector multiplies +c is possible since the B-norm of OP*x is equivalent to +c the inv(B)-norm of A*x. +c +c RESID Double precision array of length N. (INPUT/OUTPUT) +c On INPUT: RESID contains the residual vector r_{k}. +c On OUTPUT: RESID contains the residual vector r_{k+p}. +c +c RNORM Double precision scalar. (INPUT/OUTPUT) +c On INPUT the B-norm of r_{k}. +c On OUTPUT the B-norm of the updated residual r_{k+p}. +c +c V Double precision N by K+NP array. (INPUT/OUTPUT) +c On INPUT: V contains the Arnoldi vectors in the first K +c columns. +c On OUTPUT: V contains the new NP Arnoldi vectors in the next +c NP columns. The first K columns are unchanged. +c +c LDV Integer. (INPUT) +c Leading dimension of V exactly as declared in the calling +c program. +c +c H Double precision (K+NP) by 2 array. (INPUT/OUTPUT) +c H is used to store the generated symmetric tridiagonal matrix +c with the subdiagonal in the first column starting at H(2,1) +c and the main diagonal in the igraphsecond column. +c +c LDH Integer. (INPUT) +c Leading dimension of H exactly as declared in the calling +c program. +c +c IPNTR Integer array of length 3. (OUTPUT) +c Pointer to mark the starting locations in the WORK for +c vectors used by the Arnoldi iteration. +c ------------------------------------------------------------- +c IPNTR(1): pointer to the current operand vector X. +c IPNTR(2): pointer to the current result vector Y. +c IPNTR(3): pointer to the vector B * X when used in the +c shift-and-invert mode. X is the current operand. +c ------------------------------------------------------------- +c +c WORKD Double precision work array of length 3*N. (REVERSE COMMUNICATION) +c Distributed array to be used in the basic Arnoldi iteration +c for reverse communication. The calling program should not +c use WORKD as temporary workspace during the iteration !!!!!! +c On INPUT, WORKD(1:N) = B*RESID where RESID is associated +c with the K step Arnoldi factorization. Used to save some +c computation at the first step. +c On OUTPUT, WORKD(1:N) = B*RESID where RESID is associated +c with the K+NP step Arnoldi factorization. +c +c INFO Integer. (OUTPUT) +c = 0: Normal exit. +c > 0: Size of an invariant subspace of OP is found that is +c less than K + NP. +c +c\EndDoc +c +c----------------------------------------------------------------------- +c +c\BeginLib +c +c\Local variables: +c xxxxxx real +c +c\Routines called: +c igraphdgetv0 ARPACK routine to generate the initial vector. +c igraphivout ARPACK utility routine that prints integers. +c igraphdmout ARPACK utility routine that prints matrices. +c igraphdvout ARPACK utility routine that prints vectors. +c dlamch LAPACK routine that determines machine constants. +c dlascl LAPACK routine for careful scaling of a matrix. +c dgemv Level 2 BLAS routine for matrix vector multiplication. +c daxpy Level 1 BLAS that computes a vector triad. +c dscal Level 1 BLAS that scales a vector. +c dcopy Level 1 BLAS that copies one vector to another . +c ddot Level 1 BLAS that computes the scalar product of two vectors. +c dnrm2 Level 1 BLAS that computes the norm of a vector. +c +c\Author +c Danny Sorensen Phuong Vu +c Richard Lehoucq CRPC / Rice University +c Dept. of Computational & Houston, Texas +c Applied Mathematics +c Rice University +c Houston, Texas +c +c\Revision history: +c xx/xx/93: Version ' 2.4' +c +c\SCCS Information: @(#) +c FILE: saitr.F SID: 2.6 DATE OF SID: 8/28/96 RELEASE: 2 +c +c\Remarks +c The algorithm implemented is: +c +c restart = .false. +c Given V_{k} = [v_{1}, ..., v_{k}], r_{k}; +c r_{k} contains the initial residual vector even for k = 0; +c Also assume that rnorm = || B*r_{k} || and B*r_{k} are already +c computed by the calling program. +c +c betaj = rnorm ; p_{k+1} = B*r_{k} ; +c For j = k+1, ..., k+np Do +c 1) if ( betaj < tol ) stop or restart depending on j. +c if ( restart ) generate a new starting vector. +c 2) v_{j} = r(j-1)/betaj; V_{j} = [V_{j-1}, v_{j}]; +c p_{j} = p_{j}/betaj +c 3) r_{j} = OP*v_{j} where OP is defined as in igraphdsaupd +c For shift-invert mode p_{j} = B*v_{j} is already available. +c wnorm = || OP*v_{j} || +c 4) Compute the j-th step residual vector. +c w_{j} = V_{j}^T * B * OP * v_{j} +c r_{j} = OP*v_{j} - V_{j} * w_{j} +c alphaj <- j-th component of w_{j} +c rnorm = || r_{j} || +c betaj+1 = rnorm +c If (rnorm > 0.717*wnorm) accept step and go back to 1) +c 5) Re-orthogonalization step: +c s = V_{j}'*B*r_{j} +c r_{j} = r_{j} - V_{j}*s; rnorm1 = || r_{j} || +c alphaj = alphaj + s_{j}; +c 6) Iterative refinement step: +c If (rnorm1 > 0.717*rnorm) then +c rnorm = rnorm1 +c accept step and go back to 1) +c Else +c rnorm = rnorm1 +c If this is the first time in step 6), go to 5) +c Else r_{j} lies in the span of V_{j} numerically. +c Set r_{j} = 0 and rnorm = 0; go to 1) +c EndIf +c End Do +c +c\EndLib +c +c----------------------------------------------------------------------- +c + subroutine igraphdsaitr + & (ido, bmat, n, k, np, mode, resid, rnorm, v, ldv, h, ldh, + & ipntr, workd, info) +c +c %----------------------------------------------------% +c | Include files for debugging and timing information | +c %----------------------------------------------------% +c + include 'debug.h' + include 'stat.h' +c +c %------------------% +c | Scalar Arguments | +c %------------------% +c + character bmat*1 + integer ido, info, k, ldh, ldv, n, mode, np + Double precision + & rnorm +c +c %-----------------% +c | Array Arguments | +c %-----------------% +c + integer ipntr(3) + Double precision + & h(ldh,2), resid(n), v(ldv,k+np), workd(3*n) +c +c %------------% +c | Parameters | +c %------------% +c + Double precision + & one, zero + parameter (one = 1.0D+0, zero = 0.0D+0) +c +c %---------------% +c | Local Scalars | +c %---------------% +c + logical first, orth1, orth2, rstart, step3, step4 + integer i, ierr, ipj, irj, ivj, iter, itry, j, msglvl, + & infol, jj + Double precision + & rnorm1, wnorm, safmin, temp1 + save orth1, orth2, rstart, step3, step4, + & ierr, ipj, irj, ivj, iter, itry, j, msglvl, + & rnorm1, safmin, wnorm +c +c %-----------------------% +c | Local Array Arguments | +c %-----------------------% +c + Double precision + & xtemp(2) +c +c %----------------------% +c | External Subroutines | +c %----------------------% +c + external daxpy, dcopy, dscal, dgemv, igraphdgetv0, + & igraphdvout, igraphdmout, + & dlascl, igraphivout, igraphsecond +c +c %--------------------% +c | External Functions | +c %--------------------% +c + Double precision + & ddot, dnrm2, dlamch + external ddot, dnrm2, dlamch +c +c %-----------------% +c | Data statements | +c %-----------------% +c + data first / .true. / +c +c %-----------------------% +c | Executable Statements | +c %-----------------------% +c + if (first) then + first = .false. +c +c %--------------------------------% +c | safmin = safe minimum is such | +c | that 1/sfmin does not overflow | +c %--------------------------------% +c + safmin = dlamch('safmin') + end if +c + if (ido .eq. 0) then +c +c %-------------------------------% +c | Initialize timing statistics | +c | & message level for debugging | +c %-------------------------------% +c + call igraphsecond (t0) + msglvl = msaitr +c +c %------------------------------% +c | Initial call to this routine | +c %------------------------------% +c + info = 0 + step3 = .false. + step4 = .false. + rstart = .false. + orth1 = .false. + orth2 = .false. +c +c %--------------------------------% +c | Pointer to the current step of | +c | the factorization to build | +c %--------------------------------% +c + j = k + 1 +c +c %------------------------------------------% +c | Pointers used for reverse communication | +c | when using WORKD. | +c %------------------------------------------% +c + ipj = 1 + irj = ipj + n + ivj = irj + n + end if +c +c %-------------------------------------------------% +c | When in reverse communication mode one of: | +c | STEP3, STEP4, ORTH1, ORTH2, RSTART | +c | will be .true. | +c | STEP3: return from computing OP*v_{j}. | +c | STEP4: return from computing B-norm of OP*v_{j} | +c | ORTH1: return from computing B-norm of r_{j+1} | +c | ORTH2: return from computing B-norm of | +c | correction to the residual vector. | +c | RSTART: return from OP computations needed by | +c | igraphdgetv0. | +c %-------------------------------------------------% +c + if (step3) go to 50 + if (step4) go to 60 + if (orth1) go to 70 + if (orth2) go to 90 + if (rstart) go to 30 +c +c %------------------------------% +c | Else this is the first step. | +c %------------------------------% +c +c %--------------------------------------------------------------% +c | | +c | A R N O L D I I T E R A T I O N L O O P | +c | | +c | Note: B*r_{j-1} is already in WORKD(1:N)=WORKD(IPJ:IPJ+N-1) | +c %--------------------------------------------------------------% +c + 1000 continue +c + if (msglvl .gt. 2) then + call igraphivout (logfil, 1, [j], ndigit, + & '_saitr: generating Arnoldi vector no.') + call igraphdvout (logfil, 1, [rnorm], ndigit, + & '_saitr: B-norm of the current residual =') + end if +c +c %---------------------------------------------------------% +c | Check for exact zero. Equivalent to determing whether a | +c | j-step Arnoldi factorization is present. | +c %---------------------------------------------------------% +c + if (rnorm .gt. zero) go to 40 +c +c %---------------------------------------------------% +c | Invariant subspace found, generate a new starting | +c | vector which is orthogonal to the current Arnoldi | +c | basis and continue the iteration. | +c %---------------------------------------------------% +c + if (msglvl .gt. 0) then + call igraphivout (logfil, 1, [j], ndigit, + & '_saitr: ****** restart at step ******') + end if +c +c %---------------------------------------------% +c | ITRY is the loop variable that controls the | +c | maximum amount of times that a restart is | +c | attempted. NRSTRT is used by stat.h | +c %---------------------------------------------% +c + nrstrt = nrstrt + 1 + itry = 1 + 20 continue + rstart = .true. + ido = 0 + 30 continue +c +c %--------------------------------------% +c | If in reverse communication mode and | +c | RSTART = .true. flow returns here. | +c %--------------------------------------% +c + call igraphdgetv0 (ido, bmat, itry, .false., n, j, v, ldv, + & resid, rnorm, ipntr, workd, ierr) + if (ido .ne. 99) go to 9000 + if (ierr .lt. 0) then + itry = itry + 1 + if (itry .le. 3) go to 20 +c +c %------------------------------------------------% +c | Give up after several restart attempts. | +c | Set INFO to the size of the invariant subspace | +c | which spans OP and exit. | +c %------------------------------------------------% +c + info = j - 1 + call igraphsecond (t1) + tsaitr = tsaitr + (t1 - t0) + ido = 99 + go to 9000 + end if +c + 40 continue +c +c %---------------------------------------------------------% +c | STEP 2: v_{j} = r_{j-1}/rnorm and p_{j} = p_{j}/rnorm | +c | Note that p_{j} = B*r_{j-1}. In order to avoid overflow | +c | when reciprocating a small RNORM, test against lower | +c | machine bound. | +c %---------------------------------------------------------% +c + call dcopy (n, resid, 1, v(1,j), 1) + if (rnorm .ge. safmin) then + temp1 = one / rnorm + call dscal (n, temp1, v(1,j), 1) + call dscal (n, temp1, workd(ipj), 1) + else +c +c %-----------------------------------------% +c | To scale both v_{j} and p_{j} carefully | +c | use LAPACK routine SLASCL | +c %-----------------------------------------% +c + call dlascl ('General', i, i, rnorm, one, n, 1, + & v(1,j), n, infol) + call dlascl ('General', i, i, rnorm, one, n, 1, + & workd(ipj), n, infol) + end if +c +c %------------------------------------------------------% +c | STEP 3: r_{j} = OP*v_{j}; Note that p_{j} = B*v_{j} | +c | Note that this is not quite yet r_{j}. See STEP 4 | +c %------------------------------------------------------% +c + step3 = .true. + nopx = nopx + 1 + call igraphsecond (t2) + call dcopy (n, v(1,j), 1, workd(ivj), 1) + ipntr(1) = ivj + ipntr(2) = irj + ipntr(3) = ipj + ido = 1 +c +c %-----------------------------------% +c | Exit in order to compute OP*v_{j} | +c %-----------------------------------% +c + go to 9000 + 50 continue +c +c %-----------------------------------% +c | Back from reverse communication; | +c | WORKD(IRJ:IRJ+N-1) := OP*v_{j}. | +c %-----------------------------------% +c + call igraphsecond (t3) + tmvopx = tmvopx + (t3 - t2) +c + step3 = .false. +c +c %------------------------------------------% +c | Put another copy of OP*v_{j} into RESID. | +c %------------------------------------------% +c + call dcopy (n, workd(irj), 1, resid, 1) +c +c %-------------------------------------------% +c | STEP 4: Finish extending the symmetric | +c | Arnoldi to length j. If MODE = 2 | +c | then B*OP = B*inv(B)*A = A and | +c | we don't need to compute B*OP. | +c | NOTE: If MODE = 2 WORKD(IVJ:IVJ+N-1) is | +c | assumed to have A*v_{j}. | +c %-------------------------------------------% +c + if (mode .eq. 2) go to 65 + call igraphsecond (t2) + if (bmat .eq. 'G') then + nbx = nbx + 1 + step4 = .true. + ipntr(1) = irj + ipntr(2) = ipj + ido = 2 +c +c %-------------------------------------% +c | Exit in order to compute B*OP*v_{j} | +c %-------------------------------------% +c + go to 9000 + else if (bmat .eq. 'I') then + call dcopy(n, resid, 1 , workd(ipj), 1) + end if + 60 continue +c +c %-----------------------------------% +c | Back from reverse communication; | +c | WORKD(IPJ:IPJ+N-1) := B*OP*v_{j}. | +c %-----------------------------------% +c + if (bmat .eq. 'G') then + call igraphsecond (t3) + tmvbx = tmvbx + (t3 - t2) + end if +c + step4 = .false. +c +c %-------------------------------------% +c | The following is needed for STEP 5. | +c | Compute the B-norm of OP*v_{j}. | +c %-------------------------------------% +c + 65 continue + if (mode .eq. 2) then +c +c %----------------------------------% +c | Note that the B-norm of OP*v_{j} | +c | is the inv(B)-norm of A*v_{j}. | +c %----------------------------------% +c + wnorm = ddot (n, resid, 1, workd(ivj), 1) + wnorm = sqrt(abs(wnorm)) + else if (bmat .eq. 'G') then + wnorm = ddot (n, resid, 1, workd(ipj), 1) + wnorm = sqrt(abs(wnorm)) + else if (bmat .eq. 'I') then + wnorm = dnrm2(n, resid, 1) + end if +c +c %-----------------------------------------% +c | Compute the j-th residual corresponding | +c | to the j step factorization. | +c | Use Classical Gram Schmidt and compute: | +c | w_{j} <- V_{j}^T * B * OP * v_{j} | +c | r_{j} <- OP*v_{j} - V_{j} * w_{j} | +c %-----------------------------------------% +c +c +c %------------------------------------------% +c | Compute the j Fourier coefficients w_{j} | +c | WORKD(IPJ:IPJ+N-1) contains B*OP*v_{j}. | +c %------------------------------------------% +c + if (mode .ne. 2 ) then + call dgemv('T', n, j, one, v, ldv, workd(ipj), 1, zero, + & workd(irj), 1) + else if (mode .eq. 2) then + call dgemv('T', n, j, one, v, ldv, workd(ivj), 1, zero, + & workd(irj), 1) + end if +c +c %--------------------------------------% +c | Orthgonalize r_{j} against V_{j}. | +c | RESID contains OP*v_{j}. See STEP 3. | +c %--------------------------------------% +c + call dgemv('N', n, j, -one, v, ldv, workd(irj), 1, one, + & resid, 1) +c +c %--------------------------------------% +c | Extend H to have j rows and columns. | +c %--------------------------------------% +c + h(j,2) = workd(irj + j - 1) + if (j .eq. 1 .or. rstart) then + h(j,1) = zero + else + h(j,1) = rnorm + end if + call igraphsecond (t4) +c + orth1 = .true. + iter = 0 +c + call igraphsecond (t2) + if (bmat .eq. 'G') then + nbx = nbx + 1 + call dcopy (n, resid, 1, workd(irj), 1) + ipntr(1) = irj + ipntr(2) = ipj + ido = 2 +c +c %----------------------------------% +c | Exit in order to compute B*r_{j} | +c %----------------------------------% +c + go to 9000 + else if (bmat .eq. 'I') then + call dcopy (n, resid, 1, workd(ipj), 1) + end if + 70 continue +c +c %---------------------------------------------------% +c | Back from reverse communication if ORTH1 = .true. | +c | WORKD(IPJ:IPJ+N-1) := B*r_{j}. | +c %---------------------------------------------------% +c + if (bmat .eq. 'G') then + call igraphsecond (t3) + tmvbx = tmvbx + (t3 - t2) + end if +c + orth1 = .false. +c +c %------------------------------% +c | Compute the B-norm of r_{j}. | +c %------------------------------% +c + if (bmat .eq. 'G') then + rnorm = ddot (n, resid, 1, workd(ipj), 1) + rnorm = sqrt(abs(rnorm)) + else if (bmat .eq. 'I') then + rnorm = dnrm2(n, resid, 1) + end if +c +c %-----------------------------------------------------------% +c | STEP 5: Re-orthogonalization / Iterative refinement phase | +c | Maximum NITER_ITREF tries. | +c | | +c | s = V_{j}^T * B * r_{j} | +c | r_{j} = r_{j} - V_{j}*s | +c | alphaj = alphaj + s_{j} | +c | | +c | The stopping criteria used for iterative refinement is | +c | discussed in Parlett's book SEP, page 107 and in Gragg & | +c | Reichel ACM TOMS paper; Algorithm 686, Dec. 1990. | +c | Determine if we need to correct the residual. The goal is | +c | to enforce ||v(:,1:j)^T * r_{j}|| .le. eps * || r_{j} || | +c %-----------------------------------------------------------% +c + if (rnorm .gt. 0.717*wnorm) go to 100 + nrorth = nrorth + 1 +c +c %---------------------------------------------------% +c | Enter the Iterative refinement phase. If further | +c | refinement is necessary, loop back here. The loop | +c | variable is ITER. Perform a step of Classical | +c | Gram-Schmidt using all the Arnoldi vectors V_{j} | +c %---------------------------------------------------% +c + 80 continue +c + if (msglvl .gt. 2) then + xtemp(1) = wnorm + xtemp(2) = rnorm + call igraphdvout (logfil, 2, xtemp, ndigit, + & '_saitr: re-orthonalization ; wnorm and rnorm are') + end if +c +c %----------------------------------------------------% +c | Compute V_{j}^T * B * r_{j}. | +c | WORKD(IRJ:IRJ+J-1) = v(:,1:J)'*WORKD(IPJ:IPJ+N-1). | +c %----------------------------------------------------% +c + call dgemv ('T', n, j, one, v, ldv, workd(ipj), 1, + & zero, workd(irj), 1) +c +c %----------------------------------------------% +c | Compute the correction to the residual: | +c | r_{j} = r_{j} - V_{j} * WORKD(IRJ:IRJ+J-1). | +c | The correction to H is v(:,1:J)*H(1:J,1:J) + | +c | v(:,1:J)*WORKD(IRJ:IRJ+J-1)*e'_j, but only | +c | H(j,j) is updated. | +c %----------------------------------------------% +c + call dgemv ('N', n, j, -one, v, ldv, workd(irj), 1, + & one, resid, 1) +c + if (j .eq. 1 .or. rstart) h(j,1) = zero + h(j,2) = h(j,2) + workd(irj + j - 1) +c + orth2 = .true. + call igraphsecond (t2) + if (bmat .eq. 'G') then + nbx = nbx + 1 + call dcopy (n, resid, 1, workd(irj), 1) + ipntr(1) = irj + ipntr(2) = ipj + ido = 2 +c +c %-----------------------------------% +c | Exit in order to compute B*r_{j}. | +c | r_{j} is the corrected residual. | +c %-----------------------------------% +c + go to 9000 + else if (bmat .eq. 'I') then + call dcopy (n, resid, 1, workd(ipj), 1) + end if + 90 continue +c +c %---------------------------------------------------% +c | Back from reverse communication if ORTH2 = .true. | +c %---------------------------------------------------% +c + if (bmat .eq. 'G') then + call igraphsecond (t3) + tmvbx = tmvbx + (t3 - t2) + end if +c +c %-----------------------------------------------------% +c | Compute the B-norm of the corrected residual r_{j}. | +c %-----------------------------------------------------% +c + if (bmat .eq. 'G') then + rnorm1 = ddot (n, resid, 1, workd(ipj), 1) + rnorm1 = sqrt(abs(rnorm1)) + else if (bmat .eq. 'I') then + rnorm1 = dnrm2(n, resid, 1) + end if +c + if (msglvl .gt. 0 .and. iter .gt. 0) then + call igraphivout (logfil, 1, [j], ndigit, + & '_saitr: Iterative refinement for Arnoldi residual') + if (msglvl .gt. 2) then + xtemp(1) = rnorm + xtemp(2) = rnorm1 + call igraphdvout (logfil, 2, xtemp, ndigit, + & '_saitr: iterative refinement ; rnorm and rnorm1 are') + end if + end if +c +c %-----------------------------------------% +c | Determine if we need to perform another | +c | step of re-orthogonalization. | +c %-----------------------------------------% +c + if (rnorm1 .gt. 0.717*rnorm) then +c +c %--------------------------------% +c | No need for further refinement | +c %--------------------------------% +c + rnorm = rnorm1 +c + else +c +c %-------------------------------------------% +c | Another step of iterative refinement step | +c | is required. NITREF is used by stat.h | +c %-------------------------------------------% +c + nitref = nitref + 1 + rnorm = rnorm1 + iter = iter + 1 + if (iter .le. 1) go to 80 +c +c %-------------------------------------------------% +c | Otherwise RESID is numerically in the span of V | +c %-------------------------------------------------% +c + do 95 jj = 1, n + resid(jj) = zero + 95 continue + rnorm = zero + end if +c +c %----------------------------------------------% +c | Branch here directly if iterative refinement | +c | wasn't necessary or after at most NITER_REF | +c | steps of iterative refinement. | +c %----------------------------------------------% +c + 100 continue +c + rstart = .false. + orth2 = .false. +c + call igraphsecond (t5) + titref = titref + (t5 - t4) +c +c %----------------------------------------------------------% +c | Make sure the last off-diagonal element is non negative | +c | If not perform a similarity transformation on H(1:j,1:j) | +c | and scale v(:,j) by -1. | +c %----------------------------------------------------------% +c + if (h(j,1) .lt. zero) then + h(j,1) = -h(j,1) + if ( j .lt. k+np) then + call dscal(n, -one, v(1,j+1), 1) + else + call dscal(n, -one, resid, 1) + end if + end if +c +c %------------------------------------% +c | STEP 6: Update j = j+1; Continue | +c %------------------------------------% +c + j = j + 1 + if (j .gt. k+np) then + call igraphsecond (t1) + tsaitr = tsaitr + (t1 - t0) + ido = 99 +c + if (msglvl .gt. 1) then + call igraphdvout (logfil, k+np, h(1,2), ndigit, + & '_saitr: main diagonal of matrix H of step K+NP.') + if (k+np .gt. 1) then + call igraphdvout (logfil, k+np-1, h(2,1), ndigit, + & '_saitr: sub diagonal of matrix H of step K+NP.') + end if + end if +c + go to 9000 + end if +c +c %--------------------------------------------------------% +c | Loop back to extend the factorization by another step. | +c %--------------------------------------------------------% +c + go to 1000 +c +c %---------------------------------------------------------------% +c | | +c | E N D O F M A I N I T E R A T I O N L O O P | +c | | +c %---------------------------------------------------------------% +c + 9000 continue + return +c +c %---------------% +c | End of igraphdsaitr | +c %---------------% +c + end diff --git a/src/rigraph/vendor/arpack/dsapps.f b/src/rigraph/vendor/arpack/dsapps.f new file mode 100644 index 0000000..850e3fd --- /dev/null +++ b/src/rigraph/vendor/arpack/dsapps.f @@ -0,0 +1,516 @@ +c----------------------------------------------------------------------- +c\BeginDoc +c +c\Name: igraphdsapps +c +c\Description: +c Given the Arnoldi factorization +c +c A*V_{k} - V_{k}*H_{k} = r_{k+p}*e_{k+p}^T, +c +c apply NP shifts implicitly resulting in +c +c A*(V_{k}*Q) - (V_{k}*Q)*(Q^T* H_{k}*Q) = r_{k+p}*e_{k+p}^T * Q +c +c where Q is an orthogonal matrix of order KEV+NP. Q is the product of +c rotations resulting from the NP bulge chasing sweeps. The updated Arnoldi +c factorization becomes: +c +c A*VNEW_{k} - VNEW_{k}*HNEW_{k} = rnew_{k}*e_{k}^T. +c +c\Usage: +c call igraphdsapps +c ( N, KEV, NP, SHIFT, V, LDV, H, LDH, RESID, Q, LDQ, WORKD ) +c +c\Arguments +c N Integer. (INPUT) +c Problem size, i.e. dimension of matrix A. +c +c KEV Integer. (INPUT) +c INPUT: KEV+NP is the size of the input matrix H. +c OUTPUT: KEV is the size of the updated matrix HNEW. +c +c NP Integer. (INPUT) +c Number of implicit shifts to be applied. +c +c SHIFT Double precision array of length NP. (INPUT) +c The shifts to be applied. +c +c V Double precision N by (KEV+NP) array. (INPUT/OUTPUT) +c INPUT: V contains the current KEV+NP Arnoldi vectors. +c OUTPUT: VNEW = V(1:n,1:KEV); the updated Arnoldi vectors +c are in the first KEV columns of V. +c +c LDV Integer. (INPUT) +c Leading dimension of V exactly as declared in the calling +c program. +c +c H Double precision (KEV+NP) by 2 array. (INPUT/OUTPUT) +c INPUT: H contains the symmetric tridiagonal matrix of the +c Arnoldi factorization with the subdiagonal in the 1st column +c starting at H(2,1) and the main diagonal in the 2nd column. +c OUTPUT: H contains the updated tridiagonal matrix in the +c KEV leading submatrix. +c +c LDH Integer. (INPUT) +c Leading dimension of H exactly as declared in the calling +c program. +c +c RESID Double precision array of length (N). (INPUT/OUTPUT) +c INPUT: RESID contains the the residual vector r_{k+p}. +c OUTPUT: RESID is the updated residual vector rnew_{k}. +c +c Q Double precision KEV+NP by KEV+NP work array. (WORKSPACE) +c Work array used to accumulate the rotations during the bulge +c chase sweep. +c +c LDQ Integer. (INPUT) +c Leading dimension of Q exactly as declared in the calling +c program. +c +c WORKD Double precision work array of length 2*N. (WORKSPACE) +c Distributed array used in the application of the accumulated +c orthogonal matrix Q. +c +c\EndDoc +c +c----------------------------------------------------------------------- +c +c\BeginLib +c +c\Local variables: +c xxxxxx real +c +c\References: +c 1. D.C. Sorensen, "Implicit Application of Polynomial Filters in +c a k-Step Arnoldi Method", SIAM J. Matr. Anal. Apps., 13 (1992), +c pp 357-385. +c 2. R.B. Lehoucq, "Analysis and Implementation of an Implicitly +c Restarted Arnoldi Iteration", Rice University Technical Report +c TR95-13, Department of Computational and Applied Mathematics. +c +c\Routines called: +c igraphivout ARPACK utility routine that prints integers. +c igraphsecond ARPACK utility routine for timing. +c igraphdvout ARPACK utility routine that prints vectors. +c dlamch LAPACK routine that determines machine constants. +c dlartg LAPACK Givens rotation construction routine. +c dlacpy LAPACK matrix copy routine. +c dlaset LAPACK matrix initialization routine. +c dgemv Level 2 BLAS routine for matrix vector multiplication. +c daxpy Level 1 BLAS that computes a vector triad. +c dcopy Level 1 BLAS that copies one vector to another. +c dscal Level 1 BLAS that scales a vector. +c +c\Author +c Danny Sorensen Phuong Vu +c Richard Lehoucq CRPC / Rice University +c Dept. of Computational & Houston, Texas +c Applied Mathematics +c Rice University +c Houston, Texas +c +c\Revision history: +c 12/16/93: Version ' 2.1' +c +c\SCCS Information: @(#) +c FILE: sapps.F SID: 2.5 DATE OF SID: 4/19/96 RELEASE: 2 +c +c\Remarks +c 1. In this version, each shift is applied to all the subblocks of +c the tridiagonal matrix H and not just to the submatrix that it +c comes from. This routine assumes that the subdiagonal elements +c of H that are stored in h(1:kev+np,1) are nonegative upon input +c and enforce this condition upon output. This version incorporates +c deflation. See code for documentation. +c +c\EndLib +c +c----------------------------------------------------------------------- +c + subroutine igraphdsapps + & ( n, kev, np, shift, v, ldv, h, ldh, resid, q, ldq, workd ) +c +c %----------------------------------------------------% +c | Include files for debugging and timing information | +c %----------------------------------------------------% +c + include 'debug.h' + include 'stat.h' +c +c %------------------% +c | Scalar Arguments | +c %------------------% +c + integer kev, ldh, ldq, ldv, n, np +c +c %-----------------% +c | Array Arguments | +c %-----------------% +c + Double precision + & h(ldh,2), q(ldq,kev+np), resid(n), shift(np), + & v(ldv,kev+np), workd(2*n) +c +c %------------% +c | Parameters | +c %------------% +c + Double precision + & one, zero + parameter (one = 1.0D+0, zero = 0.0D+0) +c +c %---------------% +c | Local Scalars | +c %---------------% +c + integer i, iend, istart, itop, j, jj, kplusp, msglvl + logical first + Double precision + & a1, a2, a3, a4, big, c, epsmch, f, g, r, s + save epsmch, first +c +c +c %----------------------% +c | External Subroutines | +c %----------------------% +c + external daxpy, dcopy, dscal, dlacpy, dlartg, dlaset, + & igraphdvout, igraphivout, igraphsecond, dgemv +c +c %--------------------% +c | External Functions | +c %--------------------% +c + Double precision + & dlamch + external dlamch +c +c %----------------------% +c | Intrinsics Functions | +c %----------------------% +c + intrinsic abs +c +c %----------------% +c | Data statments | +c %----------------% +c + data first / .true. / +c +c %-----------------------% +c | Executable Statements | +c %-----------------------% +c + if (first) then + epsmch = dlamch('Epsilon-Machine') + first = .false. + end if + itop = 1 +c +c %-------------------------------% +c | Initialize timing statistics | +c | & message level for debugging | +c %-------------------------------% +c + call igraphsecond (t0) + msglvl = msapps +c + kplusp = kev + np +c +c %----------------------------------------------% +c | Initialize Q to the identity matrix of order | +c | kplusp used to accumulate the rotations. | +c %----------------------------------------------% +c + call dlaset ('All', kplusp, kplusp, zero, one, q, ldq) +c +c %----------------------------------------------% +c | Quick return if there are no shifts to apply | +c %----------------------------------------------% +c + if (np .eq. 0) go to 9000 +c +c %----------------------------------------------------------% +c | Apply the np shifts implicitly. Apply each shift to the | +c | whole matrix and not just to the submatrix from which it | +c | comes. | +c %----------------------------------------------------------% +c + do 90 jj = 1, np +c + istart = itop +c +c %----------------------------------------------------------% +c | Check for splitting and deflation. Currently we consider | +c | an off-diagonal element h(i+1,1) negligible if | +c | h(i+1,1) .le. epsmch*( |h(i,2)| + |h(i+1,2)| ) | +c | for i=1:KEV+NP-1. | +c | If above condition tests true then we set h(i+1,1) = 0. | +c | Note that h(1:KEV+NP,1) are assumed to be non negative. | +c %----------------------------------------------------------% +c + 20 continue +c +c %------------------------------------------------% +c | The following loop exits early if we encounter | +c | a negligible off diagonal element. | +c %------------------------------------------------% +c + do 30 i = istart, kplusp-1 + big = abs(h(i,2)) + abs(h(i+1,2)) + if (h(i+1,1) .le. epsmch*big) then + if (msglvl .gt. 0) then + call igraphivout (logfil, 1, i, ndigit, + & '_sapps: deflation at row/column no.') + call igraphivout (logfil, 1, jj, ndigit, + & '_sapps: occured before shift number.') + call igraphdvout (logfil, 1, h(i+1,1), ndigit, + & '_sapps: the corresponding off diagonal element') + end if + h(i+1,1) = zero + iend = i + go to 40 + end if + 30 continue + iend = kplusp + 40 continue +c + if (istart .lt. iend) then +c +c %--------------------------------------------------------% +c | Construct the plane rotation G'(istart,istart+1,theta) | +c | that attempts to drive h(istart+1,1) to zero. | +c %--------------------------------------------------------% +c + f = h(istart,2) - shift(jj) + g = h(istart+1,1) + call dlartg (f, g, c, s, r) +c +c %-------------------------------------------------------% +c | Apply rotation to the left and right of H; | +c | H <- G' * H * G, where G = G(istart,istart+1,theta). | +c | This will create a "bulge". | +c %-------------------------------------------------------% +c + a1 = c*h(istart,2) + s*h(istart+1,1) + a2 = c*h(istart+1,1) + s*h(istart+1,2) + a4 = c*h(istart+1,2) - s*h(istart+1,1) + a3 = c*h(istart+1,1) - s*h(istart,2) + h(istart,2) = c*a1 + s*a2 + h(istart+1,2) = c*a4 - s*a3 + h(istart+1,1) = c*a3 + s*a4 +c +c %----------------------------------------------------% +c | Accumulate the rotation in the matrix Q; Q <- Q*G | +c %----------------------------------------------------% +c + do 60 j = 1, min(istart+jj,kplusp) + a1 = c*q(j,istart) + s*q(j,istart+1) + q(j,istart+1) = - s*q(j,istart) + c*q(j,istart+1) + q(j,istart) = a1 + 60 continue +c +c +c %----------------------------------------------% +c | The following loop chases the bulge created. | +c | Note that the previous rotation may also be | +c | done within the following loop. But it is | +c | kept separate to make the distinction among | +c | the bulge chasing sweeps and the first plane | +c | rotation designed to drive h(istart+1,1) to | +c | zero. | +c %----------------------------------------------% +c + do 70 i = istart+1, iend-1 +c +c %----------------------------------------------% +c | Construct the plane rotation G'(i,i+1,theta) | +c | that zeros the i-th bulge that was created | +c | by G(i-1,i,theta). g represents the bulge. | +c %----------------------------------------------% +c + f = h(i,1) + g = s*h(i+1,1) +c +c %----------------------------------% +c | Final update with G(i-1,i,theta) | +c %----------------------------------% +c + h(i+1,1) = c*h(i+1,1) + call dlartg (f, g, c, s, r) +c +c %-------------------------------------------% +c | The following ensures that h(1:iend-1,1), | +c | the first iend-2 off diagonal of elements | +c | H, remain non negative. | +c %-------------------------------------------% +c + if (r .lt. zero) then + r = -r + c = -c + s = -s + end if +c +c %--------------------------------------------% +c | Apply rotation to the left and right of H; | +c | H <- G * H * G', where G = G(i,i+1,theta) | +c %--------------------------------------------% +c + h(i,1) = r +c + a1 = c*h(i,2) + s*h(i+1,1) + a2 = c*h(i+1,1) + s*h(i+1,2) + a3 = c*h(i+1,1) - s*h(i,2) + a4 = c*h(i+1,2) - s*h(i+1,1) +c + h(i,2) = c*a1 + s*a2 + h(i+1,2) = c*a4 - s*a3 + h(i+1,1) = c*a3 + s*a4 +c +c %----------------------------------------------------% +c | Accumulate the rotation in the matrix Q; Q <- Q*G | +c %----------------------------------------------------% +c + do 50 j = 1, min( j+jj, kplusp ) + a1 = c*q(j,i) + s*q(j,i+1) + q(j,i+1) = - s*q(j,i) + c*q(j,i+1) + q(j,i) = a1 + 50 continue +c + 70 continue +c + end if +c +c %--------------------------% +c | Update the block pointer | +c %--------------------------% +c + istart = iend + 1 +c +c %------------------------------------------% +c | Make sure that h(iend,1) is non-negative | +c | If not then set h(iend,1) <-- -h(iend,1) | +c | and negate the last column of Q. | +c | We have effectively carried out a | +c | similarity on transformation H | +c %------------------------------------------% +c + if (h(iend,1) .lt. zero) then + h(iend,1) = -h(iend,1) + call dscal(kplusp, -one, q(1,iend), 1) + end if +c +c %--------------------------------------------------------% +c | Apply the same shift to the next block if there is any | +c %--------------------------------------------------------% +c + if (iend .lt. kplusp) go to 20 +c +c %-----------------------------------------------------% +c | Check if we can increase the the start of the block | +c %-----------------------------------------------------% +c + do 80 i = itop, kplusp-1 + if (h(i+1,1) .gt. zero) go to 90 + itop = itop + 1 + 80 continue +c +c %-----------------------------------% +c | Finished applying the jj-th shift | +c %-----------------------------------% +c + 90 continue +c +c %------------------------------------------% +c | All shifts have been applied. Check for | +c | more possible deflation that might occur | +c | after the last shift is applied. | +c %------------------------------------------% +c + do 100 i = itop, kplusp-1 + big = abs(h(i,2)) + abs(h(i+1,2)) + if (h(i+1,1) .le. epsmch*big) then + if (msglvl .gt. 0) then + call igraphivout (logfil, 1, i, ndigit, + & '_sapps: deflation at row/column no.') + call igraphdvout (logfil, 1, h(i+1,1), ndigit, + & '_sapps: the corresponding off diagonal element') + end if + h(i+1,1) = zero + end if + 100 continue +c +c %-------------------------------------------------% +c | Compute the (kev+1)-st column of (V*Q) and | +c | temporarily store the result in WORKD(N+1:2*N). | +c | This is not necessary if h(kev+1,1) = 0. | +c %-------------------------------------------------% +c + if ( h(kev+1,1) .gt. zero ) + & call dgemv ('N', n, kplusp, one, v, ldv, + & q(1,kev+1), 1, zero, workd(n+1), 1) +c +c %-------------------------------------------------------% +c | Compute column 1 to kev of (V*Q) in backward order | +c | taking advantage that Q is an upper triangular matrix | +c | with lower bandwidth np. | +c | Place results in v(:,kplusp-kev:kplusp) temporarily. | +c %-------------------------------------------------------% +c + do 130 i = 1, kev + call dgemv ('N', n, kplusp-i+1, one, v, ldv, + & q(1,kev-i+1), 1, zero, workd, 1) + call dcopy (n, workd, 1, v(1,kplusp-i+1), 1) + 130 continue +c +c %-------------------------------------------------% +c | Move v(:,kplusp-kev+1:kplusp) into v(:,1:kev). | +c %-------------------------------------------------% +c + call dlacpy ('All', n, kev, v(1,np+1), ldv, v, ldv) +c +c %--------------------------------------------% +c | Copy the (kev+1)-st column of (V*Q) in the | +c | appropriate place if h(kev+1,1) .ne. zero. | +c %--------------------------------------------% +c + if ( h(kev+1,1) .gt. zero ) + & call dcopy (n, workd(n+1), 1, v(1,kev+1), 1) +c +c %-------------------------------------% +c | Update the residual vector: | +c | r <- sigmak*r + betak*v(:,kev+1) | +c | where | +c | sigmak = (e_{kev+p}'*Q)*e_{kev} | +c | betak = e_{kev+1}'*H*e_{kev} | +c %-------------------------------------% +c + call dscal (n, q(kplusp,kev), resid, 1) + if (h(kev+1,1) .gt. zero) + & call daxpy (n, h(kev+1,1), v(1,kev+1), 1, resid, 1) +c + if (msglvl .gt. 1) then + call igraphdvout (logfil, 1, q(kplusp,kev), ndigit, + & '_sapps: sigmak of the updated residual vector') + call igraphdvout (logfil, 1, h(kev+1,1), ndigit, + & '_sapps: betak of the updated residual vector') + call igraphdvout (logfil, kev, h(1,2), ndigit, + & '_sapps: updated main diagonal of H for next iteration') + if (kev .gt. 1) then + call igraphdvout (logfil, kev-1, h(2,1), ndigit, + & '_sapps: updated sub diagonal of H for next iteration') + end if + end if +c + call igraphsecond (t1) + tsapps = tsapps + (t1 - t0) +c + 9000 continue + return +c +c %---------------% +c | End of igraphdsapps | +c %---------------% +c + end diff --git a/src/rigraph/vendor/arpack/dsaup2.f b/src/rigraph/vendor/arpack/dsaup2.f new file mode 100644 index 0000000..116dd31 --- /dev/null +++ b/src/rigraph/vendor/arpack/dsaup2.f @@ -0,0 +1,853 @@ +c----------------------------------------------------------------------- +c\BeginDoc +c +c\Name: igraphdsaup2 +c +c\Description: +c Intermediate level interface called by igraphdsaupd. +c +c\Usage: +c call igraphdsaup2 +c ( IDO, BMAT, N, WHICH, NEV, NP, TOL, RESID, MODE, IUPD, +c ISHIFT, MXITER, V, LDV, H, LDH, RITZ, BOUNDS, Q, LDQ, WORKL, +c IPNTR, WORKD, INFO ) +c +c\Arguments +c +c IDO, BMAT, N, WHICH, NEV, TOL, RESID: same as defined in igraphdsaupd. +c MODE, ISHIFT, MXITER: see the definition of IPARAM in igraphdsaupd. +c +c NP Integer. (INPUT/OUTPUT) +c Contains the number of implicit shifts to apply during +c each Arnoldi/Lanczos iteration. +c If ISHIFT=1, NP is adjusted dynamically at each iteration +c to accelerate convergence and prevent stagnation. +c This is also roughly equal to the number of matrix-vector +c products (involving the operator OP) per Arnoldi iteration. +c The logic for adjusting is contained within the current +c subroutine. +c If ISHIFT=0, NP is the number of shifts the user needs +c to provide via reverse comunication. 0 < NP < NCV-NEV. +c NP may be less than NCV-NEV since a leading block of the current +c upper Tridiagonal matrix has split off and contains "unwanted" +c Ritz values. +c Upon termination of the IRA iteration, NP contains the number +c of "converged" wanted Ritz values. +c +c IUPD Integer. (INPUT) +c IUPD .EQ. 0: use explicit restart instead implicit update. +c IUPD .NE. 0: use implicit update. +c +c V Double precision N by (NEV+NP) array. (INPUT/OUTPUT) +c The Lanczos basis vectors. +c +c LDV Integer. (INPUT) +c Leading dimension of V exactly as declared in the calling +c program. +c +c H Double precision (NEV+NP) by 2 array. (OUTPUT) +c H is used to store the generated symmetric tridiagonal matrix +c The subdiagonal is stored in the first column of H starting +c at H(2,1). The main diagonal is stored in the igraphsecond column +c of H starting at H(1,2). If igraphdsaup2 converges store the +c B-norm of the final residual vector in H(1,1). +c +c LDH Integer. (INPUT) +c Leading dimension of H exactly as declared in the calling +c program. +c +c RITZ Double precision array of length NEV+NP. (OUTPUT) +c RITZ(1:NEV) contains the computed Ritz values of OP. +c +c BOUNDS Double precision array of length NEV+NP. (OUTPUT) +c BOUNDS(1:NEV) contain the error bounds corresponding to RITZ. +c +c Q Double precision (NEV+NP) by (NEV+NP) array. (WORKSPACE) +c Private (replicated) work array used to accumulate the +c rotation in the shift application step. +c +c LDQ Integer. (INPUT) +c Leading dimension of Q exactly as declared in the calling +c program. +c +c WORKL Double precision array of length at least 3*(NEV+NP). (INPUT/WORKSPACE) +c Private (replicated) array on each PE or array allocated on +c the front end. It is used in the computation of the +c tridiagonal eigenvalue problem, the calculation and +c application of the shifts and convergence checking. +c If ISHIFT .EQ. O and IDO .EQ. 3, the first NP locations +c of WORKL are used in reverse communication to hold the user +c supplied shifts. +c +c IPNTR Integer array of length 3. (OUTPUT) +c Pointer to mark the starting locations in the WORKD for +c vectors used by the Lanczos iteration. +c ------------------------------------------------------------- +c IPNTR(1): pointer to the current operand vector X. +c IPNTR(2): pointer to the current result vector Y. +c IPNTR(3): pointer to the vector B * X when used in one of +c the spectral transformation modes. X is the current +c operand. +c ------------------------------------------------------------- +c +c WORKD Double precision work array of length 3*N. (REVERSE COMMUNICATION) +c Distributed array to be used in the basic Lanczos iteration +c for reverse communication. The user should not use WORKD +c as temporary workspace during the iteration !!!!!!!!!! +c See Data Distribution Note in igraphdsaupd. +c +c INFO Integer. (INPUT/OUTPUT) +c If INFO .EQ. 0, a randomly initial residual vector is used. +c If INFO .NE. 0, RESID contains the initial residual vector, +c possibly from a previous run. +c Error flag on output. +c = 0: Normal return. +c = 1: All possible eigenvalues of OP has been found. +c NP returns the size of the invariant subspace +c spanning the operator OP. +c = 2: No shifts could be applied. +c = -8: Error return from trid. eigenvalue calculation; +c This should never happen. +c = -9: Starting vector is zero. +c = -9999: Could not build an Lanczos factorization. +c Size that was built in returned in NP. +c +c\EndDoc +c +c----------------------------------------------------------------------- +c +c\BeginLib +c +c\References: +c 1. D.C. Sorensen, "Implicit Application of Polynomial Filters in +c a k-Step Arnoldi Method", SIAM J. Matr. Anal. Apps., 13 (1992), +c pp 357-385. +c 2. R.B. Lehoucq, "Analysis and Implementation of an Implicitly +c Restarted Arnoldi Iteration", Rice University Technical Report +c TR95-13, Department of Computational and Applied Mathematics. +c 3. B.N. Parlett, "The Symmetric Eigenvalue Problem". Prentice-Hall, +c 1980. +c 4. B.N. Parlett, B. Nour-Omid, "Towards a Black Box Lanczos Program", +c Computer Physics Communications, 53 (1989), pp 169-179. +c 5. B. Nour-Omid, B.N. Parlett, T. Ericson, P.S. Jensen, "How to +c Implement the Spectral Transformation", Math. Comp., 48 (1987), +c pp 663-673. +c 6. R.G. Grimes, J.G. Lewis and H.D. Simon, "A Shifted Block Lanczos +c Algorithm for Solving Sparse Symmetric Generalized Eigenproblems", +c SIAM J. Matr. Anal. Apps., January (1993). +c 7. L. Reichel, W.B. Gragg, "Algorithm 686: FORTRAN Subroutines +c for Updating the QR decomposition", ACM TOMS, December 1990, +c Volume 16 Number 4, pp 369-377. +c +c\Routines called: +c igraphdgetv0 ARPACK initial vector generation routine. +c igraphdsaitr ARPACK Lanczos factorization routine. +c igraphdsapps ARPACK application of implicit shifts routine. +c igraphdsconv ARPACK convergence of Ritz values routine. +c igraphdseigt ARPACK compute Ritz values and error bounds routine. +c igraphdsgets ARPACK reorder Ritz values and error bounds routine. +c igraphdsortr ARPACK sorting routine. +c igraphivout ARPACK utility routine that prints integers. +c igraphsecond ARPACK utility routine for timing. +c igraphdvout ARPACK utility routine that prints vectors. +c dlamch LAPACK routine that determines machine constants. +c dcopy Level 1 BLAS that copies one vector to another. +c ddot Level 1 BLAS that computes the scalar product of two vectors. +c dnrm2 Level 1 BLAS that computes the norm of a vector. +c dscal Level 1 BLAS that scales a vector. +c dswap Level 1 BLAS that swaps two vectors. +c +c\Author +c Danny Sorensen Phuong Vu +c Richard Lehoucq CRPC / Rice University +c Dept. of Computational & Houston, Texas +c Applied Mathematics +c Rice University +c Houston, Texas +c +c\Revision history: +c 12/15/93: Version ' 2.4' +c xx/xx/95: Version ' 2.4'. (R.B. Lehoucq) +c +c\SCCS Information: @(#) +c FILE: saup2.F SID: 2.6 DATE OF SID: 8/16/96 RELEASE: 2 +c +c\EndLib +c +c----------------------------------------------------------------------- +c + subroutine igraphdsaup2 + & ( ido, bmat, n, which, nev, np, tol, resid, mode, iupd, + & ishift, mxiter, v, ldv, h, ldh, ritz, bounds, + & q, ldq, workl, ipntr, workd, info ) +c +c %----------------------------------------------------% +c | Include files for debugging and timing information | +c %----------------------------------------------------% +c + include 'debug.h' + include 'stat.h' +c +c %------------------% +c | Scalar Arguments | +c %------------------% +c + character bmat*1, which*2 + integer ido, info, ishift, iupd, ldh, ldq, ldv, mxiter, + & n, mode, nev, np + Double precision + & tol +c +c %-----------------% +c | Array Arguments | +c %-----------------% +c + integer ipntr(3) + Double precision + & bounds(nev+np), h(ldh,2), q(ldq,nev+np), resid(n), + & ritz(nev+np), v(ldv,nev+np), workd(3*n), + & workl(3*(nev+np)) +c +c %------------% +c | Parameters | +c %------------% +c + Double precision + & one, zero + parameter (one = 1.0D+0, zero = 0.0D+0) +c +c %---------------% +c | Local Scalars | +c %---------------% +c + character wprime*2 + logical cnorm, getv0, initv, update, ushift + integer ierr, iter, j, kplusp, msglvl, nconv, nevbef, nev0, + & np0, nptemp, nevd2, nevm2, kp(3) + Double precision + & rnorm, temp, eps23 + save cnorm, getv0, initv, update, ushift, + & iter, kplusp, msglvl, nconv, nev0, np0, + & rnorm, eps23 +c +c %----------------------% +c | External Subroutines | +c %----------------------% +c + external dcopy, igraphdgetv0, igraphdsaitr, dscal, + & igraphdsconv, igraphdseigt, igraphdsgets, + & igraphdsapps, igraphdsortr, igraphdvout, igraphivout, + & igraphsecond, dswap +c +c %--------------------% +c | External Functions | +c %--------------------% +c + Double precision + & ddot, dnrm2, dlamch + external ddot, dnrm2, dlamch +c +c %---------------------% +c | Intrinsic Functions | +c %---------------------% +c + intrinsic min +c +c %-----------------------% +c | Executable Statements | +c %-----------------------% +c + if (ido .eq. 0) then +c +c %-------------------------------% +c | Initialize timing statistics | +c | & message level for debugging | +c %-------------------------------% +c + call igraphsecond (t0) + msglvl = msaup2 +c +c %---------------------------------% +c | Set machine dependent constant. | +c %---------------------------------% +c + eps23 = dlamch('Epsilon-Machine') + eps23 = eps23**(2.0D+0/3.0D+0) +c +c %-------------------------------------% +c | nev0 and np0 are integer variables | +c | hold the initial values of NEV & NP | +c %-------------------------------------% +c + nev0 = nev + np0 = np +c +c %-------------------------------------% +c | kplusp is the bound on the largest | +c | Lanczos factorization built. | +c | nconv is the current number of | +c | "converged" eigenvlues. | +c | iter is the counter on the current | +c | iteration step. | +c %-------------------------------------% +c + kplusp = nev0 + np0 + nconv = 0 + iter = 0 +c +c %--------------------------------------------% +c | Set flags for computing the first NEV steps | +c | of the Lanczos factorization. | +c %--------------------------------------------% +c + getv0 = .true. + update = .false. + ushift = .false. + cnorm = .false. +c + if (info .ne. 0) then +c +c %--------------------------------------------% +c | User provides the initial residual vector. | +c %--------------------------------------------% +c + initv = .true. + info = 0 + else + initv = .false. + end if + end if +c +c %---------------------------------------------% +c | Get a possibly random starting vector and | +c | force it into the range of the operator OP. | +c %---------------------------------------------% +c + 10 continue +c + if (getv0) then + call igraphdgetv0 (ido, bmat, 1, initv, n, 1, v, ldv, resid, + & rnorm, ipntr, workd, info) +c + if (ido .ne. 99) go to 9000 +c + if (rnorm .eq. zero) then +c +c %-----------------------------------------% +c | The initial vector is zero. Error exit. | +c %-----------------------------------------% +c + info = -9 + go to 1200 + end if + getv0 = .false. + ido = 0 + end if +c +c %------------------------------------------------------------% +c | Back from reverse communication: continue with update step | +c %------------------------------------------------------------% +c + if (update) go to 20 +c +c %-------------------------------------------% +c | Back from computing user specified shifts | +c %-------------------------------------------% +c + if (ushift) go to 50 +c +c %-------------------------------------% +c | Back from computing residual norm | +c | at the end of the current iteration | +c %-------------------------------------% +c + if (cnorm) go to 100 +c +c %----------------------------------------------------------% +c | Compute the first NEV steps of the Lanczos factorization | +c %----------------------------------------------------------% +c + call igraphdsaitr (ido, bmat, n, 0, nev0, mode, resid, rnorm, v, + & ldv, h, ldh, ipntr, workd, info) +c +c %---------------------------------------------------% +c | ido .ne. 99 implies use of reverse communication | +c | to compute operations involving OP and possibly B | +c %---------------------------------------------------% +c + if (ido .ne. 99) go to 9000 +c + if (info .gt. 0) then +c +c %-----------------------------------------------------% +c | igraphdsaitr was unable to build an Lanczos factorization | +c | of length NEV0. INFO is returned with the size of | +c | the factorization built. Exit main loop. | +c %-----------------------------------------------------% +c + np = info + mxiter = iter + info = -9999 + go to 1200 + end if +c +c %--------------------------------------------------------------% +c | | +c | M A I N LANCZOS I T E R A T I O N L O O P | +c | Each iteration implicitly restarts the Lanczos | +c | factorization in place. | +c | | +c %--------------------------------------------------------------% +c + 1000 continue +c + iter = iter + 1 +c + if (msglvl .gt. 0) then + call igraphivout (logfil, 1, [iter], ndigit, + & '_saup2: **** Start of major iteration number ****') + end if + if (msglvl .gt. 1) then + call igraphivout (logfil, 1, [nev], ndigit, + & '_saup2: The length of the current Lanczos factorization') + call igraphivout (logfil, 1, [np], ndigit, + & '_saup2: Extend the Lanczos factorization by') + end if +c +c %------------------------------------------------------------% +c | Compute NP additional steps of the Lanczos factorization. | +c %------------------------------------------------------------% +c + ido = 0 + 20 continue + update = .true. +c + call igraphdsaitr (ido, bmat, n, nev, np, mode, resid, rnorm, + & v, ldv, h, ldh, ipntr, workd, info) +c +c %---------------------------------------------------% +c | ido .ne. 99 implies use of reverse communication | +c | to compute operations involving OP and possibly B | +c %---------------------------------------------------% +c + if (ido .ne. 99) go to 9000 +c + if (info .gt. 0) then +c +c %-----------------------------------------------------% +c | igraphdsaitr was unable to build an Lanczos factorization | +c | of length NEV0+NP0. INFO is returned with the size | +c | of the factorization built. Exit main loop. | +c %-----------------------------------------------------% +c + np = info + mxiter = iter + info = -9999 + go to 1200 + end if + update = .false. +c + if (msglvl .gt. 1) then + call igraphdvout (logfil, 1, [rnorm], ndigit, + & '_saup2: Current B-norm of residual for factorization') + end if +c +c %--------------------------------------------------------% +c | Compute the eigenvalues and corresponding error bounds | +c | of the current symmetric tridiagonal matrix. | +c %--------------------------------------------------------% +c + call igraphdseigt (rnorm, kplusp, h, ldh, ritz, bounds, workl, + & ierr) +c + if (ierr .ne. 0) then + info = -8 + go to 1200 + end if +c +c %----------------------------------------------------% +c | Make a copy of eigenvalues and corresponding error | +c | bounds obtained from _seigt. | +c %----------------------------------------------------% +c + call dcopy(kplusp, ritz, 1, workl(kplusp+1), 1) + call dcopy(kplusp, bounds, 1, workl(2*kplusp+1), 1) +c +c %---------------------------------------------------% +c | Select the wanted Ritz values and their bounds | +c | to be used in the convergence test. | +c | The selection is based on the requested number of | +c | eigenvalues instead of the current NEV and NP to | +c | prevent possible misconvergence. | +c | * Wanted Ritz values := RITZ(NP+1:NEV+NP) | +c | * Shifts := RITZ(1:NP) := WORKL(1:NP) | +c %---------------------------------------------------% +c + nev = nev0 + np = np0 + call igraphdsgets (ishift, which, nev, np, ritz, bounds, workl) +c +c %-------------------% +c | Convergence test. | +c %-------------------% +c + call dcopy (nev, bounds(np+1), 1, workl(np+1), 1) + call igraphdsconv (nev, ritz(np+1), workl(np+1), tol, nconv) +c + if (msglvl .gt. 2) then + kp(1) = nev + kp(2) = np + kp(3) = nconv + call igraphivout (logfil, 3, kp, ndigit, + & '_saup2: NEV, NP, NCONV are') + call igraphdvout (logfil, kplusp, ritz, ndigit, + & '_saup2: The eigenvalues of H') + call igraphdvout (logfil, kplusp, bounds, ndigit, + & '_saup2: Ritz estimates of the current NCV Ritz values') + end if +c +c %---------------------------------------------------------% +c | Count the number of unwanted Ritz values that have zero | +c | Ritz estimates. If any Ritz estimates are equal to zero | +c | then a leading block of H of order equal to at least | +c | the number of Ritz values with zero Ritz estimates has | +c | split off. None of these Ritz values may be removed by | +c | shifting. Decrease NP the number of shifts to apply. If | +c | no shifts may be applied, then prepare to exit | +c %---------------------------------------------------------% +c + nptemp = np + do 30 j=1, nptemp + if (bounds(j) .eq. zero) then + np = np - 1 + nev = nev + 1 + end if + 30 continue +c + if ( (nconv .ge. nev0) .or. + & (iter .gt. mxiter) .or. + & (np .eq. 0) ) then +c +c %------------------------------------------------% +c | Prepare to exit. Put the converged Ritz values | +c | and corresponding bounds in RITZ(1:NCONV) and | +c | BOUNDS(1:NCONV) respectively. Then sort. Be | +c | careful when NCONV > NP since we don't want to | +c | swap overlapping locations. | +c %------------------------------------------------% +c + if (which .eq. 'BE') then +c +c %-----------------------------------------------------% +c | Both ends of the spectrum are requested. | +c | Sort the eigenvalues into algebraically decreasing | +c | order first then swap low end of the spectrum next | +c | to high end in appropriate locations. | +c | NOTE: when np < floor(nev/2) be careful not to swap | +c | overlapping locations. | +c %-----------------------------------------------------% +c + wprime = 'SA' + call igraphdsortr (wprime, .true., kplusp, ritz, bounds) + nevd2 = nev / 2 + nevm2 = nev - nevd2 + if ( nev .gt. 1 ) then + call dswap ( min(nevd2,np), ritz(nevm2+1), 1, + & ritz( max(kplusp-nevd2+1,kplusp-np+1) ), 1) + call dswap ( min(nevd2,np), bounds(nevm2+1), 1, + & bounds( max(kplusp-nevd2+1,kplusp-np)+1 ), 1) + end if +c + else +c +c %--------------------------------------------------% +c | LM, SM, LA, SA case. | +c | Sort the eigenvalues of H into the an order that | +c | is opposite to WHICH, and apply the resulting | +c | order to BOUNDS. The eigenvalues are sorted so | +c | that the wanted part are always within the first | +c | NEV locations. | +c %--------------------------------------------------% +c + if (which .eq. 'LM') wprime = 'SM' + if (which .eq. 'SM') wprime = 'LM' + if (which .eq. 'LA') wprime = 'SA' + if (which .eq. 'SA') wprime = 'LA' +c + call igraphdsortr (wprime, .true., kplusp, ritz, bounds) +c + end if +c +c %--------------------------------------------------% +c | Scale the Ritz estimate of each Ritz value | +c | by 1 / max(eps23,magnitude of the Ritz value). | +c %--------------------------------------------------% +c + do 35 j = 1, nev0 + temp = max( eps23, abs(ritz(j)) ) + bounds(j) = bounds(j)/temp + 35 continue +c +c %----------------------------------------------------% +c | Sort the Ritz values according to the scaled Ritz | +c | esitmates. This will push all the converged ones | +c | towards the front of ritzr, ritzi, bounds | +c | (in the case when NCONV < NEV.) | +c %----------------------------------------------------% +c + wprime = 'LA' + call igraphdsortr(wprime, .true., nev0, bounds, ritz) +c +c %----------------------------------------------% +c | Scale the Ritz estimate back to its original | +c | value. | +c %----------------------------------------------% +c + do 40 j = 1, nev0 + temp = max( eps23, abs(ritz(j)) ) + bounds(j) = bounds(j)*temp + 40 continue +c +c %--------------------------------------------------% +c | Sort the "converged" Ritz values again so that | +c | the "threshold" values and their associated Ritz | +c | estimates appear at the appropriate position in | +c | ritz and bound. | +c %--------------------------------------------------% +c + if (which .eq. 'BE') then +c +c %------------------------------------------------% +c | Sort the "converged" Ritz values in increasing | +c | order. The "threshold" values are in the | +c | middle. | +c %------------------------------------------------% +c + wprime = 'LA' + call igraphdsortr(wprime, .true., nconv, ritz, bounds) +c + else +c +c %----------------------------------------------% +c | In LM, SM, LA, SA case, sort the "converged" | +c | Ritz values according to WHICH so that the | +c | "threshold" value appears at the front of | +c | ritz. | +c %----------------------------------------------% + + call igraphdsortr(which, .true., nconv, ritz, bounds) +c + end if +c +c %------------------------------------------% +c | Use h( 1,1 ) as storage to communicate | +c | rnorm to _seupd if needed | +c %------------------------------------------% +c + h(1,1) = rnorm +c + if (msglvl .gt. 1) then + call igraphdvout (logfil, kplusp, ritz, ndigit, + & '_saup2: Sorted Ritz values.') + call igraphdvout (logfil, kplusp, bounds, ndigit, + & '_saup2: Sorted ritz estimates.') + end if +c +c %------------------------------------% +c | Max iterations have been exceeded. | +c %------------------------------------% +c + if (iter .gt. mxiter .and. nconv .lt. nev) info = 1 +c +c %---------------------% +c | No shifts to apply. | +c %---------------------% +c + if (np .eq. 0 .and. nconv .lt. nev0) info = 2 +c + np = nconv + go to 1100 +c + else if (nconv .lt. nev .and. ishift .eq. 1) then +c +c %---------------------------------------------------% +c | Do not have all the requested eigenvalues yet. | +c | To prevent possible stagnation, adjust the number | +c | of Ritz values and the shifts. | +c %---------------------------------------------------% +c + nevbef = nev + nev = nev + min (nconv, np/2) + if (nev .eq. 1 .and. kplusp .ge. 6) then + nev = kplusp / 2 + else if (nev .eq. 1 .and. kplusp .gt. 2) then + nev = 2 + end if + np = kplusp - nev +c +c %---------------------------------------% +c | If the size of NEV was just increased | +c | resort the eigenvalues. | +c %---------------------------------------% +c + if (nevbef .lt. nev) + & call igraphdsgets (ishift, which, nev, np, ritz, bounds, + & workl) +c + end if +c + if (msglvl .gt. 0) then + call igraphivout (logfil, 1, [nconv], ndigit, + & '_saup2: no. of "converged" Ritz values at this iter.') + if (msglvl .gt. 1) then + kp(1) = nev + kp(2) = np + call igraphivout (logfil, 2, kp, ndigit, + & '_saup2: NEV and NP are') + call igraphdvout (logfil, nev, ritz(np+1), ndigit, + & '_saup2: "wanted" Ritz values.') + call igraphdvout (logfil, nev, bounds(np+1), ndigit, + & '_saup2: Ritz estimates of the "wanted" values ') + end if + end if + +c + if (ishift .eq. 0) then +c +c %-----------------------------------------------------% +c | User specified shifts: reverse communication to | +c | compute the shifts. They are returned in the first | +c | NP locations of WORKL. | +c %-----------------------------------------------------% +c + ushift = .true. + ido = 3 + go to 9000 + end if +c + 50 continue +c +c %------------------------------------% +c | Back from reverse communication; | +c | User specified shifts are returned | +c | in WORKL(1:*NP) | +c %------------------------------------% +c + ushift = .false. +c +c +c %---------------------------------------------------------% +c | Move the NP shifts to the first NP locations of RITZ to | +c | free up WORKL. This is for the non-exact shift case; | +c | in the exact shift case, igraphdsgets already handles this. | +c %---------------------------------------------------------% +c + if (ishift .eq. 0) call dcopy (np, workl, 1, ritz, 1) +c + if (msglvl .gt. 2) then + call igraphivout (logfil, 1, [np], ndigit, + & '_saup2: The number of shifts to apply ') + call igraphdvout (logfil, np, workl, ndigit, + & '_saup2: shifts selected') + if (ishift .eq. 1) then + call igraphdvout (logfil, np, bounds, ndigit, + & '_saup2: corresponding Ritz estimates') + end if + end if +c +c %---------------------------------------------------------% +c | Apply the NP0 implicit shifts by QR bulge chasing. | +c | Each shift is applied to the entire tridiagonal matrix. | +c | The first 2*N locations of WORKD are used as workspace. | +c | After igraphdsapps is done, we have a Lanczos | +c | factorization of length NEV. | +c %---------------------------------------------------------% +c + call igraphdsapps (n, nev, np, ritz, v, ldv, h, ldh, resid, + & q, ldq, workd) +c +c %---------------------------------------------% +c | Compute the B-norm of the updated residual. | +c | Keep B*RESID in WORKD(1:N) to be used in | +c | the first step of the next call to igraphdsaitr. | +c %---------------------------------------------% +c + cnorm = .true. + call igraphsecond (t2) + if (bmat .eq. 'G') then + nbx = nbx + 1 + call dcopy (n, resid, 1, workd(n+1), 1) + ipntr(1) = n + 1 + ipntr(2) = 1 + ido = 2 +c +c %----------------------------------% +c | Exit in order to compute B*RESID | +c %----------------------------------% +c + go to 9000 + else if (bmat .eq. 'I') then + call dcopy (n, resid, 1, workd, 1) + end if +c + 100 continue +c +c %----------------------------------% +c | Back from reverse communication; | +c | WORKD(1:N) := B*RESID | +c %----------------------------------% +c + if (bmat .eq. 'G') then + call igraphsecond (t3) + tmvbx = tmvbx + (t3 - t2) + end if +c + if (bmat .eq. 'G') then + rnorm = ddot (n, resid, 1, workd, 1) + rnorm = sqrt(abs(rnorm)) + else if (bmat .eq. 'I') then + rnorm = dnrm2(n, resid, 1) + end if + cnorm = .false. + 130 continue +c + if (msglvl .gt. 2) then + call igraphdvout (logfil, 1, [rnorm], ndigit, + & '_saup2: B-norm of residual for NEV factorization') + call igraphdvout (logfil, nev, h(1,2), ndigit, + & '_saup2: main diagonal of compressed H matrix') + call igraphdvout (logfil, nev-1, h(2,1), ndigit, + & '_saup2: subdiagonal of compressed H matrix') + end if +c + go to 1000 +c +c %---------------------------------------------------------------% +c | | +c | E N D O F M A I N I T E R A T I O N L O O P | +c | | +c %---------------------------------------------------------------% +c + 1100 continue +c + mxiter = iter + nev = nconv +c + 1200 continue + ido = 99 +c +c %------------% +c | Error exit | +c %------------% +c + call igraphsecond (t1) + tsaup2 = t1 - t0 +c + 9000 continue + return +c +c %---------------% +c | End of igraphdsaup2 | +c %---------------% +c + end diff --git a/src/rigraph/vendor/arpack/dsaupd.f b/src/rigraph/vendor/arpack/dsaupd.f new file mode 100644 index 0000000..7e85781 --- /dev/null +++ b/src/rigraph/vendor/arpack/dsaupd.f @@ -0,0 +1,653 @@ +c----------------------------------------------------------------------- +c\BeginDoc +c +c\Name: igraphdsaupd +c +c\Description: +c +c Reverse communication interface for the Implicitly Restarted Arnoldi +c Iteration. For symmetric problems this reduces to a variant of the Lanczos +c method. This method has been designed to compute approximations to a +c few eigenpairs of a linear operator OP that is real and symmetric +c with respect to a real positive semi-definite symmetric matrix B, +c i.e. +c +c B*OP = (OP')*B. +c +c Another way to express this condition is +c +c < x,OPy > = < OPx,y > where < z,w > = z'Bw . +c +c In the standard eigenproblem B is the identity matrix. +c ( A' denotes transpose of A) +c +c The computed approximate eigenvalues are called Ritz values and +c the corresponding approximate eigenvectors are called Ritz vectors. +c +c igraphdsaupd is usually called iteratively to solve one of the +c following problems: +c +c Mode 1: A*x = lambda*x, A symmetric +c ===> OP = A and B = I. +c +c Mode 2: A*x = lambda*M*x, A symmetric, M symmetric positive definite +c ===> OP = inv[M]*A and B = M. +c ===> (If M can be factored see remark 3 below) +c +c Mode 3: K*x = lambda*M*x, K symmetric, M symmetric positive semi-definite +c ===> OP = (inv[K - sigma*M])*M and B = M. +c ===> Shift-and-Invert mode +c +c Mode 4: K*x = lambda*KG*x, K symmetric positive semi-definite, +c KG symmetric indefinite +c ===> OP = (inv[K - sigma*KG])*K and B = K. +c ===> Buckling mode +c +c Mode 5: A*x = lambda*M*x, A symmetric, M symmetric positive semi-definite +c ===> OP = inv[A - sigma*M]*[A + sigma*M] and B = M. +c ===> Cayley transformed mode +c +c NOTE: The action of w <- inv[A - sigma*M]*v or w <- inv[M]*v +c should be accomplished either by a direct method +c using a sparse matrix factorization and solving +c +c [A - sigma*M]*w = v or M*w = v, +c +c or through an iterative method for solving these +c systems. If an iterative method is used, the +c convergence test must be more stringent than +c the accuracy requirements for the eigenvalue +c approximations. +c +c\Usage: +c call igraphdsaupd +c ( IDO, BMAT, N, WHICH, NEV, TOL, RESID, NCV, V, LDV, IPARAM, +c IPNTR, WORKD, WORKL, LWORKL, INFO ) +c +c\Arguments +c IDO Integer. (INPUT/OUTPUT) +c Reverse communication flag. IDO must be zero on the first +c call to igraphdsaupd. IDO will be set internally to +c indicate the type of operation to be performed. Control is +c then given back to the calling routine which has the +c responsibility to carry out the requested operation and call +c igraphdsaupd with the result. The operand is given in +c WORKD(IPNTR(1)), the result must be put in WORKD(IPNTR(2)). +c (If Mode = 2 see remark 5 below) +c ------------------------------------------------------------- +c IDO = 0: first call to the reverse communication interface +c IDO = -1: compute Y = OP * X where +c IPNTR(1) is the pointer into WORKD for X, +c IPNTR(2) is the pointer into WORKD for Y. +c This is for the initialization phase to force the +c starting vector into the range of OP. +c IDO = 1: compute Y = OP * X where +c IPNTR(1) is the pointer into WORKD for X, +c IPNTR(2) is the pointer into WORKD for Y. +c In mode 3,4 and 5, the vector B * X is already +c available in WORKD(ipntr(3)). It does not +c need to be recomputed in forming OP * X. +c IDO = 2: compute Y = B * X where +c IPNTR(1) is the pointer into WORKD for X, +c IPNTR(2) is the pointer into WORKD for Y. +c IDO = 3: compute the IPARAM(8) shifts where +c IPNTR(11) is the pointer into WORKL for +c placing the shifts. See remark 6 below. +c IDO = 99: done +c ------------------------------------------------------------- +c +c BMAT Character*1. (INPUT) +c BMAT specifies the type of the matrix B that defines the +c semi-inner product for the operator OP. +c B = 'I' -> standard eigenvalue problem A*x = lambda*x +c B = 'G' -> generalized eigenvalue problem A*x = lambda*B*x +c +c N Integer. (INPUT) +c Dimension of the eigenproblem. +c +c WHICH Character*2. (INPUT) +c Specify which of the Ritz values of OP to compute. +c +c 'LA' - compute the NEV largest (algebraic) eigenvalues. +c 'SA' - compute the NEV smallest (algebraic) eigenvalues. +c 'LM' - compute the NEV largest (in magnitude) eigenvalues. +c 'SM' - compute the NEV smallest (in magnitude) eigenvalues. +c 'BE' - compute NEV eigenvalues, half from each end of the +c spectrum. When NEV is odd, compute one more from the +c high end than from the low end. +c (see remark 1 below) +c +c NEV Integer. (INPUT) +c Number of eigenvalues of OP to be computed. 0 < NEV < N. +c +c TOL Double precision scalar. (INPUT) +c Stopping criterion: the relative accuracy of the Ritz value +c is considered acceptable if BOUNDS(I) .LE. TOL*ABS(RITZ(I)). +c If TOL .LE. 0. is passed a default is set: +c DEFAULT = DLAMCH('EPS') (machine precision as computed +c by the LAPACK auxiliary subroutine DLAMCH). +c +c RESID Double precision array of length N. (INPUT/OUTPUT) +c On INPUT: +c If INFO .EQ. 0, a random initial residual vector is used. +c If INFO .NE. 0, RESID contains the initial residual vector, +c possibly from a previous run. +c On OUTPUT: +c RESID contains the final residual vector. +c +c NCV Integer. (INPUT) +c Number of columns of the matrix V (less than or equal to N). +c This will indicate how many Lanczos vectors are generated +c at each iteration. After the startup phase in which NEV +c Lanczos vectors are generated, the algorithm generates +c NCV-NEV Lanczos vectors at each subsequent update iteration. +c Most of the cost in generating each Lanczos vector is in the +c matrix-vector product OP*x. (See remark 4 below). +c +c V Double precision N by NCV array. (OUTPUT) +c The NCV columns of V contain the Lanczos basis vectors. +c +c LDV Integer. (INPUT) +c Leading dimension of V exactly as declared in the calling +c program. +c +c IPARAM Integer array of length 11. (INPUT/OUTPUT) +c IPARAM(1) = ISHIFT: method for selecting the implicit shifts. +c The shifts selected at each iteration are used to restart +c the Arnoldi iteration in an implicit fashion. +c ------------------------------------------------------------- +c ISHIFT = 0: the shifts are provided by the user via +c reverse communication. The NCV eigenvalues of +c the current tridiagonal matrix T are returned in +c the part of WORKL array corresponding to RITZ. +c See remark 6 below. +c ISHIFT = 1: exact shifts with respect to the reduced +c tridiagonal matrix T. This is equivalent to +c restarting the iteration with a starting vector +c that is a linear combination of Ritz vectors +c associated with the "wanted" Ritz values. +c ------------------------------------------------------------- +c +c IPARAM(2) = LEVEC +c No longer referenced. See remark 2 below. +c +c IPARAM(3) = MXITER +c On INPUT: maximum number of Arnoldi update iterations allowed. +c On OUTPUT: actual number of Arnoldi update iterations taken. +c +c IPARAM(4) = NB: blocksize to be used in the recurrence. +c The code currently works only for NB = 1. +c +c IPARAM(5) = NCONV: number of "converged" Ritz values. +c This represents the number of Ritz values that satisfy +c the convergence criterion. +c +c IPARAM(6) = IUPD +c No longer referenced. Implicit restarting is ALWAYS used. +c +c IPARAM(7) = MODE +c On INPUT determines what type of eigenproblem is being solved. +c Must be 1,2,3,4,5; See under \Description of igraphdsaupd for the +c five modes available. +c +c IPARAM(8) = NP +c When ido = 3 and the user provides shifts through reverse +c communication (IPARAM(1)=0), igraphdsaupd returns NP, the number +c of shifts the user is to provide. 0 < NP <=NCV-NEV. See Remark +c 6 below. +c +c IPARAM(9) = NUMOP, IPARAM(10) = NUMOPB, IPARAM(11) = NUMREO, +c OUTPUT: NUMOP = total number of OP*x operations, +c NUMOPB = total number of B*x operations if BMAT='G', +c NUMREO = total number of steps of re-orthogonalization. +c +c IPNTR Integer array of length 11. (OUTPUT) +c Pointer to mark the starting locations in the WORKD and WORKL +c arrays for matrices/vectors used by the Lanczos iteration. +c ------------------------------------------------------------- +c IPNTR(1): pointer to the current operand vector X in WORKD. +c IPNTR(2): pointer to the current result vector Y in WORKD. +c IPNTR(3): pointer to the vector B * X in WORKD when used in +c the shift-and-invert mode. +c IPNTR(4): pointer to the next available location in WORKL +c that is untouched by the program. +c IPNTR(5): pointer to the NCV by 2 tridiagonal matrix T in WORKL. +c IPNTR(6): pointer to the NCV RITZ values array in WORKL. +c IPNTR(7): pointer to the Ritz estimates in array WORKL associated +c with the Ritz values located in RITZ in WORKL. +c IPNTR(11): pointer to the NP shifts in WORKL. See Remark 6 below. +c +c Note: IPNTR(8:10) is only referenced by igraphdseupd. See Remark 2. +c IPNTR(8): pointer to the NCV RITZ values of the original system. +c IPNTR(9): pointer to the NCV corresponding error bounds. +c IPNTR(10): pointer to the NCV by NCV matrix of eigenvectors +c of the tridiagonal matrix T. Only referenced by +c igraphdseupd if RVEC = .TRUE. See Remarks. +c ------------------------------------------------------------- +c +c WORKD Double precision work array of length 3*N. (REVERSE COMMUNICATION) +c Distributed array to be used in the basic Arnoldi iteration +c for reverse communication. The user should not use WORKD +c as temporary workspace during the iteration. Upon termination +c WORKD(1:N) contains B*RESID(1:N). If the Ritz vectors are desired +c subroutine igraphdseupd uses this output. +c See Data Distribution Note below. +c +c WORKL Double precision work array of length LWORKL. (OUTPUT/WORKSPACE) +c Private (replicated) array on each PE or array allocated on +c the front end. See Data Distribution Note below. +c +c LWORKL Integer. (INPUT) +c LWORKL must be at least NCV**2 + 8*NCV . +c +c INFO Integer. (INPUT/OUTPUT) +c If INFO .EQ. 0, a randomly initial residual vector is used. +c If INFO .NE. 0, RESID contains the initial residual vector, +c possibly from a previous run. +c Error flag on output. +c = 0: Normal exit. +c = 1: Maximum number of iterations taken. +c All possible eigenvalues of OP has been found. IPARAM(5) +c returns the number of wanted converged Ritz values. +c = 2: No longer an informational error. Deprecated starting +c with release 2 of ARPACK. +c = 3: No shifts could be applied during a cycle of the +c Implicitly restarted Arnoldi iteration. One possibility +c is to increase the size of NCV relative to NEV. +c See remark 4 below. +c = -1: N must be positive. +c = -2: NEV must be positive. +c = -3: NCV must be greater than NEV and less than or equal to N. +c = -4: The maximum number of Arnoldi update iterations allowed +c must be greater than zero. +c = -5: WHICH must be one of 'LM', 'SM', 'LA', 'SA' or 'BE'. +c = -6: BMAT must be one of 'I' or 'G'. +c = -7: Length of private work array WORKL is not sufficient. +c = -8: Error return from trid. eigenvalue calculation; +c Informatinal error from LAPACK routine dsteqr. +c = -9: Starting vector is zero. +c = -10: IPARAM(7) must be 1,2,3,4,5. +c = -11: IPARAM(7) = 1 and BMAT = 'G' are incompatable. +c = -12: IPARAM(1) must be equal to 0 or 1. +c = -13: NEV and WHICH = 'BE' are incompatable. +c = -9999: Could not build an Arnoldi factorization. +c IPARAM(5) returns the size of the current Arnoldi +c factorization. The user is advised to check that +c enough workspace and array storage has been allocated. +c +c +c\Remarks +c 1. The converged Ritz values are always returned in ascending +c algebraic order. The computed Ritz values are approximate +c eigenvalues of OP. The selection of WHICH should be made +c with this in mind when Mode = 3,4,5. After convergence, +c approximate eigenvalues of the original problem may be obtained +c with the ARPACK subroutine igraphdseupd. +c +c 2. If the Ritz vectors corresponding to the converged Ritz values +c are needed, the user must call igraphdseupd immediately following completion +c of igraphdsaupd. This is new starting with version 2.1 of ARPACK. +c +c 3. If M can be factored into a Cholesky factorization M = LL' +c then Mode = 2 should not be selected. Instead one should use +c Mode = 1 with OP = inv(L)*A*inv(L'). Appropriate triangular +c linear systems should be solved with L and L' rather +c than computing inverses. After convergence, an approximate +c eigenvector z of the original problem is recovered by solving +c L'z = x where x is a Ritz vector of OP. +c +c 4. At present there is no a-priori analysis to guide the selection +c of NCV relative to NEV. The only formal requrement is that NCV > NEV. +c However, it is recommended that NCV .ge. 2*NEV. If many problems of +c the same type are to be solved, one should experiment with increasing +c NCV while keeping NEV fixed for a given test problem. This will +c usually decrease the required number of OP*x operations but it +c also increases the work and storage required to maintain the orthogonal +c basis vectors. The optimal "cross-over" with respect to CPU time +c is problem dependent and must be determined empirically. +c +c 5. If IPARAM(7) = 2 then in the Reverse commuication interface the user +c must do the following. When IDO = 1, Y = OP * X is to be computed. +c When IPARAM(7) = 2 OP = inv(B)*A. After computing A*X the user +c must overwrite X with A*X. Y is then the solution to the linear set +c of equations B*Y = A*X. +c +c 6. When IPARAM(1) = 0, and IDO = 3, the user needs to provide the +c NP = IPARAM(8) shifts in locations: +c 1 WORKL(IPNTR(11)) +c 2 WORKL(IPNTR(11)+1) +c . +c . +c . +c NP WORKL(IPNTR(11)+NP-1). +c +c The eigenvalues of the current tridiagonal matrix are located in +c WORKL(IPNTR(6)) through WORKL(IPNTR(6)+NCV-1). They are in the +c order defined by WHICH. The associated Ritz estimates are located in +c WORKL(IPNTR(8)), WORKL(IPNTR(8)+1), ... , WORKL(IPNTR(8)+NCV-1). +c +c----------------------------------------------------------------------- +c +c\Data Distribution Note: +c +c Fortran-D syntax: +c ================ +c REAL RESID(N), V(LDV,NCV), WORKD(3*N), WORKL(LWORKL) +c DECOMPOSE D1(N), D2(N,NCV) +c ALIGN RESID(I) with D1(I) +c ALIGN V(I,J) with D2(I,J) +c ALIGN WORKD(I) with D1(I) range (1:N) +c ALIGN WORKD(I) with D1(I-N) range (N+1:2*N) +c ALIGN WORKD(I) with D1(I-2*N) range (2*N+1:3*N) +c DISTRIBUTE D1(BLOCK), D2(BLOCK,:) +c REPLICATED WORKL(LWORKL) +c +c Cray MPP syntax: +c =============== +c REAL RESID(N), V(LDV,NCV), WORKD(N,3), WORKL(LWORKL) +c SHARED RESID(BLOCK), V(BLOCK,:), WORKD(BLOCK,:) +c REPLICATED WORKL(LWORKL) +c +c +c\BeginLib +c +c\References: +c 1. D.C. Sorensen, "Implicit Application of Polynomial Filters in +c a k-Step Arnoldi Method", SIAM J. Matr. Anal. Apps., 13 (1992), +c pp 357-385. +c 2. R.B. Lehoucq, "Analysis and Implementation of an Implicitly +c Restarted Arnoldi Iteration", Rice University Technical Report +c TR95-13, Department of Computational and Applied Mathematics. +c 3. B.N. Parlett, "The Symmetric Eigenvalue Problem". Prentice-Hall, +c 1980. +c 4. B.N. Parlett, B. Nour-Omid, "Towards a Black Box Lanczos Program", +c Computer Physics Communications, 53 (1989), pp 169-179. +c 5. B. Nour-Omid, B.N. Parlett, T. Ericson, P.S. Jensen, "How to +c Implement the Spectral Transformation", Math. Comp., 48 (1987), +c pp 663-673. +c 6. R.G. Grimes, J.G. Lewis and H.D. Simon, "A Shifted Block Lanczos +c Algorithm for Solving Sparse Symmetric Generalized Eigenproblems", +c SIAM J. Matr. Anal. Apps., January (1993). +c 7. L. Reichel, W.B. Gragg, "Algorithm 686: FORTRAN Subroutines +c for Updating the QR decomposition", ACM TOMS, December 1990, +c Volume 16 Number 4, pp 369-377. +c 8. R.B. Lehoucq, D.C. Sorensen, "Implementation of Some Spectral +c Transformations in a k-Step Arnoldi Method". In Preparation. +c +c\Routines called: +c igraphdsaup2 ARPACK routine that implements the Implicitly Restarted +c Arnoldi Iteration. +c igraphdstats ARPACK routine that initialize timing and other statistics +c variables. +c igraphivout ARPACK utility routine that prints integers. +c igraphsecond ARPACK utility routine for timing. +c igraphdvout ARPACK utility routine that prints vectors. +c dlamch LAPACK routine that determines machine constants. +c +c\Authors +c Danny Sorensen Phuong Vu +c Richard Lehoucq CRPC / Rice University +c Dept. of Computational & Houston, Texas +c Applied Mathematics +c Rice University +c Houston, Texas +c +c\Revision history: +c 12/15/93: Version ' 2.4' +c +c\SCCS Information: @(#) +c FILE: saupd.F SID: 2.7 DATE OF SID: 8/27/96 RELEASE: 2 +c +c\Remarks +c 1. None +c +c\EndLib +c +c----------------------------------------------------------------------- +c + subroutine igraphdsaupd + & ( ido, bmat, n, which, nev, tol, resid, ncv, v, ldv, iparam, + & ipntr, workd, workl, lworkl, info ) +c +c %----------------------------------------------------% +c | Include files for debugging and timing information | +c %----------------------------------------------------% +c + include 'debug.h' + include 'stat.h' +c +c %------------------% +c | Scalar Arguments | +c %------------------% +c + character bmat*1, which*2 + integer ido, info, ldv, lworkl, n, ncv, nev + Double precision + & tol +c +c %-----------------% +c | Array Arguments | +c %-----------------% +c + integer iparam(11), ipntr(11) + Double precision + & resid(n), v(ldv,ncv), workd(3*n), workl(lworkl) +c +c %------------% +c | Parameters | +c %------------% +c + Double precision + & one, zero + parameter (one = 1.0D+0, zero = 0.0D+0) +c +c %---------------% +c | Local Scalars | +c %---------------% +c + integer bounds, ierr, ih, iq, ishift, iupd, iw, + & ldh, ldq, msglvl, mxiter, mode, nb, + & nev0, next, np, ritz, j + save bounds, ierr, ih, iq, ishift, iupd, iw, + & ldh, ldq, msglvl, mxiter, mode, nb, + & nev0, next, np, ritz +c +c %----------------------% +c | External Subroutines | +c %----------------------% +c + external igraphdsaup2, igraphdvout, igraphivout, + & igraphsecond, igraphdstats +c +c %--------------------% +c | External Functions | +c %--------------------% +c + Double precision + & dlamch + external dlamch +c +c %-----------------------% +c | Executable Statements | +c %-----------------------% +c + if (ido .eq. 0) then +c +c %-------------------------------% +c | Initialize timing statistics | +c | & message level for debugging | +c %-------------------------------% +c + call igraphdstats + call igraphsecond (t0) + msglvl = msaupd +c + ierr = 0 + ishift = iparam(1) + mxiter = iparam(3) + nb = iparam(4) +c +c %--------------------------------------------% +c | Revision 2 performs only implicit restart. | +c %--------------------------------------------% +c + iupd = 1 + mode = iparam(7) +c +c %----------------% +c | Error checking | +c %----------------% +c + if (n .le. 0) then + ierr = -1 + else if (nev .le. 0) then + ierr = -2 + else if (ncv .le. nev .or. ncv .gt. n) then + ierr = -3 + end if +c +c %----------------------------------------------% +c | NP is the number of additional steps to | +c | extend the length NEV Lanczos factorization. | +c %----------------------------------------------% +c + np = ncv - nev +c + if (mxiter .le. 0) ierr = -4 + if (which .ne. 'LM' .and. + & which .ne. 'SM' .and. + & which .ne. 'LA' .and. + & which .ne. 'SA' .and. + & which .ne. 'BE') ierr = -5 + if (bmat .ne. 'I' .and. bmat .ne. 'G') ierr = -6 +c + if (lworkl .lt. ncv**2 + 8*ncv) ierr = -7 + if (mode .lt. 1 .or. mode .gt. 5) then + ierr = -10 + else if (mode .eq. 1 .and. bmat .eq. 'G') then + ierr = -11 + else if (ishift .lt. 0 .or. ishift .gt. 1) then + ierr = -12 + else if (nev .eq. 1 .and. which .eq. 'BE') then + ierr = -13 + end if +c +c %------------% +c | Error Exit | +c %------------% +c + if (ierr .ne. 0) then + info = ierr + ido = 99 + go to 9000 + end if +c +c %------------------------% +c | Set default parameters | +c %------------------------% +c + if (nb .le. 0) nb = 1 + if (tol .le. zero) tol = dlamch('EpsMach') +c +c %----------------------------------------------% +c | NP is the number of additional steps to | +c | extend the length NEV Lanczos factorization. | +c | NEV0 is the local variable designating the | +c | size of the invariant subspace desired. | +c %----------------------------------------------% +c + np = ncv - nev + nev0 = nev +c +c %-----------------------------% +c | Zero out internal workspace | +c %-----------------------------% +c + do 10 j = 1, ncv**2 + 8*ncv + workl(j) = zero + 10 continue +c +c %-------------------------------------------------------% +c | Pointer into WORKL for address of H, RITZ, BOUNDS, Q | +c | etc... and the remaining workspace. | +c | Also update pointer to be used on output. | +c | Memory is laid out as follows: | +c | workl(1:2*ncv) := generated tridiagonal matrix | +c | workl(2*ncv+1:2*ncv+ncv) := ritz values | +c | workl(3*ncv+1:3*ncv+ncv) := computed error bounds | +c | workl(4*ncv+1:4*ncv+ncv*ncv) := rotation matrix Q | +c | workl(4*ncv+ncv*ncv+1:7*ncv+ncv*ncv) := workspace | +c %-------------------------------------------------------% +c + ldh = ncv + ldq = ncv + ih = 1 + ritz = ih + 2*ldh + bounds = ritz + ncv + iq = bounds + ncv + iw = iq + ncv**2 + next = iw + 3*ncv +c + ipntr(4) = next + ipntr(5) = ih + ipntr(6) = ritz + ipntr(7) = bounds + ipntr(11) = iw + end if +c +c %-------------------------------------------------------% +c | Carry out the Implicitly restarted Lanczos Iteration. | +c %-------------------------------------------------------% +c + call igraphdsaup2 + & ( ido, bmat, n, which, nev0, np, tol, resid, mode, iupd, + & ishift, mxiter, v, ldv, workl(ih), ldh, workl(ritz), + & workl(bounds), workl(iq), ldq, workl(iw), ipntr, workd, + & info ) +c +c %--------------------------------------------------% +c | ido .ne. 99 implies use of reverse communication | +c | to compute operations involving OP or shifts. | +c %--------------------------------------------------% +c + if (ido .eq. 3) iparam(8) = np + if (ido .ne. 99) go to 9000 +c + iparam(3) = mxiter + iparam(5) = np + iparam(9) = nopx + iparam(10) = nbx + iparam(11) = nrorth +c +c %------------------------------------% +c | Exit if there was an informational | +c | error within igraphdsaup2. | +c %------------------------------------% +c + if (info .lt. 0) go to 9000 + if (info .eq. 2) info = 3 +c + if (msglvl .gt. 0) then + call igraphivout (logfil, 1, mxiter, ndigit, + & '_saupd: number of update iterations taken') + call igraphivout (logfil, 1, np, ndigit, + & '_saupd: number of "converged" Ritz values') + call igraphdvout (logfil, np, workl(Ritz), ndigit, + & '_saupd: final Ritz values') + call igraphdvout (logfil, np, workl(Bounds), ndigit, + & '_saupd: corresponding error bounds') + end if +c + call igraphsecond (t1) + tsaupd = t1 - t0 +c +c + 9000 continue +c + return +c +c %---------------% +c | End of igraphdsaupd | +c %---------------% +c + end diff --git a/src/rigraph/vendor/arpack/dsconv.f b/src/rigraph/vendor/arpack/dsconv.f new file mode 100644 index 0000000..d8bac2e --- /dev/null +++ b/src/rigraph/vendor/arpack/dsconv.f @@ -0,0 +1,138 @@ +c----------------------------------------------------------------------- +c\BeginDoc +c +c\Name: igraphdsconv +c +c\Description: +c Convergence testing for the symmetric Arnoldi eigenvalue routine. +c +c\Usage: +c call igraphdsconv +c ( N, RITZ, BOUNDS, TOL, NCONV ) +c +c\Arguments +c N Integer. (INPUT) +c Number of Ritz values to check for convergence. +c +c RITZ Double precision array of length N. (INPUT) +c The Ritz values to be checked for convergence. +c +c BOUNDS Double precision array of length N. (INPUT) +c Ritz estimates associated with the Ritz values in RITZ. +c +c TOL Double precision scalar. (INPUT) +c Desired relative accuracy for a Ritz value to be considered +c "converged". +c +c NCONV Integer scalar. (OUTPUT) +c Number of "converged" Ritz values. +c +c\EndDoc +c +c----------------------------------------------------------------------- +c +c\BeginLib +c +c\Routines called: +c igraphsecond ARPACK utility routine for timing. +c dlamch LAPACK routine that determines machine constants. +c +c\Author +c Danny Sorensen Phuong Vu +c Richard Lehoucq CRPC / Rice University +c Dept. of Computational & Houston, Texas +c Applied Mathematics +c Rice University +c Houston, Texas +c +c\SCCS Information: @(#) +c FILE: sconv.F SID: 2.4 DATE OF SID: 4/19/96 RELEASE: 2 +c +c\Remarks +c 1. Starting with version 2.4, this routine no longer uses the +c Parlett strategy using the gap conditions. +c +c\EndLib +c +c----------------------------------------------------------------------- +c + subroutine igraphdsconv (n, ritz, bounds, tol, nconv) +c +c %----------------------------------------------------% +c | Include files for debugging and timing information | +c %----------------------------------------------------% +c + include 'debug.h' + include 'stat.h' +c +c %------------------% +c | Scalar Arguments | +c %------------------% +c + integer n, nconv + Double precision + & tol +c +c %-----------------% +c | Array Arguments | +c %-----------------% +c + Double precision + & ritz(n), bounds(n) +c +c %---------------% +c | Local Scalars | +c %---------------% +c + integer i + Double precision + & temp, eps23 +c +c %-------------------% +c | External routines | +c %-------------------% +c + Double precision + & dlamch + external dlamch + +c %---------------------% +c | Intrinsic Functions | +c %---------------------% +c + intrinsic abs +c +c %-----------------------% +c | Executable Statements | +c %-----------------------% +c + call igraphsecond (t0) +c + eps23 = dlamch('Epsilon-Machine') + eps23 = eps23**(2.0D+0 / 3.0D+0) +c + nconv = 0 + do 10 i = 1, n +c +c %-----------------------------------------------------% +c | The i-th Ritz value is considered "converged" | +c | when: bounds(i) .le. TOL*max(eps23, abs(ritz(i))) | +c %-----------------------------------------------------% +c + temp = max( eps23, abs(ritz(i)) ) + if ( bounds(i) .le. tol*temp ) then + nconv = nconv + 1 + end if +c + 10 continue +c + call igraphsecond (t1) + tsconv = tsconv + (t1 - t0) +c + return +c +c %---------------% +c | End of igraphdsconv | +c %---------------% +c + end diff --git a/src/rigraph/vendor/arpack/dseigt.f b/src/rigraph/vendor/arpack/dseigt.f new file mode 100644 index 0000000..dc5dccd --- /dev/null +++ b/src/rigraph/vendor/arpack/dseigt.f @@ -0,0 +1,181 @@ +c----------------------------------------------------------------------- +c\BeginDoc +c +c\Name: igraphdseigt +c +c\Description: +c Compute the eigenvalues of the current symmetric tridiagonal matrix +c and the corresponding error bounds given the current residual norm. +c +c\Usage: +c call igraphdseigt +c ( RNORM, N, H, LDH, EIG, BOUNDS, WORKL, IERR ) +c +c\Arguments +c RNORM Double precision scalar. (INPUT) +c RNORM contains the residual norm corresponding to the current +c symmetric tridiagonal matrix H. +c +c N Integer. (INPUT) +c Size of the symmetric tridiagonal matrix H. +c +c H Double precision N by 2 array. (INPUT) +c H contains the symmetric tridiagonal matrix with the +c subdiagonal in the first column starting at H(2,1) and the +c main diagonal in igraphsecond column. +c +c LDH Integer. (INPUT) +c Leading dimension of H exactly as declared in the calling +c program. +c +c EIG Double precision array of length N. (OUTPUT) +c On output, EIG contains the N eigenvalues of H possibly +c unsorted. The BOUNDS arrays are returned in the +c same sorted order as EIG. +c +c BOUNDS Double precision array of length N. (OUTPUT) +c On output, BOUNDS contains the error estimates corresponding +c to the eigenvalues EIG. This is equal to RNORM times the +c last components of the eigenvectors corresponding to the +c eigenvalues in EIG. +c +c WORKL Double precision work array of length 3*N. (WORKSPACE) +c Private (replicated) array on each PE or array allocated on +c the front end. +c +c IERR Integer. (OUTPUT) +c Error exit flag from igraphdstqrb. +c +c\EndDoc +c +c----------------------------------------------------------------------- +c +c\BeginLib +c +c\Local variables: +c xxxxxx real +c +c\Routines called: +c igraphdstqrb ARPACK routine that computes the eigenvalues and the +c last components of the eigenvectors of a symmetric +c and tridiagonal matrix. +c igraphsecond ARPACK utility routine for timing. +c igraphdvout ARPACK utility routine that prints vectors. +c dcopy Level 1 BLAS that copies one vector to another. +c +c\Author +c Danny Sorensen Phuong Vu +c Richard Lehoucq CRPC / Rice University +c Dept. of Computational & Houston, Texas +c Applied Mathematics +c Rice University +c Houston, Texas +c +c\Revision history: +c xx/xx/92: Version ' 2.4' +c +c\SCCS Information: @(#) +c FILE: seigt.F SID: 2.4 DATE OF SID: 8/27/96 RELEASE: 2 +c +c\Remarks +c None +c +c\EndLib +c +c----------------------------------------------------------------------- +c + subroutine igraphdseigt + & ( rnorm, n, h, ldh, eig, bounds, workl, ierr ) +c +c %----------------------------------------------------% +c | Include files for debugging and timing information | +c %----------------------------------------------------% +c + include 'debug.h' + include 'stat.h' +c +c %------------------% +c | Scalar Arguments | +c %------------------% +c + integer ierr, ldh, n + Double precision + & rnorm +c +c %-----------------% +c | Array Arguments | +c %-----------------% +c + Double precision + & eig(n), bounds(n), h(ldh,2), workl(3*n) +c +c %------------% +c | Parameters | +c %------------% +c + Double precision + & zero + parameter (zero = 0.0D+0) +c +c %---------------% +c | Local Scalars | +c %---------------% +c + integer i, k, msglvl +c +c %----------------------% +c | External Subroutines | +c %----------------------% +c + external dcopy, igraphdstqrb, igraphdvout, igraphsecond +c +c %-----------------------% +c | Executable Statements | +c %-----------------------% +c +c %-------------------------------% +c | Initialize timing statistics | +c | & message level for debugging | +c %-------------------------------% +c + call igraphsecond (t0) + msglvl = mseigt +c + if (msglvl .gt. 0) then + call igraphdvout (logfil, n, h(1,2), ndigit, + & '_seigt: main diagonal of matrix H') + if (n .gt. 1) then + call igraphdvout (logfil, n-1, h(2,1), ndigit, + & '_seigt: sub diagonal of matrix H') + end if + end if +c + call dcopy (n, h(1,2), 1, eig, 1) + call dcopy (n-1, h(2,1), 1, workl, 1) + call igraphdstqrb (n, eig, workl, bounds, workl(n+1), ierr) + if (ierr .ne. 0) go to 9000 + if (msglvl .gt. 1) then + call igraphdvout (logfil, n, bounds, ndigit, + & '_seigt: last row of the eigenvector matrix for H') + end if +c +c %-----------------------------------------------% +c | Finally determine the error bounds associated | +c | with the n Ritz values of H. | +c %-----------------------------------------------% +c + do 30 k = 1, n + bounds(k) = rnorm*abs(bounds(k)) + 30 continue +c + call igraphsecond (t1) + tseigt = tseigt + (t1 - t0) +c + 9000 continue + return +c +c %---------------% +c | End of igraphdseigt | +c %---------------% +c + end diff --git a/src/rigraph/vendor/arpack/dsesrt.f b/src/rigraph/vendor/arpack/dsesrt.f new file mode 100644 index 0000000..05e2c36 --- /dev/null +++ b/src/rigraph/vendor/arpack/dsesrt.f @@ -0,0 +1,217 @@ +c----------------------------------------------------------------------- +c\BeginDoc +c +c\Name: igraphdsesrt +c +c\Description: +c Sort the array X in the order specified by WHICH and optionally +c apply the permutation to the columns of the matrix A. +c +c\Usage: +c call igraphdsesrt +c ( WHICH, APPLY, N, X, NA, A, LDA) +c +c\Arguments +c WHICH Character*2. (Input) +c 'LM' -> X is sorted into increasing order of magnitude. +c 'SM' -> X is sorted into decreasing order of magnitude. +c 'LA' -> X is sorted into increasing order of algebraic. +c 'SA' -> X is sorted into decreasing order of algebraic. +c +c APPLY Logical. (Input) +c APPLY = .TRUE. -> apply the sorted order to A. +c APPLY = .FALSE. -> do not apply the sorted order to A. +c +c N Integer. (INPUT) +c Dimension of the array X. +c +c X Double precision array of length N. (INPUT/OUTPUT) +c The array to be sorted. +c +c NA Integer. (INPUT) +c Number of rows of the matrix A. +c +c A Double precision array of length NA by N. (INPUT/OUTPUT) +c +c LDA Integer. (INPUT) +c Leading dimension of A. +c +c\EndDoc +c +c----------------------------------------------------------------------- +c +c\BeginLib +c +c\Routines +c dswap Level 1 BLAS that swaps the contents of two vectors. +c +c\Authors +c Danny Sorensen Phuong Vu +c Richard Lehoucq CRPC / Rice University +c Dept. of Computational & Houston, Texas +c Applied Mathematics +c Rice University +c Houston, Texas +c +c\Revision history: +c 12/15/93: Version ' 2.1'. +c Adapted from the sort routine in LANSO and +c the ARPACK code igraphdsortr +c +c\SCCS Information: @(#) +c FILE: sesrt.F SID: 2.3 DATE OF SID: 4/19/96 RELEASE: 2 +c +c\EndLib +c +c----------------------------------------------------------------------- +c + subroutine igraphdsesrt (which, apply, n, x, na, a, lda) +c +c %------------------% +c | Scalar Arguments | +c %------------------% +c + character*2 which + logical apply + integer lda, n, na +c +c %-----------------% +c | Array Arguments | +c %-----------------% +c + Double precision + & x(0:n-1), a(lda, 0:n-1) +c +c %---------------% +c | Local Scalars | +c %---------------% +c + integer i, igap, j + Double precision + & temp +c +c %----------------------% +c | External Subroutines | +c %----------------------% +c + external dswap +c +c %-----------------------% +c | Executable Statements | +c %-----------------------% +c + igap = n / 2 +c + if (which .eq. 'SA') then +c +c X is sorted into decreasing order of algebraic. +c + 10 continue + if (igap .eq. 0) go to 9000 + do 30 i = igap, n-1 + j = i-igap + 20 continue +c + if (j.lt.0) go to 30 +c + if (x(j).lt.x(j+igap)) then + temp = x(j) + x(j) = x(j+igap) + x(j+igap) = temp + if (apply) call dswap( na, a(1, j), 1, a(1,j+igap), 1) + else + go to 30 + endif + j = j-igap + go to 20 + 30 continue + igap = igap / 2 + go to 10 +c + else if (which .eq. 'SM') then +c +c X is sorted into decreasing order of magnitude. +c + 40 continue + if (igap .eq. 0) go to 9000 + do 60 i = igap, n-1 + j = i-igap + 50 continue +c + if (j.lt.0) go to 60 +c + if (abs(x(j)).lt.abs(x(j+igap))) then + temp = x(j) + x(j) = x(j+igap) + x(j+igap) = temp + if (apply) call dswap( na, a(1, j), 1, a(1,j+igap), 1) + else + go to 60 + endif + j = j-igap + go to 50 + 60 continue + igap = igap / 2 + go to 40 +c + else if (which .eq. 'LA') then +c +c X is sorted into increasing order of algebraic. +c + 70 continue + if (igap .eq. 0) go to 9000 + do 90 i = igap, n-1 + j = i-igap + 80 continue +c + if (j.lt.0) go to 90 +c + if (x(j).gt.x(j+igap)) then + temp = x(j) + x(j) = x(j+igap) + x(j+igap) = temp + if (apply) call dswap( na, a(1, j), 1, a(1,j+igap), 1) + else + go to 90 + endif + j = j-igap + go to 80 + 90 continue + igap = igap / 2 + go to 70 +c + else if (which .eq. 'LM') then +c +c X is sorted into increasing order of magnitude. +c + 100 continue + if (igap .eq. 0) go to 9000 + do 120 i = igap, n-1 + j = i-igap + 110 continue +c + if (j.lt.0) go to 120 +c + if (abs(x(j)).gt.abs(x(j+igap))) then + temp = x(j) + x(j) = x(j+igap) + x(j+igap) = temp + if (apply) call dswap( na, a(1, j), 1, a(1,j+igap), 1) + else + go to 120 + endif + j = j-igap + go to 110 + 120 continue + igap = igap / 2 + go to 100 + end if +c + 9000 continue + return +c +c %---------------% +c | End of igraphdsesrt | +c %---------------% +c + end diff --git a/src/rigraph/vendor/arpack/dseupd.f b/src/rigraph/vendor/arpack/dseupd.f new file mode 100644 index 0000000..0336c3a --- /dev/null +++ b/src/rigraph/vendor/arpack/dseupd.f @@ -0,0 +1,905 @@ +c\BeginDoc +c +c\Name: igraphdseupd +c +c\Description: +c +c This subroutine returns the converged approximations to eigenvalues +c of A*z = lambda*B*z and (optionally): +c +c (1) the corresponding approximate eigenvectors, +c +c (2) an orthonormal (Lanczos) basis for the associated approximate +c invariant subspace, +c +c (3) Both. +c +c There is negligible additional cost to obtain eigenvectors. An orthonormal +c (Lanczos) basis is always computed. There is an additional storage cost +c of n*nev if both are requested (in this case a separate array Z must be +c supplied). +c +c These quantities are obtained from the Lanczos factorization computed +c by DSAUPD for the linear operator OP prescribed by the MODE selection +c (see IPARAM(7) in DSAUPD documentation.) DSAUPD must be called before +c this routine is called. These approximate eigenvalues and vectors are +c commonly called Ritz values and Ritz vectors respectively. They are +c referred to as such in the comments that follow. The computed orthonormal +c basis for the invariant subspace corresponding to these Ritz values is +c referred to as a Lanczos basis. +c +c See documentation in the header of the subroutine DSAUPD for a definition +c of OP as well as other terms and the relation of computed Ritz values +c and vectors of OP with respect to the given problem A*z = lambda*B*z. +c +c The approximate eigenvalues of the original problem are returned in +c ascending algebraic order. The user may elect to call this routine +c once for each desired Ritz vector and store it peripherally if desired. +c There is also the option of computing a selected set of these vectors +c with a single call. +c +c\Usage: +c call igraphdseupd +c ( RVEC, HOWMNY, SELECT, D, Z, LDZ, SIGMA, BMAT, N, WHICH, NEV, TOL, +c RESID, NCV, V, LDV, IPARAM, IPNTR, WORKD, WORKL, LWORKL, INFO ) +c +c RVEC LOGICAL (INPUT) +c Specifies whether Ritz vectors corresponding to the Ritz value +c approximations to the eigenproblem A*z = lambda*B*z are computed. +c +c RVEC = .FALSE. Compute Ritz values only. +c +c RVEC = .TRUE. Compute Ritz vectors. +c +c HOWMNY Character*1 (INPUT) +c Specifies how many Ritz vectors are wanted and the form of Z +c the matrix of Ritz vectors. See remark 1 below. +c = 'A': compute NEV Ritz vectors; +c = 'S': compute some of the Ritz vectors, specified +c by the logical array SELECT. +c +c SELECT Logical array of dimension NEV. (INPUT) +c If HOWMNY = 'S', SELECT specifies the Ritz vectors to be +c computed. To select the Ritz vector corresponding to a +c Ritz value D(j), SELECT(j) must be set to .TRUE.. +c If HOWMNY = 'A' , SELECT is not referenced. +c +c D Double precision array of dimension NEV. (OUTPUT) +c On exit, D contains the Ritz value approximations to the +c eigenvalues of A*z = lambda*B*z. The values are returned +c in ascending order. If IPARAM(7) = 3,4,5 then D represents +c the Ritz values of OP computed by igraphdsaupd transformed to +c those of the original eigensystem A*z = lambda*B*z. If +c IPARAM(7) = 1,2 then the Ritz values of OP are the same +c as the those of A*z = lambda*B*z. +c +c Z Double precision N by NEV array if HOWMNY = 'A'. (OUTPUT) +c On exit, Z contains the B-orthonormal Ritz vectors of the +c eigensystem A*z = lambda*B*z corresponding to the Ritz +c value approximations. +c If RVEC = .FALSE. then Z is not referenced. +c NOTE: The array Z may be set equal to first NEV columns of the +c Arnoldi/Lanczos basis array V computed by DSAUPD. +c +c LDZ Integer. (INPUT) +c The leading dimension of the array Z. If Ritz vectors are +c desired, then LDZ .ge. max( 1, N ). In any case, LDZ .ge. 1. +c +c SIGMA Double precision (INPUT) +c If IPARAM(7) = 3,4,5 represents the shift. Not referenced if +c IPARAM(7) = 1 or 2. +c +c +c **** The remaining arguments MUST be the same as for the **** +c **** call to DNAUPD that was just completed. **** +c +c NOTE: The remaining arguments +c +c BMAT, N, WHICH, NEV, TOL, RESID, NCV, V, LDV, IPARAM, IPNTR, +c WORKD, WORKL, LWORKL, INFO +c +c must be passed directly to DSEUPD following the last call +c to DSAUPD. These arguments MUST NOT BE MODIFIED between +c the the last call to DSAUPD and the call to DSEUPD. +c +c Two of these parameters (WORKL, INFO) are also output parameters: +c +c WORKL Double precision work array of length LWORKL. (OUTPUT/WORKSPACE) +c WORKL(1:4*ncv) contains information obtained in +c igraphdsaupd. They are not changed by igraphdseupd. +c WORKL(4*ncv+1:ncv*ncv+8*ncv) holds the +c untransformed Ritz values, the computed error estimates, +c and the associated eigenvector matrix of H. +c +c Note: IPNTR(8:10) contains the pointer into WORKL for addresses +c of the above information computed by igraphdseupd. +c ------------------------------------------------------------- +c IPNTR(8): pointer to the NCV RITZ values of the original system. +c IPNTR(9): pointer to the NCV corresponding error bounds. +c IPNTR(10): pointer to the NCV by NCV matrix of eigenvectors +c of the tridiagonal matrix T. Only referenced by +c igraphdseupd if RVEC = .TRUE. See Remarks. +c ------------------------------------------------------------- +c +c INFO Integer. (OUTPUT) +c Error flag on output. +c = 0: Normal exit. +c = -1: N must be positive. +c = -2: NEV must be positive. +c = -3: NCV must be greater than NEV and less than or equal to N. +c = -5: WHICH must be one of 'LM', 'SM', 'LA', 'SA' or 'BE'. +c = -6: BMAT must be one of 'I' or 'G'. +c = -7: Length of private work WORKL array is not sufficient. +c = -8: Error return from trid. eigenvalue calculation; +c Information error from LAPACK routine dsteqr. +c = -9: Starting vector is zero. +c = -10: IPARAM(7) must be 1,2,3,4,5. +c = -11: IPARAM(7) = 1 and BMAT = 'G' are incompatible. +c = -12: NEV and WHICH = 'BE' are incompatible. +c = -14: DSAUPD did not find any eigenvalues to sufficient +c accuracy. +c = -15: HOWMNY must be one of 'A' or 'S' if RVEC = .true. +c = -16: HOWMNY = 'S' not yet implemented +c +c\BeginLib +c +c\References: +c 1. D.C. Sorensen, "Implicit Application of Polynomial Filters in +c a k-Step Arnoldi Method", SIAM J. Matr. Anal. Apps., 13 (1992), +c pp 357-385. +c 2. R.B. Lehoucq, "Analysis and Implementation of an Implicitly +c Restarted Arnoldi Iteration", Rice University Technical Report +c TR95-13, Department of Computational and Applied Mathematics. +c 3. B.N. Parlett, "The Symmetric Eigenvalue Problem". Prentice-Hall, +c 1980. +c 4. B.N. Parlett, B. Nour-Omid, "Towards a Black Box Lanczos Program", +c Computer Physics Communications, 53 (1989), pp 169-179. +c 5. B. Nour-Omid, B.N. Parlett, T. Ericson, P.S. Jensen, "How to +c Implement the Spectral Transformation", Math. Comp., 48 (1987), +c pp 663-673. +c 6. R.G. Grimes, J.G. Lewis and H.D. Simon, "A Shifted Block Lanczos +c Algorithm for Solving Sparse Symmetric Generalized Eigenproblems", +c SIAM J. Matr. Anal. Apps., January (1993). +c 7. L. Reichel, W.B. Gragg, "Algorithm 686: FORTRAN Subroutines +c for Updating the QR decomposition", ACM TOMS, December 1990, +c Volume 16 Number 4, pp 369-377. +c +c\Remarks +c 1. The converged Ritz values are always returned in increasing +c (algebraic) order. +c +c 2. Currently only HOWMNY = 'A' is implemented. It is included at this +c stage for the user who wants to incorporate it. +c +c\Routines called: +c igraphdsesrt ARPACK routine that sorts an array X, and applies the +c corresponding permutation to a matrix A. +c igraphdsortr igraphdsortr ARPACK sorting routine. +c igraphivout ARPACK utility routine that prints integers. +c igraphdvout ARPACK utility routine that prints vectors. +c dgeqr2 LAPACK routine that computes the QR factorization of +c a matrix. +c dlacpy LAPACK matrix copy routine. +c dlamch LAPACK routine that determines machine constants. +c dorm2r LAPACK routine that applies an orthogonal matrix in +c factored form. +c dsteqr LAPACK routine that computes eigenvalues and eigenvectors +c of a tridiagonal matrix. +c dger Level 2 BLAS rank one update to a matrix. +c dcopy Level 1 BLAS that copies one vector to another . +c dnrm2 Level 1 BLAS that computes the norm of a vector. +c dscal Level 1 BLAS that scales a vector. +c dswap Level 1 BLAS that swaps the contents of two vectors. + +c\Authors +c Danny Sorensen Phuong Vu +c Richard Lehoucq CRPC / Rice University +c Chao Yang Houston, Texas +c Dept. of Computational & +c Applied Mathematics +c Rice University +c Houston, Texas +c +c\Revision history: +c 12/15/93: Version ' 2.1' +c +c\SCCS Information: @(#) +c FILE: seupd.F SID: 2.7 DATE OF SID: 8/27/96 RELEASE: 2 +c +c\EndLib +c +c----------------------------------------------------------------------- + subroutine igraphdseupd (rvec, howmny, select, d, z, ldz, + & sigma, bmat, n, which, nev, tol, resid, ncv, v, ldv, iparam, + & ipntr, workd, workl, lworkl, info ) +c +c %----------------------------------------------------% +c | Include files for debugging and timing information | +c %----------------------------------------------------% +c + include 'debug.h' + include 'stat.h' +c +c %------------------% +c | Scalar Arguments | +c %------------------% +c + character bmat, howmny, which*2 + logical rvec, select(ncv) + integer info, ldz, ldv, lworkl, n, ncv, nev + Double precision + & sigma, tol +c +c %-----------------% +c | Array Arguments | +c %-----------------% +c + integer iparam(7), ipntr(11) + Double precision + & d(nev), resid(n), v(ldv,ncv), z(ldz, nev), + & workd(2*n), workl(lworkl) +c +c %------------% +c | Parameters | +c %------------% +c + Double precision + & one, zero + parameter (one = 1.0D+0, zero = 0.0D+0) +c +c %---------------% +c | Local Scalars | +c %---------------% +c + character type*6 + integer bounds, ierr, ih, ihb, ihd, iq, iw, j, k, + & ldh, ldq, mode, msglvl, nconv, next, ritz, + & irz, ibd, ktrord, leftptr, rghtptr, ism, ilg + Double precision + & bnorm2, rnorm, temp, thres1, thres2, tempbnd, eps23 + logical reord +c +c %--------------% +c | Local Arrays | +c %--------------% +c + Double precision + & kv(2) +c +c %----------------------% +c | External Subroutines | +c %----------------------% +c + external dcopy, dger, dgeqr2, dlacpy, dorm2r, dscal, + & igraphdsesrt, dsteqr, dswap, igraphdvout, + & igraphivout, igraphdsortr +c +c %--------------------% +c | External Functions | +c %--------------------% +c + Double precision + & dnrm2, dlamch + external dnrm2, dlamch +c +c %---------------------% +c | Intrinsic Functions | +c %---------------------% +c + intrinsic min +c +c %-----------------------% +c | Executable Statements | +c %-----------------------% +c +c %------------------------% +c | Set default parameters | +c %------------------------% +c + msglvl = mseupd + mode = iparam(7) + nconv = iparam(5) + info = 0 +c +c %--------------% +c | Quick return | +c %--------------% +c + if (nconv .eq. 0) go to 9000 + ierr = 0 +c + if (nconv .le. 0) ierr = -14 + if (n .le. 0) ierr = -1 + if (nev .le. 0) ierr = -2 + if (ncv .le. nev .or. ncv .gt. n) ierr = -3 + if (which .ne. 'LM' .and. + & which .ne. 'SM' .and. + & which .ne. 'LA' .and. + & which .ne. 'SA' .and. + & which .ne. 'BE') ierr = -5 + if (bmat .ne. 'I' .and. bmat .ne. 'G') ierr = -6 + if ( (howmny .ne. 'A' .and. + & howmny .ne. 'P' .and. + & howmny .ne. 'S') .and. rvec ) + & ierr = -15 + if (rvec .and. howmny .eq. 'S') ierr = -16 +c + if (rvec .and. lworkl .lt. ncv**2+8*ncv) ierr = -7 +c + if (mode .eq. 1 .or. mode .eq. 2) then + type = 'REGULR' + else if (mode .eq. 3 ) then + type = 'SHIFTI' + else if (mode .eq. 4 ) then + type = 'BUCKLE' + else if (mode .eq. 5 ) then + type = 'CAYLEY' + else + ierr = -10 + end if + if (mode .eq. 1 .and. bmat .eq. 'G') ierr = -11 + if (nev .eq. 1 .and. which .eq. 'BE') ierr = -12 +c +c %------------% +c | Error Exit | +c %------------% +c + if (ierr .ne. 0) then + info = ierr + go to 9000 + end if +c +c %-------------------------------------------------------% +c | Pointer into WORKL for address of H, RITZ, BOUNDS, Q | +c | etc... and the remaining workspace. | +c | Also update pointer to be used on output. | +c | Memory is laid out as follows: | +c | workl(1:2*ncv) := generated tridiagonal matrix H | +c | The subdiagonal is stored in workl(2:ncv). | +c | The dead spot is workl(1) but upon exiting | +c | igraphdsaupd stores the B-norm of the last residual | +c | vector in workl(1). We use this !!! | +c | workl(2*ncv+1:2*ncv+ncv) := ritz values | +c | The wanted values are in the first NCONV spots. | +c | workl(3*ncv+1:3*ncv+ncv) := computed Ritz estimates | +c | The wanted values are in the first NCONV spots. | +c | NOTE: workl(1:4*ncv) is set by igraphdsaupd and is not | +c | modified by igraphdseupd. | +c %-------------------------------------------------------% +c +c %-------------------------------------------------------% +c | The following is used and set by igraphdseupd. | +c | workl(4*ncv+1:4*ncv+ncv) := used as workspace during | +c | computation of the eigenvectors of H. Stores | +c | the diagonal of H. Upon EXIT contains the NCV | +c | Ritz values of the original system. The first | +c | NCONV spots have the wanted values. If MODE = | +c | 1 or 2 then will equal workl(2*ncv+1:3*ncv). | +c | workl(5*ncv+1:5*ncv+ncv) := used as workspace during | +c | computation of the eigenvectors of H. Stores | +c | the subdiagonal of H. Upon EXIT contains the | +c | NCV corresponding Ritz estimates of the | +c | original system. The first NCONV spots have the | +c | wanted values. If MODE = 1,2 then will equal | +c | workl(3*ncv+1:4*ncv). | +c | workl(6*ncv+1:6*ncv+ncv*ncv) := orthogonal Q that is | +c | the eigenvector matrix for H as returned by | +c | dsteqr. Not referenced if RVEC = .False. | +c | Ordering follows that of workl(4*ncv+1:5*ncv) | +c | workl(6*ncv+ncv*ncv+1:6*ncv+ncv*ncv+2*ncv) := | +c | Workspace. Needed by dsteqr and by igraphdseupd. | +c | GRAND total of NCV*(NCV+8) locations. | +c %-------------------------------------------------------% +c +c + 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 + 2*ncv + ipntr(4) = next + ipntr(8) = ihd + ipntr(9) = ihb + ipntr(10) = iq +c +c %----------------------------------------% +c | irz points to the Ritz values computed | +c | by _seigt before exiting _saup2. | +c | ibd points to the Ritz estimates | +c | computed by _seigt before exiting | +c | _saup2. | +c %----------------------------------------% +c + irz = ipntr(11)+ncv + ibd = irz+ncv +c +c +c %---------------------------------% +c | Set machine dependent constant. | +c %---------------------------------% +c + eps23 = dlamch('Epsilon-Machine') + eps23 = eps23**(2.0D+0 / 3.0D+0) +c +c %---------------------------------------% +c | RNORM is B-norm of the RESID(1:N). | +c | BNORM2 is the 2 norm of B*RESID(1:N). | +c | Upon exit of igraphdsaupd WORKD(1:N) has | +c | B*RESID(1:N). | +c %---------------------------------------% +c + rnorm = workl(ih) + if (bmat .eq. 'I') then + bnorm2 = rnorm + else if (bmat .eq. 'G') then + bnorm2 = dnrm2(n, workd, 1) + end if +c + if (rvec) then +c +c %------------------------------------------------% +c | Get the converged Ritz value on the boundary. | +c | This value will be used to dermine whether we | +c | need to reorder the eigenvalues and | +c | eigenvectors comupted by _steqr, and is | +c | referred to as the "threshold" value. | +c | | +c | A Ritz value gamma is said to be a wanted | +c | one, if | +c | abs(gamma) .ge. threshold, when WHICH = 'LM'; | +c | abs(gamma) .le. threshold, when WHICH = 'SM'; | +c | gamma .ge. threshold, when WHICH = 'LA'; | +c | gamma .le. threshold, when WHICH = 'SA'; | +c | gamma .le. thres1 .or. gamma .ge. thres2 | +c | when WHICH = 'BE'; | +c | | +c | Note: converged Ritz values and associated | +c | Ritz estimates have been placed in the first | +c | NCONV locations in workl(ritz) and | +c | workl(bounds) respectively. They have been | +c | sorted (in _saup2) according to the WHICH | +c | selection criterion. (Except in the case | +c | WHICH = 'BE', they are sorted in an increasing | +c | order.) | +c %------------------------------------------------% +c + if (which .eq. 'LM' .or. which .eq. 'SM' + & .or. which .eq. 'LA' .or. which .eq. 'SA' ) then +c + thres1 = workl(ritz) +c + if (msglvl .gt. 2) then + call igraphdvout(logfil, 1, [thres1], ndigit, + & '_seupd: Threshold eigenvalue used for re-ordering') + end if +c + else if (which .eq. 'BE') then +c +c %------------------------------------------------% +c | Ritz values returned from _saup2 have been | +c | sorted in increasing order. Thus two | +c | "threshold" values (one for the small end, one | +c | for the large end) are in the middle. | +c %------------------------------------------------% +c + ism = max(nev,nconv) / 2 + ilg = ism + 1 + thres1 = workl(ism) + thres2 = workl(ilg) +c + if (msglvl .gt. 2) then + kv(1) = thres1 + kv(2) = thres2 + call igraphdvout(logfil, 2, kv, ndigit, + & '_seupd: Threshold eigenvalues used for re-ordering') + end if +c + end if +c +c %----------------------------------------------------------% +c | Check to see if all converged Ritz values appear within | +c | the first NCONV diagonal elements returned from _seigt. | +c | This is done in the following way: | +c | | +c | 1) For each Ritz value obtained from _seigt, compare it | +c | with the threshold Ritz value computed above to | +c | determine whether it is a wanted one. | +c | | +c | 2) If it is wanted, then check the corresponding Ritz | +c | estimate to see if it has converged. If it has, set | +c | correponding entry in the logical array SELECT to | +c | .TRUE.. | +c | | +c | If SELECT(j) = .TRUE. and j > NCONV, then there is a | +c | converged Ritz value that does not appear at the top of | +c | the diagonal matrix computed by _seigt in _saup2. | +c | Reordering is needed. | +c %----------------------------------------------------------% +c + reord = .false. + ktrord = 0 + do 10 j = 0, ncv-1 + select(j+1) = .false. + if (which .eq. 'LM') then + if (abs(workl(irz+j)) .ge. abs(thres1)) then + tempbnd = max( eps23, abs(workl(irz+j)) ) + if (workl(ibd+j) .le. tol*tempbnd) then + select(j+1) = .true. + end if + end if + else if (which .eq. 'SM') then + if (abs(workl(irz+j)) .le. abs(thres1)) then + tempbnd = max( eps23, abs(workl(irz+j)) ) + if (workl(ibd+j) .le. tol*tempbnd) then + select(j+1) = .true. + end if + end if + else if (which .eq. 'LA') then + if (workl(irz+j) .ge. thres1) then + tempbnd = max( eps23, abs(workl(irz+j)) ) + if (workl(ibd+j) .le. tol*tempbnd) then + select(j+1) = .true. + end if + end if + else if (which .eq. 'SA') then + if (workl(irz+j) .le. thres1) then + tempbnd = max( eps23, abs(workl(irz+j)) ) + if (workl(ibd+j) .le. tol*tempbnd) then + select(j+1) = .true. + end if + end if + else if (which .eq. 'BE') then + if ( workl(irz+j) .le. thres1 .or. + & workl(irz+j) .ge. thres2 ) then + tempbnd = max( eps23, abs(workl(irz+j)) ) + if (workl(ibd+j) .le. tol*tempbnd) then + select(j+1) = .true. + end if + end if + end if + if (j+1 .gt. nconv ) reord = select(j+1) .or. reord + if (select(j+1)) ktrord = ktrord + 1 + 10 continue + +c %-------------------------------------------% +c | If KTRORD .ne. NCONV, something is wrong. | +c %-------------------------------------------% +c + if (msglvl .gt. 2) then + call igraphivout(logfil, 1, [ktrord], ndigit, + & '_seupd: Number of specified eigenvalues') + call igraphivout(logfil, 1, [nconv], ndigit, + & '_seupd: Number of "converged" eigenvalues') + end if +c +c %-----------------------------------------------------------% +c | Call LAPACK routine _steqr to compute the eigenvalues and | +c | eigenvectors of the final symmetric tridiagonal matrix H. | +c | Initialize the eigenvector matrix Q to the identity. | +c %-----------------------------------------------------------% +c + call dcopy (ncv-1, workl(ih+1), 1, workl(ihb), 1) + call dcopy (ncv, workl(ih+ldh), 1, workl(ihd), 1) +c + call dsteqr ('Identity', ncv, workl(ihd), workl(ihb), + & workl(iq), ldq, workl(iw), ierr) +c + if (ierr .ne. 0) then + info = -8 + go to 9000 + end if +c + if (msglvl .gt. 1) then + call dcopy (ncv, workl(iq+ncv-1), ldq, workl(iw), 1) + call igraphdvout (logfil, ncv, workl(ihd), ndigit, + & '_seupd: NCV Ritz values of the final H matrix') + call igraphdvout (logfil, ncv, workl(iw), ndigit, + & '_seupd: last row of the eigenvector matrix for H') + end if +c + if (reord) then +c +c %---------------------------------------------% +c | Reordered the eigenvalues and eigenvectors | +c | computed by _steqr so that the "converged" | +c | eigenvalues appear in the first NCONV | +c | positions of workl(ihd), and the associated | +c | eigenvectors appear in the first NCONV | +c | columns. | +c %---------------------------------------------% +c + leftptr = 1 + rghtptr = ncv +c + if (ncv .eq. 1) go to 30 +c + 20 if (select(leftptr)) then +c +c %-------------------------------------------% +c | Search, from the left, for the first Ritz | +c | value that has not converged. | +c %-------------------------------------------% +c + leftptr = leftptr + 1 +c + else if ( .not. select(rghtptr)) then +c +c %----------------------------------------------% +c | Search, from the right, the first Ritz value | +c | that has converged. | +c %----------------------------------------------% +c + rghtptr = rghtptr - 1 +c + else +c +c %----------------------------------------------% +c | Swap the Ritz value on the left that has not | +c | converged with the Ritz value on the right | +c | that has converged. Swap the associated | +c | eigenvector of the tridiagonal matrix H as | +c | well. | +c %----------------------------------------------% +c + temp = workl(ihd+leftptr-1) + workl(ihd+leftptr-1) = workl(ihd+rghtptr-1) + workl(ihd+rghtptr-1) = temp + call dcopy(ncv, workl(iq+ncv*(leftptr-1)), 1, + & workl(iw), 1) + call dcopy(ncv, workl(iq+ncv*(rghtptr-1)), 1, + & workl(iq+ncv*(leftptr-1)), 1) + call dcopy(ncv, workl(iw), 1, + & workl(iq+ncv*(rghtptr-1)), 1) + leftptr = leftptr + 1 + rghtptr = rghtptr - 1 +c + end if +c + if (leftptr .lt. rghtptr) go to 20 +c + 30 end if +c + if (msglvl .gt. 2) then + call igraphdvout (logfil, ncv, workl(ihd), ndigit, + & '_seupd: The eigenvalues of H--reordered') + end if +c +c %----------------------------------------% +c | Load the converged Ritz values into D. | +c %----------------------------------------% +c + call dcopy(nconv, workl(ihd), 1, d, 1) +c + else +c +c %-----------------------------------------------------% +c | Ritz vectors not required. Load Ritz values into D. | +c %-----------------------------------------------------% +c + call dcopy (nconv, workl(ritz), 1, d, 1) + call dcopy (ncv, workl(ritz), 1, workl(ihd), 1) +c + end if +c +c %------------------------------------------------------------------% +c | Transform the Ritz values and possibly vectors and corresponding | +c | Ritz estimates of OP to those of A*x=lambda*B*x. The Ritz values | +c | (and corresponding data) are returned in ascending order. | +c %------------------------------------------------------------------% +c + if (type .eq. 'REGULR') then +c +c %---------------------------------------------------------% +c | Ascending sort of wanted Ritz values, vectors and error | +c | bounds. Not necessary if only Ritz values are desired. | +c %---------------------------------------------------------% +c + if (rvec) then + call igraphdsesrt ('LA', rvec , nconv, d, ncv, workl(iq), + & ldq) + else + call dcopy (ncv, workl(bounds), 1, workl(ihb), 1) + end if +c + else +c +c %-------------------------------------------------------------% +c | * Make a copy of all the Ritz values. | +c | * Transform the Ritz values back to the original system. | +c | For TYPE = 'SHIFTI' the transformation is | +c | lambda = 1/theta + sigma | +c | For TYPE = 'BUCKLE' the transformation is | +c | lambda = sigma * theta / ( theta - 1 ) | +c | For TYPE = 'CAYLEY' the transformation is | +c | lambda = sigma * (theta + 1) / (theta - 1 ) | +c | where the theta are the Ritz values returned by igraphdsaupd. | +c | NOTES: | +c | *The Ritz vectors are not affected by the transformation. | +c | They are only reordered. | +c %-------------------------------------------------------------% +c + call dcopy (ncv, workl(ihd), 1, workl(iw), 1) + if (type .eq. 'SHIFTI') then + do 40 k=1, ncv + workl(ihd+k-1) = one / workl(ihd+k-1) + sigma + 40 continue + else if (type .eq. 'BUCKLE') then + do 50 k=1, ncv + workl(ihd+k-1) = sigma * workl(ihd+k-1) / + & (workl(ihd+k-1) - one) + 50 continue + else if (type .eq. 'CAYLEY') then + do 60 k=1, ncv + workl(ihd+k-1) = sigma * (workl(ihd+k-1) + one) / + & (workl(ihd+k-1) - one) + 60 continue + end if +c +c %-------------------------------------------------------------% +c | * Store the wanted NCONV lambda values into D. | +c | * Sort the NCONV wanted lambda in WORKL(IHD:IHD+NCONV-1) | +c | into ascending order and apply sort to the NCONV theta | +c | values in the transformed system. We'll need this to | +c | compute Ritz estimates in the original system. | +c | * Finally sort the lambda's into ascending order and apply | +c | to Ritz vectors if wanted. Else just sort lambda's into | +c | ascending order. | +c | NOTES: | +c | *workl(iw:iw+ncv-1) contain the theta ordered so that they | +c | match the ordering of the lambda. We'll use them again for | +c | Ritz vector purification. | +c %-------------------------------------------------------------% +c + call dcopy (nconv, workl(ihd), 1, d, 1) + call igraphdsortr ('LA', .true., nconv, workl(ihd), workl(iw)) + if (rvec) then + call igraphdsesrt ('LA', rvec , nconv, d, ncv, workl(iq), + & ldq) + else + call dcopy (ncv, workl(bounds), 1, workl(ihb), 1) + call dscal (ncv, bnorm2/rnorm, workl(ihb), 1) + call igraphdsortr ('LA', .true., nconv, d, workl(ihb)) + end if +c + end if +c +c %------------------------------------------------% +c | Compute the Ritz vectors. Transform the wanted | +c | eigenvectors of the symmetric tridiagonal H by | +c | the Lanczos basis matrix V. | +c %------------------------------------------------% +c + if (rvec .and. howmny .eq. 'A') then +c +c %----------------------------------------------------------% +c | Compute the QR factorization of the matrix representing | +c | the wanted invariant subspace located in the first NCONV | +c | columns of workl(iq,ldq). | +c %----------------------------------------------------------% +c + call dgeqr2 (ncv, nconv, workl(iq), ldq, workl(iw+ncv), + & workl(ihb), ierr) +c +c +c %--------------------------------------------------------% +c | * Postmultiply V by Q. | +c | * Copy the first NCONV columns of VQ into Z. | +c | The N by NCONV matrix Z is now a matrix representation | +c | of the approximate invariant subspace associated with | +c | the Ritz values in workl(ihd). | +c %--------------------------------------------------------% +c + call dorm2r ('Right', 'Notranspose', n, ncv, nconv, workl(iq), + & ldq, workl(iw+ncv), v, ldv, workd(n+1), ierr) + call dlacpy ('All', n, nconv, v, ldv, z, ldz) +c +c %-----------------------------------------------------% +c | In order to compute the Ritz estimates for the Ritz | +c | values in both systems, need the last row of the | +c | eigenvector matrix. Remember, it's in factored form | +c %-----------------------------------------------------% +c + do 65 j = 1, ncv-1 + workl(ihb+j-1) = zero + 65 continue + workl(ihb+ncv-1) = one + call dorm2r ('Left', 'Transpose', ncv, 1, nconv, workl(iq), + & ldq, workl(iw+ncv), workl(ihb), ncv, temp, ierr) +c + else if (rvec .and. howmny .eq. 'S') then +c +c Not yet implemented. See remark 2 above. +c + end if +c + if (type .eq. 'REGULR' .and. rvec) then +c + do 70 j=1, ncv + workl(ihb+j-1) = rnorm * abs( workl(ihb+j-1) ) + 70 continue +c + else if (type .ne. 'REGULR' .and. rvec) then +c +c %-------------------------------------------------% +c | * Determine Ritz estimates of the theta. | +c | If RVEC = .true. then compute Ritz estimates | +c | of the theta. | +c | If RVEC = .false. then copy Ritz estimates | +c | as computed by igraphdsaupd. | +c | * Determine Ritz estimates of the lambda. | +c %-------------------------------------------------% +c + call dscal (ncv, bnorm2, workl(ihb), 1) + if (type .eq. 'SHIFTI') then +c + do 80 k=1, ncv + workl(ihb+k-1) = abs( workl(ihb+k-1) ) / workl(iw+k-1)**2 + 80 continue +c + else if (type .eq. 'BUCKLE') then +c + do 90 k=1, ncv + workl(ihb+k-1) = sigma * abs( workl(ihb+k-1) ) / + & ( workl(iw+k-1)-one )**2 + 90 continue +c + else if (type .eq. 'CAYLEY') then +c + do 100 k=1, ncv + workl(ihb+k-1) = abs( workl(ihb+k-1) / + & workl(iw+k-1)*(workl(iw+k-1)-one) ) + 100 continue +c + end if +c + end if +c + if (type .ne. 'REGULR' .and. msglvl .gt. 1) then + call igraphdvout (logfil, nconv, d, ndigit, + & '_seupd: Untransformed converged Ritz values') + call igraphdvout (logfil, nconv, workl(ihb), ndigit, + & '_seupd: Ritz estimates of the untransformed Ritz values') + else if (msglvl .gt. 1) then + call igraphdvout (logfil, nconv, d, ndigit, + & '_seupd: Converged Ritz values') + call igraphdvout (logfil, nconv, workl(ihb), ndigit, + & '_seupd: Associated Ritz estimates') + end if +c +c %-------------------------------------------------% +c | Ritz vector purification step. Formally perform | +c | one of inverse subspace iteration. Only used | +c | for MODE = 3,4,5. See reference 7 | +c %-------------------------------------------------% +c + if (rvec .and. (type .eq. 'SHIFTI' .or. type .eq. 'CAYLEY')) then +c + do 110 k=0, nconv-1 + workl(iw+k) = workl(iq+k*ldq+ncv-1) / workl(iw+k) + 110 continue +c + else if (rvec .and. type .eq. 'BUCKLE') then +c + do 120 k=0, nconv-1 + workl(iw+k) = workl(iq+k*ldq+ncv-1) / (workl(iw+k)-one) + 120 continue +c + end if +c + if (type .ne. 'REGULR') + & call dger (n, nconv, one, resid, 1, workl(iw), 1, z, ldz) +c + 9000 continue +c + return +c +c %---------------% +c | End of igraphdseupd | +c %---------------% +c + end diff --git a/src/rigraph/vendor/arpack/dsgets.f b/src/rigraph/vendor/arpack/dsgets.f new file mode 100644 index 0000000..2b0794f --- /dev/null +++ b/src/rigraph/vendor/arpack/dsgets.f @@ -0,0 +1,220 @@ +c----------------------------------------------------------------------- +c\BeginDoc +c +c\Name: igraphdsgets +c +c\Description: +c Given the eigenvalues of the symmetric tridiagonal matrix H, +c computes the NP shifts AMU that are zeros of the polynomial of +c degree NP which filters out components of the unwanted eigenvectors +c corresponding to the AMU's based on some given criteria. +c +c NOTE: This is called even in the case of user specified shifts in +c order to sort the eigenvalues, and error bounds of H for later use. +c +c\Usage: +c call igraphdsgets +c ( ISHIFT, WHICH, KEV, NP, RITZ, BOUNDS, SHIFTS ) +c +c\Arguments +c ISHIFT Integer. (INPUT) +c Method for selecting the implicit shifts at each iteration. +c ISHIFT = 0: user specified shifts +c ISHIFT = 1: exact shift with respect to the matrix H. +c +c WHICH Character*2. (INPUT) +c Shift selection criteria. +c 'LM' -> KEV eigenvalues of largest magnitude are retained. +c 'SM' -> KEV eigenvalues of smallest magnitude are retained. +c 'LA' -> KEV eigenvalues of largest value are retained. +c 'SA' -> KEV eigenvalues of smallest value are retained. +c 'BE' -> KEV eigenvalues, half from each end of the spectrum. +c If KEV is odd, compute one more from the high end. +c +c KEV Integer. (INPUT) +c KEV+NP is the size of the matrix H. +c +c NP Integer. (INPUT) +c Number of implicit shifts to be computed. +c +c RITZ Double precision array of length KEV+NP. (INPUT/OUTPUT) +c On INPUT, RITZ contains the eigenvalues of H. +c On OUTPUT, RITZ are sorted so that the unwanted eigenvalues +c are in the first NP locations and the wanted part is in +c the last KEV locations. When exact shifts are selected, the +c unwanted part corresponds to the shifts to be applied. +c +c BOUNDS Double precision array of length KEV+NP. (INPUT/OUTPUT) +c Error bounds corresponding to the ordering in RITZ. +c +c SHIFTS Double precision array of length NP. (INPUT/OUTPUT) +c On INPUT: contains the user specified shifts if ISHIFT = 0. +c On OUTPUT: contains the shifts sorted into decreasing order +c of magnitude with respect to the Ritz estimates contained in +c BOUNDS. If ISHIFT = 0, SHIFTS is not modified on exit. +c +c\EndDoc +c +c----------------------------------------------------------------------- +c +c\BeginLib +c +c\Local variables: +c xxxxxx real +c +c\Routines called: +c igraphdsortr ARPACK utility sorting routine. +c igraphivout ARPACK utility routine that prints integers. +c igraphsecond ARPACK utility routine for timing. +c igraphdvout ARPACK utility routine that prints vectors. +c dcopy Level 1 BLAS that copies one vector to another. +c dswap Level 1 BLAS that swaps the contents of two vectors. +c +c\Author +c Danny Sorensen Phuong Vu +c Richard Lehoucq CRPC / Rice University +c Dept. of Computational & Houston, Texas +c Applied Mathematics +c Rice University +c Houston, Texas +c +c\Revision history: +c xx/xx/93: Version ' 2.1' +c +c\SCCS Information: @(#) +c FILE: sgets.F SID: 2.4 DATE OF SID: 4/19/96 RELEASE: 2 +c +c\Remarks +c +c\EndLib +c +c----------------------------------------------------------------------- +c + subroutine igraphdsgets ( ishift, which, kev, np, ritz, bounds, + & shifts ) +c +c %----------------------------------------------------% +c | Include files for debugging and timing information | +c %----------------------------------------------------% +c + include 'debug.h' + include 'stat.h' +c +c %------------------% +c | Scalar Arguments | +c %------------------% +c + character*2 which + integer ishift, kev, np +c +c %-----------------% +c | Array Arguments | +c %-----------------% +c + Double precision + & bounds(kev+np), ritz(kev+np), shifts(np) +c +c %------------% +c | Parameters | +c %------------% +c + Double precision + & one, zero + parameter (one = 1.0D+0, zero = 0.0D+0) +c +c %---------------% +c | Local Scalars | +c %---------------% +c + integer kevd2, msglvl +c +c %----------------------% +c | External Subroutines | +c %----------------------% +c + external dswap, dcopy, igraphdsortr, igraphsecond +c +c %---------------------% +c | Intrinsic Functions | +c %---------------------% +c + intrinsic max, min +c +c %-----------------------% +c | Executable Statements | +c %-----------------------% +c +c %-------------------------------% +c | Initialize timing statistics | +c | & message level for debugging | +c %-------------------------------% +c + call igraphsecond (t0) + msglvl = msgets +c + if (which .eq. 'BE') then +c +c %-----------------------------------------------------% +c | Both ends of the spectrum are requested. | +c | Sort the eigenvalues into algebraically increasing | +c | order first then swap high end of the spectrum next | +c | to low end in appropriate locations. | +c | NOTE: when np < floor(kev/2) be careful not to swap | +c | overlapping locations. | +c %-----------------------------------------------------% +c + call igraphdsortr ('LA', .true., kev+np, ritz, bounds) + kevd2 = kev / 2 + if ( kev .gt. 1 ) then + call dswap ( min(kevd2,np), ritz, 1, + & ritz( max(kevd2,np)+1 ), 1) + call dswap ( min(kevd2,np), bounds, 1, + & bounds( max(kevd2,np)+1 ), 1) + end if +c + else +c +c %----------------------------------------------------% +c | LM, SM, LA, SA case. | +c | Sort the eigenvalues of H into the desired order | +c | and apply the resulting order to BOUNDS. | +c | The eigenvalues are sorted so that the wanted part | +c | are always in the last KEV locations. | +c %----------------------------------------------------% +c + call igraphdsortr (which, .true., kev+np, ritz, bounds) + end if +c + if (ishift .eq. 1 .and. np .gt. 0) then +c +c %-------------------------------------------------------% +c | Sort the unwanted Ritz values used as shifts so that | +c | the ones with largest Ritz estimates are first. | +c | This will tend to minimize the effects of the | +c | forward instability of the iteration when the shifts | +c | are applied in subroutine igraphdsapps. | +c %-------------------------------------------------------% +c + call igraphdsortr ('SM', .true., np, bounds, ritz) + call dcopy (np, ritz, 1, shifts, 1) + end if +c + call igraphsecond (t1) + tsgets = tsgets + (t1 - t0) +c + if (msglvl .gt. 0) then + call igraphivout (logfil, 1, kev, ndigit, '_sgets: KEV is') + call igraphivout (logfil, 1, np, ndigit, '_sgets: NP is') + call igraphdvout (logfil, kev+np, ritz, ndigit, + & '_sgets: Eigenvalues of current H matrix') + call igraphdvout (logfil, kev+np, bounds, ndigit, + & '_sgets: Associated Ritz estimates') + end if +c + return +c +c %---------------% +c | End of igraphdsgets | +c %---------------% +c + end diff --git a/src/rigraph/vendor/arpack/dsortc.f b/src/rigraph/vendor/arpack/dsortc.f new file mode 100644 index 0000000..a356adc --- /dev/null +++ b/src/rigraph/vendor/arpack/dsortc.f @@ -0,0 +1,344 @@ +c----------------------------------------------------------------------- +c\BeginDoc +c +c\Name: igraphdsortc +c +c\Description: +c Sorts the complex array in XREAL and XIMAG into the order +c specified by WHICH and optionally applies the permutation to the +c real array Y. It is assumed that if an element of XIMAG is +c nonzero, then its negative is also an element. In other words, +c both members of a complex conjugate pair are to be sorted and the +c pairs are kept adjacent to each other. +c +c\Usage: +c call igraphdsortc +c ( WHICH, APPLY, N, XREAL, XIMAG, Y ) +c +c\Arguments +c WHICH Character*2. (Input) +c 'LM' -> sort XREAL,XIMAG into increasing order of magnitude. +c 'SM' -> sort XREAL,XIMAG into decreasing order of magnitude. +c 'LR' -> sort XREAL into increasing order of algebraic. +c 'SR' -> sort XREAL into decreasing order of algebraic. +c 'LI' -> sort XIMAG into increasing order of magnitude. +c 'SI' -> sort XIMAG into decreasing order of magnitude. +c NOTE: If an element of XIMAG is non-zero, then its negative +c is also an element. +c +c APPLY Logical. (Input) +c APPLY = .TRUE. -> apply the sorted order to array Y. +c APPLY = .FALSE. -> do not apply the sorted order to array Y. +c +c N Integer. (INPUT) +c Size of the arrays. +c +c XREAL, Double precision array of length N. (INPUT/OUTPUT) +c XIMAG Real and imaginary part of the array to be sorted. +c +c Y Double precision array of length N. (INPUT/OUTPUT) +c +c\EndDoc +c +c----------------------------------------------------------------------- +c +c\BeginLib +c +c\Author +c Danny Sorensen Phuong Vu +c Richard Lehoucq CRPC / Rice University +c Dept. of Computational & Houston, Texas +c Applied Mathematics +c Rice University +c Houston, Texas +c +c\Revision history: +c xx/xx/92: Version ' 2.1' +c Adapted from the sort routine in LANSO. +c +c\SCCS Information: @(#) +c FILE: sortc.F SID: 2.3 DATE OF SID: 4/20/96 RELEASE: 2 +c +c\EndLib +c +c----------------------------------------------------------------------- +c + subroutine igraphdsortc (which, apply, n, xreal, ximag, y) +c +c %------------------% +c | Scalar Arguments | +c %------------------% +c + character*2 which + logical apply + integer n +c +c %-----------------% +c | Array Arguments | +c %-----------------% +c + Double precision + & xreal(0:n-1), ximag(0:n-1), y(0:n-1) +c +c %---------------% +c | Local Scalars | +c %---------------% +c + integer i, igap, j + Double precision + & temp, temp1, temp2 +c +c %--------------------% +c | External Functions | +c %--------------------% +c + Double precision + & dlapy2 + external dlapy2 +c +c %-----------------------% +c | Executable Statements | +c %-----------------------% +c + igap = n / 2 +c + if (which .eq. 'LM') then +c +c %------------------------------------------------------% +c | Sort XREAL,XIMAG into increasing order of magnitude. | +c %------------------------------------------------------% +c + 10 continue + if (igap .eq. 0) go to 9000 +c + do 30 i = igap, n-1 + j = i-igap + 20 continue +c + if (j.lt.0) go to 30 +c + temp1 = dlapy2(xreal(j),ximag(j)) + temp2 = dlapy2(xreal(j+igap),ximag(j+igap)) +c + if (temp1.gt.temp2) then + temp = xreal(j) + xreal(j) = xreal(j+igap) + xreal(j+igap) = temp +c + temp = ximag(j) + ximag(j) = ximag(j+igap) + ximag(j+igap) = temp +c + if (apply) then + temp = y(j) + y(j) = y(j+igap) + y(j+igap) = temp + end if + else + go to 30 + end if + j = j-igap + go to 20 + 30 continue + igap = igap / 2 + go to 10 +c + else if (which .eq. 'SM') then +c +c %------------------------------------------------------% +c | Sort XREAL,XIMAG into decreasing order of magnitude. | +c %------------------------------------------------------% +c + 40 continue + if (igap .eq. 0) go to 9000 +c + do 60 i = igap, n-1 + j = i-igap + 50 continue +c + if (j .lt. 0) go to 60 +c + temp1 = dlapy2(xreal(j),ximag(j)) + temp2 = dlapy2(xreal(j+igap),ximag(j+igap)) +c + if (temp1.lt.temp2) then + temp = xreal(j) + xreal(j) = xreal(j+igap) + xreal(j+igap) = temp +c + temp = ximag(j) + ximag(j) = ximag(j+igap) + ximag(j+igap) = temp +c + if (apply) then + temp = y(j) + y(j) = y(j+igap) + y(j+igap) = temp + end if + else + go to 60 + endif + j = j-igap + go to 50 + 60 continue + igap = igap / 2 + go to 40 +c + else if (which .eq. 'LR') then +c +c %------------------------------------------------% +c | Sort XREAL into increasing order of algebraic. | +c %------------------------------------------------% +c + 70 continue + if (igap .eq. 0) go to 9000 +c + do 90 i = igap, n-1 + j = i-igap + 80 continue +c + if (j.lt.0) go to 90 +c + if (xreal(j).gt.xreal(j+igap)) then + temp = xreal(j) + xreal(j) = xreal(j+igap) + xreal(j+igap) = temp +c + temp = ximag(j) + ximag(j) = ximag(j+igap) + ximag(j+igap) = temp +c + if (apply) then + temp = y(j) + y(j) = y(j+igap) + y(j+igap) = temp + end if + else + go to 90 + endif + j = j-igap + go to 80 + 90 continue + igap = igap / 2 + go to 70 +c + else if (which .eq. 'SR') then +c +c %------------------------------------------------% +c | Sort XREAL into decreasing order of algebraic. | +c %------------------------------------------------% +c + 100 continue + if (igap .eq. 0) go to 9000 + do 120 i = igap, n-1 + j = i-igap + 110 continue +c + if (j.lt.0) go to 120 +c + if (xreal(j).lt.xreal(j+igap)) then + temp = xreal(j) + xreal(j) = xreal(j+igap) + xreal(j+igap) = temp +c + temp = ximag(j) + ximag(j) = ximag(j+igap) + ximag(j+igap) = temp +c + if (apply) then + temp = y(j) + y(j) = y(j+igap) + y(j+igap) = temp + end if + else + go to 120 + endif + j = j-igap + go to 110 + 120 continue + igap = igap / 2 + go to 100 +c + else if (which .eq. 'LI') then +c +c %------------------------------------------------% +c | Sort XIMAG into increasing order of magnitude. | +c %------------------------------------------------% +c + 130 continue + if (igap .eq. 0) go to 9000 + do 150 i = igap, n-1 + j = i-igap + 140 continue +c + if (j.lt.0) go to 150 +c + if (abs(ximag(j)).gt.abs(ximag(j+igap))) then + temp = xreal(j) + xreal(j) = xreal(j+igap) + xreal(j+igap) = temp +c + temp = ximag(j) + ximag(j) = ximag(j+igap) + ximag(j+igap) = temp +c + if (apply) then + temp = y(j) + y(j) = y(j+igap) + y(j+igap) = temp + end if + else + go to 150 + endif + j = j-igap + go to 140 + 150 continue + igap = igap / 2 + go to 130 +c + else if (which .eq. 'SI') then +c +c %------------------------------------------------% +c | Sort XIMAG into decreasing order of magnitude. | +c %------------------------------------------------% +c + 160 continue + if (igap .eq. 0) go to 9000 + do 180 i = igap, n-1 + j = i-igap + 170 continue +c + if (j.lt.0) go to 180 +c + if (abs(ximag(j)).lt.abs(ximag(j+igap))) then + temp = xreal(j) + xreal(j) = xreal(j+igap) + xreal(j+igap) = temp +c + temp = ximag(j) + ximag(j) = ximag(j+igap) + ximag(j+igap) = temp +c + if (apply) then + temp = y(j) + y(j) = y(j+igap) + y(j+igap) = temp + end if + else + go to 180 + endif + j = j-igap + go to 170 + 180 continue + igap = igap / 2 + go to 160 + end if +c + 9000 continue + return +c +c %---------------% +c | End of igraphdsortc | +c %---------------% +c + end diff --git a/src/rigraph/vendor/arpack/dsortr.f b/src/rigraph/vendor/arpack/dsortr.f new file mode 100644 index 0000000..d75bd61 --- /dev/null +++ b/src/rigraph/vendor/arpack/dsortr.f @@ -0,0 +1,218 @@ +c----------------------------------------------------------------------- +c\BeginDoc +c +c\Name: igraphdsortr +c +c\Description: +c Sort the array X1 in the order specified by WHICH and optionally +c applies the permutation to the array X2. +c +c\Usage: +c call igraphdsortr +c ( WHICH, APPLY, N, X1, X2 ) +c +c\Arguments +c WHICH Character*2. (Input) +c 'LM' -> X1 is sorted into increasing order of magnitude. +c 'SM' -> X1 is sorted into decreasing order of magnitude. +c 'LA' -> X1 is sorted into increasing order of algebraic. +c 'SA' -> X1 is sorted into decreasing order of algebraic. +c +c APPLY Logical. (Input) +c APPLY = .TRUE. -> apply the sorted order to X2. +c APPLY = .FALSE. -> do not apply the sorted order to X2. +c +c N Integer. (INPUT) +c Size of the arrays. +c +c X1 Double precision array of length N. (INPUT/OUTPUT) +c The array to be sorted. +c +c X2 Double precision array of length N. (INPUT/OUTPUT) +c Only referenced if APPLY = .TRUE. +c +c\EndDoc +c +c----------------------------------------------------------------------- +c +c\BeginLib +c +c\Author +c Danny Sorensen Phuong Vu +c Richard Lehoucq CRPC / Rice University +c Dept. of Computational & Houston, Texas +c Applied Mathematics +c Rice University +c Houston, Texas +c +c\Revision history: +c 12/16/93: Version ' 2.1'. +c Adapted from the sort routine in LANSO. +c +c\SCCS Information: @(#) +c FILE: sortr.F SID: 2.3 DATE OF SID: 4/19/96 RELEASE: 2 +c +c\EndLib +c +c----------------------------------------------------------------------- +c + subroutine igraphdsortr (which, apply, n, x1, x2) +c +c %------------------% +c | Scalar Arguments | +c %------------------% +c + character*2 which + logical apply + integer n +c +c %-----------------% +c | Array Arguments | +c %-----------------% +c + Double precision + & x1(0:n-1), x2(0:n-1) +c +c %---------------% +c | Local Scalars | +c %---------------% +c + integer i, igap, j + Double precision + & temp +c +c %-----------------------% +c | Executable Statements | +c %-----------------------% +c + igap = n / 2 +c + if (which .eq. 'SA') then +c +c X1 is sorted into decreasing order of algebraic. +c + 10 continue + if (igap .eq. 0) go to 9000 + do 30 i = igap, n-1 + j = i-igap + 20 continue +c + if (j.lt.0) go to 30 +c + if (x1(j).lt.x1(j+igap)) then + temp = x1(j) + x1(j) = x1(j+igap) + x1(j+igap) = temp + if (apply) then + temp = x2(j) + x2(j) = x2(j+igap) + x2(j+igap) = temp + end if + else + go to 30 + endif + j = j-igap + go to 20 + 30 continue + igap = igap / 2 + go to 10 +c + else if (which .eq. 'SM') then +c +c X1 is sorted into decreasing order of magnitude. +c + 40 continue + if (igap .eq. 0) go to 9000 + do 60 i = igap, n-1 + j = i-igap + 50 continue +c + if (j.lt.0) go to 60 +c + if (abs(x1(j)).lt.abs(x1(j+igap))) then + temp = x1(j) + x1(j) = x1(j+igap) + x1(j+igap) = temp + if (apply) then + temp = x2(j) + x2(j) = x2(j+igap) + x2(j+igap) = temp + end if + else + go to 60 + endif + j = j-igap + go to 50 + 60 continue + igap = igap / 2 + go to 40 +c + else if (which .eq. 'LA') then +c +c X1 is sorted into increasing order of algebraic. +c + 70 continue + if (igap .eq. 0) go to 9000 + do 90 i = igap, n-1 + j = i-igap + 80 continue +c + if (j.lt.0) go to 90 +c + if (x1(j).gt.x1(j+igap)) then + temp = x1(j) + x1(j) = x1(j+igap) + x1(j+igap) = temp + if (apply) then + temp = x2(j) + x2(j) = x2(j+igap) + x2(j+igap) = temp + end if + else + go to 90 + endif + j = j-igap + go to 80 + 90 continue + igap = igap / 2 + go to 70 +c + else if (which .eq. 'LM') then +c +c X1 is sorted into increasing order of magnitude. +c + 100 continue + if (igap .eq. 0) go to 9000 + do 120 i = igap, n-1 + j = i-igap + 110 continue +c + if (j.lt.0) go to 120 +c + if (abs(x1(j)).gt.abs(x1(j+igap))) then + temp = x1(j) + x1(j) = x1(j+igap) + x1(j+igap) = temp + if (apply) then + temp = x2(j) + x2(j) = x2(j+igap) + x2(j+igap) = temp + end if + else + go to 120 + endif + j = j-igap + go to 110 + 120 continue + igap = igap / 2 + go to 100 + end if +c + 9000 continue + return +c +c %---------------% +c | End of igraphdsortr | +c %---------------% +c + end diff --git a/src/rigraph/vendor/arpack/dstatn.f b/src/rigraph/vendor/arpack/dstatn.f new file mode 100644 index 0000000..afd0a57 --- /dev/null +++ b/src/rigraph/vendor/arpack/dstatn.f @@ -0,0 +1,61 @@ +c +c %---------------------------------------------% +c | Initialize statistic and timing information | +c | for nonsymmetric Arnoldi code. | +c %---------------------------------------------% +c +c\Author +c Danny Sorensen Phuong Vu +c Richard Lehoucq CRPC / Rice University +c Dept. of Computational & Houston, Texas +c Applied Mathematics +c Rice University +c Houston, Texas +c +c\SCCS Information: @(#) +c FILE: statn.F SID: 2.4 DATE OF SID: 4/20/96 RELEASE: 2 +c + subroutine igraphdstatn +c +c %--------------------------------% +c | See stat.doc for documentation | +c %--------------------------------% +c + include 'stat.h' +c +c %-----------------------% +c | Executable Statements | +c %-----------------------% +c + nopx = 0 + nbx = 0 + nrorth = 0 + nitref = 0 + nrstrt = 0 +c + tnaupd = 0.0D+0 + tnaup2 = 0.0D+0 + tnaitr = 0.0D+0 + tneigh = 0.0D+0 + tngets = 0.0D+0 + tnapps = 0.0D+0 + tnconv = 0.0D+0 + titref = 0.0D+0 + tgetv0 = 0.0D+0 + trvec = 0.0D+0 +c +c %----------------------------------------------------% +c | User time including reverse communication overhead | +c %----------------------------------------------------% +c + tmvopx = 0.0D+0 + tmvbx = 0.0D+0 +c + return +c +c +c %---------------% +c | End of igraphdstatn | +c %---------------% +c + end diff --git a/src/rigraph/vendor/arpack/dstats.f b/src/rigraph/vendor/arpack/dstats.f new file mode 100644 index 0000000..545ed19 --- /dev/null +++ b/src/rigraph/vendor/arpack/dstats.f @@ -0,0 +1,47 @@ +c +c\SCCS Information: @(#) +c FILE: stats.F SID: 2.1 DATE OF SID: 4/19/96 RELEASE: 2 +c %---------------------------------------------% +c | Initialize statistic and timing information | +c | for symmetric Arnoldi code. | +c %---------------------------------------------% + + subroutine igraphdstats + +c %--------------------------------% +c | See stat.doc for documentation | +c %--------------------------------% + include 'stat.h' + +c %-----------------------% +c | Executable Statements | +c %-----------------------% + + nopx = 0 + nbx = 0 + nrorth = 0 + nitref = 0 + nrstrt = 0 + + tsaupd = 0.0D+0 + tsaup2 = 0.0D+0 + tsaitr = 0.0D+0 + tseigt = 0.0D+0 + tsgets = 0.0D+0 + tsapps = 0.0D+0 + tsconv = 0.0D+0 + titref = 0.0D+0 + tgetv0 = 0.0D+0 + trvec = 0.0D+0 + +c %----------------------------------------------------% +c | User time including reverse communication overhead | +c %----------------------------------------------------% + tmvopx = 0.0D+0 + tmvbx = 0.0D+0 + + return +c +c End of igraphdstats +c + end diff --git a/src/rigraph/vendor/arpack/dstqrb.f b/src/rigraph/vendor/arpack/dstqrb.f new file mode 100644 index 0000000..eff1369 --- /dev/null +++ b/src/rigraph/vendor/arpack/dstqrb.f @@ -0,0 +1,594 @@ +c----------------------------------------------------------------------- +c\BeginDoc +c +c\Name: igraphdstqrb +c +c\Description: +c Computes all eigenvalues and the last component of the eigenvectors +c of a symmetric tridiagonal matrix using the implicit QL or QR method. +c +c This is mostly a modification of the LAPACK routine dsteqr. +c See Remarks. +c +c\Usage: +c call igraphdstqrb +c ( N, D, E, Z, WORK, INFO ) +c +c\Arguments +c N Integer. (INPUT) +c The number of rows and columns in the matrix. N >= 0. +c +c D Double precision array, dimension (N). (INPUT/OUTPUT) +c On entry, D contains the diagonal elements of the +c tridiagonal matrix. +c On exit, D contains the eigenvalues, in ascending order. +c If an error exit is made, the eigenvalues are correct +c for indices 1,2,...,INFO-1, but they are unordered and +c may not be the smallest eigenvalues of the matrix. +c +c E Double precision array, dimension (N-1). (INPUT/OUTPUT) +c On entry, E contains the subdiagonal elements of the +c tridiagonal matrix in positions 1 through N-1. +c On exit, E has been destroyed. +c +c Z Double precision array, dimension (N). (OUTPUT) +c On exit, Z contains the last row of the orthonormal +c eigenvector matrix of the symmetric tridiagonal matrix. +c If an error exit is made, Z contains the last row of the +c eigenvector matrix associated with the stored eigenvalues. +c +c WORK Double precision array, dimension (max(1,2*N-2)). (WORKSPACE) +c Workspace used in accumulating the transformation for +c computing the last components of the eigenvectors. +c +c INFO Integer. (OUTPUT) +c = 0: normal return. +c < 0: if INFO = -i, the i-th argument had an illegal value. +c > 0: if INFO = +i, the i-th eigenvalue has not converged +c after a total of 30*N iterations. +c +c\Remarks +c 1. None. +c +c----------------------------------------------------------------------- +c +c\BeginLib +c +c\Local variables: +c xxxxxx real +c +c\Routines called: +c daxpy Level 1 BLAS that computes a vector triad. +c dcopy Level 1 BLAS that copies one vector to another. +c dswap Level 1 BLAS that swaps the contents of two vectors. +c lsame LAPACK character comparison routine. +c dlae2 LAPACK routine that computes the eigenvalues of a 2-by-2 +c symmetric matrix. +c dlaev2 LAPACK routine that eigendecomposition of a 2-by-2 symmetric +c matrix. +c dlamch LAPACK routine that determines machine constants. +c dlanst LAPACK routine that computes the norm of a matrix. +c dlapy2 LAPACK routine to compute sqrt(x**2+y**2) carefully. +c dlartg LAPACK Givens rotation construction routine. +c dlascl LAPACK routine for careful scaling of a matrix. +c dlaset LAPACK matrix initialization routine. +c dlasr LAPACK routine that applies an orthogonal transformation to +c a matrix. +c dlasrt LAPACK sorting routine. +c dsteqr LAPACK routine that computes eigenvalues and eigenvectors +c of a symmetric tridiagonal matrix. +c xerbla LAPACK error handler routine. +c +c\Authors +c Danny Sorensen Phuong Vu +c Richard Lehoucq CRPC / Rice University +c Dept. of Computational & Houston, Texas +c Applied Mathematics +c Rice University +c Houston, Texas +c +c\SCCS Information: @(#) +c FILE: stqrb.F SID: 2.5 DATE OF SID: 8/27/96 RELEASE: 2 +c +c\Remarks +c 1. Starting with version 2.5, this routine is a modified version +c of LAPACK version 2.0 subroutine SSTEQR. No lines are deleted, +c only commeted out and new lines inserted. +c All lines commented out have "c$$$" at the beginning. +c Note that the LAPACK version 1.0 subroutine SSTEQR contained +c bugs. +c +c\EndLib +c +c----------------------------------------------------------------------- +c + subroutine igraphdstqrb ( n, d, e, z, work, info ) +c +c %------------------% +c | Scalar Arguments | +c %------------------% +c + integer info, n +c +c %-----------------% +c | Array Arguments | +c %-----------------% +c + Double precision + & d( n ), e( n-1 ), z( n ), work( 2*n-2 ) +c +c .. parameters .. + Double precision + & zero, one, two, three + parameter ( zero = 0.0D+0, one = 1.0D+0, + & two = 2.0D+0, three = 3.0D+0 ) + integer maxit + parameter ( maxit = 30 ) +c .. +c .. local scalars .. + integer i, icompz, ii, iscale, j, jtot, k, l, l1, lend, + & lendm1, lendp1, lendsv, lm1, lsv, m, mm, mm1, + & nm1, nmaxit + Double precision + & anorm, b, c, eps, eps2, f, g, p, r, rt1, rt2, + & s, safmax, safmin, ssfmax, ssfmin, tst +c .. +c .. external functions .. + logical lsame + Double precision + & dlamch, dlanst, dlapy2 + external lsame, dlamch, dlanst, dlapy2 +c .. +c .. external subroutines .. + external dlae2, dlaev2, dlartg, dlascl, dlaset, dlasr, + & dlasrt, dswap, xerbla +c .. +c .. intrinsic functions .. + intrinsic abs, max, sign, sqrt +c .. +c .. executable statements .. +c +c test the input parameters. +c + info = 0 +c +c$$$ IF( LSAME( COMPZ, 'N' ) ) THEN +c$$$ ICOMPZ = 0 +c$$$ ELSE IF( LSAME( COMPZ, 'V' ) ) THEN +c$$$ ICOMPZ = 1 +c$$$ ELSE IF( LSAME( COMPZ, 'I' ) ) THEN +c$$$ ICOMPZ = 2 +c$$$ ELSE +c$$$ ICOMPZ = -1 +c$$$ END IF +c$$$ IF( ICOMPZ.LT.0 ) THEN +c$$$ INFO = -1 +c$$$ ELSE IF( N.LT.0 ) THEN +c$$$ INFO = -2 +c$$$ ELSE IF( ( LDZ.LT.1 ) .OR. ( ICOMPZ.GT.0 .AND. LDZ.LT.MAX( 1, +c$$$ $ N ) ) ) THEN +c$$$ INFO = -6 +c$$$ END IF +c$$$ IF( INFO.NE.0 ) THEN +c$$$ CALL XERBLA( 'SSTEQR', -INFO ) +c$$$ RETURN +c$$$ END IF +c +c *** New starting with version 2.5 *** +c + icompz = 2 +c ************************************* +c +c quick return if possible +c + if( n.eq.0 ) + $ return +c + if( n.eq.1 ) then + if( icompz.eq.2 ) z( 1 ) = one + return + end if +c +c determine the unit roundoff and over/underflow thresholds. +c + eps = dlamch( 'e' ) + eps2 = eps**2 + safmin = dlamch( 's' ) + safmax = one / safmin + ssfmax = sqrt( safmax ) / three + ssfmin = sqrt( safmin ) / eps2 +c +c compute the eigenvalues and eigenvectors of the tridiagonal +c matrix. +c +c$$ if( icompz.eq.2 ) +c$$$ $ call dlaset( 'full', n, n, zero, one, z, ldz ) +c +c *** New starting with version 2.5 *** +c + if ( icompz .eq. 2 ) then + do 5 j = 1, n-1 + z(j) = zero + 5 continue + z( n ) = one + end if +c ************************************* +c + nmaxit = n*maxit + jtot = 0 +c +c determine where the matrix splits and choose ql or qr iteration +c for each block, according to whether top or bottom diagonal +c element is smaller. +c + l1 = 1 + nm1 = n - 1 +c + 10 continue + if( l1.gt.n ) + $ go to 160 + if( l1.gt.1 ) + $ e( l1-1 ) = zero + if( l1.le.nm1 ) then + do 20 m = l1, nm1 + tst = abs( e( m ) ) + if( tst.eq.zero ) + $ go to 30 + if( tst.le.( sqrt( abs( d( m ) ) )*sqrt( abs( d( m+ + $ 1 ) ) ) )*eps ) then + e( m ) = zero + go to 30 + end if + 20 continue + end if + m = n +c + 30 continue + l = l1 + lsv = l + lend = m + lendsv = lend + l1 = m + 1 + if( lend.eq.l ) + $ go to 10 +c +c scale submatrix in rows and columns l to lend +c + anorm = dlanst( 'i', lend-l+1, d( l ), e( l ) ) + iscale = 0 + if( anorm.eq.zero ) + $ go to 10 + if( anorm.gt.ssfmax ) then + iscale = 1 + call dlascl( 'g', 0, 0, anorm, ssfmax, lend-l+1, 1, d( l ), n, + $ info ) + call dlascl( 'g', 0, 0, anorm, ssfmax, lend-l, 1, e( l ), n, + $ info ) + else if( anorm.lt.ssfmin ) then + iscale = 2 + call dlascl( 'g', 0, 0, anorm, ssfmin, lend-l+1, 1, d( l ), n, + $ info ) + call dlascl( 'g', 0, 0, anorm, ssfmin, lend-l, 1, e( l ), n, + $ info ) + end if +c +c choose between ql and qr iteration +c + if( abs( d( lend ) ).lt.abs( d( l ) ) ) then + lend = lsv + l = lendsv + end if +c + if( lend.gt.l ) then +c +c ql iteration +c +c look for small subdiagonal element. +c + 40 continue + if( l.ne.lend ) then + lendm1 = lend - 1 + do 50 m = l, lendm1 + tst = abs( e( m ) )**2 + if( tst.le.( eps2*abs( d( m ) ) )*abs( d( m+1 ) )+ + $ safmin )go to 60 + 50 continue + end if +c + m = lend +c + 60 continue + if( m.lt.lend ) + $ e( m ) = zero + p = d( l ) + if( m.eq.l ) + $ go to 80 +c +c if remaining matrix is 2-by-2, use dlae2 or dlaev2 +c to compute its eigensystem. +c + if( m.eq.l+1 ) then + if( icompz.gt.0 ) then + call dlaev2( d( l ), e( l ), d( l+1 ), rt1, rt2, c, s ) + work( l ) = c + work( n-1+l ) = s +c$$$ call dlasr( 'r', 'v', 'b', n, 2, work( l ), +c$$$ $ work( n-1+l ), z( 1, l ), ldz ) +c +c *** New starting with version 2.5 *** +c + tst = z(l+1) + z(l+1) = c*tst - s*z(l) + z(l) = s*tst + c*z(l) +c ************************************* + else + call dlae2( d( l ), e( l ), d( l+1 ), rt1, rt2 ) + end if + d( l ) = rt1 + d( l+1 ) = rt2 + e( l ) = zero + l = l + 2 + if( l.le.lend ) + $ go to 40 + go to 140 + end if +c + if( jtot.eq.nmaxit ) + $ go to 140 + jtot = jtot + 1 +c +c form shift. +c + g = ( d( l+1 )-p ) / ( two*e( l ) ) + r = dlapy2( g, one ) + g = d( m ) - p + ( e( l ) / ( g+sign( r, g ) ) ) +c + s = one + c = one + p = zero +c +c inner loop +c + mm1 = m - 1 + do 70 i = mm1, l, -1 + f = s*e( i ) + b = c*e( i ) + call dlartg( g, f, c, s, r ) + if( i.ne.m-1 ) + $ e( i+1 ) = r + g = d( i+1 ) - p + r = ( d( i )-g )*s + two*c*b + p = s*r + d( i+1 ) = g + p + g = c*r - b +c +c if eigenvectors are desired, then save rotations. +c + if( icompz.gt.0 ) then + work( i ) = c + work( n-1+i ) = -s + end if +c + 70 continue +c +c if eigenvectors are desired, then apply saved rotations. +c + if( icompz.gt.0 ) then + mm = m - l + 1 +c$$$ call dlasr( 'r', 'v', 'b', n, mm, work( l ), work( n-1+l ), +c$$$ $ z( 1, l ), ldz ) +c +c *** New starting with version 2.5 *** +c + call dlasr( 'r', 'v', 'b', 1, mm, work( l ), + & work( n-1+l ), z( l ), 1 ) +c ************************************* + end if +c + d( l ) = d( l ) - p + e( l ) = g + go to 40 +c +c eigenvalue found. +c + 80 continue + d( l ) = p +c + l = l + 1 + if( l.le.lend ) + $ go to 40 + go to 140 +c + else +c +c qr iteration +c +c look for small superdiagonal element. +c + 90 continue + if( l.ne.lend ) then + lendp1 = lend + 1 + do 100 m = l, lendp1, -1 + tst = abs( e( m-1 ) )**2 + if( tst.le.( eps2*abs( d( m ) ) )*abs( d( m-1 ) )+ + $ safmin )go to 110 + 100 continue + end if +c + m = lend +c + 110 continue + if( m.gt.lend ) + $ e( m-1 ) = zero + p = d( l ) + if( m.eq.l ) + $ go to 130 +c +c if remaining matrix is 2-by-2, use dlae2 or dlaev2 +c to compute its eigensystem. +c + if( m.eq.l-1 ) then + if( icompz.gt.0 ) then + call dlaev2( d( l-1 ), e( l-1 ), d( l ), rt1, rt2, c, s ) +c$$$ work( m ) = c +c$$$ work( n-1+m ) = s +c$$$ call dlasr( 'r', 'v', 'f', n, 2, work( m ), +c$$$ $ work( n-1+m ), z( 1, l-1 ), ldz ) +c +c *** New starting with version 2.5 *** +c + tst = z(l) + z(l) = c*tst - s*z(l-1) + z(l-1) = s*tst + c*z(l-1) +c ************************************* + else + call dlae2( d( l-1 ), e( l-1 ), d( l ), rt1, rt2 ) + end if + d( l-1 ) = rt1 + d( l ) = rt2 + e( l-1 ) = zero + l = l - 2 + if( l.ge.lend ) + $ go to 90 + go to 140 + end if +c + if( jtot.eq.nmaxit ) + $ go to 140 + jtot = jtot + 1 +c +c form shift. +c + g = ( d( l-1 )-p ) / ( two*e( l-1 ) ) + r = dlapy2( g, one ) + g = d( m ) - p + ( e( l-1 ) / ( g+sign( r, g ) ) ) +c + s = one + c = one + p = zero +c +c inner loop +c + lm1 = l - 1 + do 120 i = m, lm1 + f = s*e( i ) + b = c*e( i ) + call dlartg( g, f, c, s, r ) + if( i.ne.m ) + $ e( i-1 ) = r + g = d( i ) - p + r = ( d( i+1 )-g )*s + two*c*b + p = s*r + d( i ) = g + p + g = c*r - b +c +c if eigenvectors are desired, then save rotations. +c + if( icompz.gt.0 ) then + work( i ) = c + work( n-1+i ) = s + end if +c + 120 continue +c +c if eigenvectors are desired, then apply saved rotations. +c + if( icompz.gt.0 ) then + mm = l - m + 1 +c$$$ call dlasr( 'r', 'v', 'f', n, mm, work( m ), work( n-1+m ), +c$$$ $ z( 1, m ), ldz ) +c +c *** New starting with version 2.5 *** +c + call dlasr( 'r', 'v', 'f', 1, mm, work( m ), work( n-1+m ), + & z( m ), 1 ) +c ************************************* + end if +c + d( l ) = d( l ) - p + e( lm1 ) = g + go to 90 +c +c eigenvalue found. +c + 130 continue + d( l ) = p +c + l = l - 1 + if( l.ge.lend ) + $ go to 90 + go to 140 +c + end if +c +c undo scaling if necessary +c + 140 continue + if( iscale.eq.1 ) then + call dlascl( 'g', 0, 0, ssfmax, anorm, lendsv-lsv+1, 1, + $ d( lsv ), n, info ) + call dlascl( 'g', 0, 0, ssfmax, anorm, lendsv-lsv, 1, e( lsv ), + $ n, info ) + else if( iscale.eq.2 ) then + call dlascl( 'g', 0, 0, ssfmin, anorm, lendsv-lsv+1, 1, + $ d( lsv ), n, info ) + call dlascl( 'g', 0, 0, ssfmin, anorm, lendsv-lsv, 1, e( lsv ), + $ n, info ) + end if +c +c check for no convergence to an eigenvalue after a total +c of n*maxit iterations. +c + if( jtot.lt.nmaxit ) + $ go to 10 + do 150 i = 1, n - 1 + if( e( i ).ne.zero ) + $ info = info + 1 + 150 continue + go to 190 +c +c order eigenvalues and eigenvectors. +c + 160 continue + if( icompz.eq.0 ) then +c +c use quick sort +c + call dlasrt( 'i', n, d, info ) +c + else +c +c use selection sort to minimize swaps of eigenvectors +c + do 180 ii = 2, n + i = ii - 1 + k = i + p = d( i ) + do 170 j = ii, n + if( d( j ).lt.p ) then + k = j + p = d( j ) + end if + 170 continue + if( k.ne.i ) then + d( k ) = d( i ) + d( i ) = p +c$$$ call dswap( n, z( 1, i ), 1, z( 1, k ), 1 ) +c *** New starting with version 2.5 *** +c + p = z(k) + z(k) = z(i) + z(i) = p +c ************************************* + end if + 180 continue + end if +c + 190 continue + return +c +c %---------------% +c | End of igraphdstqrb | +c %---------------% +c + end diff --git a/src/rigraph/vendor/arpack/dvout.f b/src/rigraph/vendor/arpack/dvout.f new file mode 100644 index 0000000..8bd7b1b --- /dev/null +++ b/src/rigraph/vendor/arpack/dvout.f @@ -0,0 +1,122 @@ +*----------------------------------------------------------------------- +* 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 IGRAPHDVOUT( LOUT, N, SX, IDIGIT, IFMT ) +* ... +* ... SPECIFICATIONS FOR ARGUMENTS +* ... +* ... SPECIFICATIONS FOR LOCAL VARIABLES +* .. Scalar Arguments .. + CHARACTER*( * ) IFMT + INTEGER IDIGIT, LOUT, N +* .. +* .. Array Arguments .. + DOUBLE PRECISION SX( * ) +* .. +* .. Local Scalars .. + CHARACTER*80 LINE + INTEGER I, K1, K2, LLL, NDIGIT +* .. +* .. Intrinsic Functions .. + INTRINSIC LEN, MIN, MIN0 +* .. +* .. Executable Statements .. +* ... +* ... FIRST EXECUTABLE STATEMENT +* +* +c$$$ LLL = MIN( LEN( IFMT ), 80 ) +c$$$ DO 10 I = 1, LLL +c$$$ LINE( I: I ) = '-' +c$$$ 10 CONTINUE +c$$$* +c$$$ DO 20 I = LLL + 1, 80 +c$$$ LINE( I: I ) = ' ' +c$$$ 20 CONTINUE +c$$$* +c$$$ WRITE( LOUT, FMT = 9999 )IFMT, LINE( 1: LLL ) +c$$$ 9999 FORMAT( / 1X, A, / 1X, A ) +c$$$* +c$$$ IF( N.LE.0 ) +c$$$ $ RETURN +c$$$ NDIGIT = IDIGIT +c$$$ IF( IDIGIT.EQ.0 ) +c$$$ $ NDIGIT = 4 +c$$$* +c$$$*======================================================================= +c$$$* CODE FOR OUTPUT USING 72 COLUMNS FORMAT +c$$$*======================================================================= +c$$$* +c$$$ IF( IDIGIT.LT.0 ) THEN +c$$$ NDIGIT = -IDIGIT +c$$$ IF( NDIGIT.LE.4 ) THEN +c$$$ DO 30 K1 = 1, N, 5 +c$$$ K2 = MIN0( N, K1+4 ) +c$$$ WRITE( LOUT, FMT = 9998 )K1, K2, ( SX( I ), I = K1, K2 ) +c$$$ 30 CONTINUE +c$$$ ELSE IF( NDIGIT.LE.6 ) THEN +c$$$ DO 40 K1 = 1, N, 4 +c$$$ K2 = MIN0( N, K1+3 ) +c$$$ WRITE( LOUT, FMT = 9997 )K1, K2, ( SX( I ), I = K1, K2 ) +c$$$ 40 CONTINUE +c$$$ ELSE IF( NDIGIT.LE.10 ) THEN +c$$$ DO 50 K1 = 1, N, 3 +c$$$ K2 = MIN0( N, K1+2 ) +c$$$ WRITE( LOUT, FMT = 9996 )K1, K2, ( SX( I ), I = K1, K2 ) +c$$$ 50 CONTINUE +c$$$ ELSE +c$$$ DO 60 K1 = 1, N, 2 +c$$$ K2 = MIN0( N, K1+1 ) +c$$$ WRITE( LOUT, FMT = 9995 )K1, K2, ( SX( I ), I = K1, K2 ) +c$$$ 60 CONTINUE +c$$$ END IF +c$$$* +c$$$*======================================================================= +c$$$* CODE FOR OUTPUT USING 132 COLUMNS FORMAT +c$$$*======================================================================= +c$$$* +c$$$ ELSE +c$$$ IF( NDIGIT.LE.4 ) THEN +c$$$ DO 70 K1 = 1, N, 10 +c$$$ K2 = MIN0( N, K1+9 ) +c$$$ WRITE( LOUT, FMT = 9998 )K1, K2, ( SX( I ), I = K1, K2 ) +c$$$ 70 CONTINUE +c$$$ ELSE IF( NDIGIT.LE.6 ) THEN +c$$$ DO 80 K1 = 1, N, 8 +c$$$ K2 = MIN0( N, K1+7 ) +c$$$ WRITE( LOUT, FMT = 9997 )K1, K2, ( SX( I ), I = K1, K2 ) +c$$$ 80 CONTINUE +c$$$ ELSE IF( NDIGIT.LE.10 ) THEN +c$$$ DO 90 K1 = 1, N, 6 +c$$$ K2 = MIN0( N, K1+5 ) +c$$$ WRITE( LOUT, FMT = 9996 )K1, K2, ( SX( I ), I = K1, K2 ) +c$$$ 90 CONTINUE +c$$$ ELSE +c$$$ DO 100 K1 = 1, N, 5 +c$$$ K2 = MIN0( N, K1+4 ) +c$$$ WRITE( LOUT, FMT = 9995 )K1, K2, ( SX( I ), I = K1, K2 ) +c$$$ 100 CONTINUE +c$$$ END IF +c$$$ END IF +c$$$ WRITE( LOUT, FMT = 9994 ) +c$$$ RETURN +c$$$ 9998 FORMAT( 1X, I4, ' - ', I4, ':', 1P, 10D12.3 ) +c$$$ 9997 FORMAT( 1X, I4, ' - ', I4, ':', 1X, 1P, 8D14.5 ) +c$$$ 9996 FORMAT( 1X, I4, ' - ', I4, ':', 1X, 1P, 6D18.9 ) +c$$$ 9995 FORMAT( 1X, I4, ' - ', I4, ':', 1X, 1P, 5D24.13 ) +c$$$ 9994 FORMAT( 1X, ' ' ) + END diff --git a/src/rigraph/vendor/arpack/ivout.f b/src/rigraph/vendor/arpack/ivout.f new file mode 100644 index 0000000..3f6089c --- /dev/null +++ b/src/rigraph/vendor/arpack/ivout.f @@ -0,0 +1,120 @@ +C----------------------------------------------------------------------- +C Routine: IVOUT +C +C Purpose: Integer vector output routine. +C +C Usage: CALL IVOUT (LOUT, N, IX, IDIGIT, IFMT) +C +C Arguments +C N - Length of array IX. (Input) +C IX - Integer array to be printed. (Input) +C IFMT - Format to be used in printing array IX. (Input) +C IDIGIT - Print up to ABS(IDIGIT) decimal digits / number. (Input) +C If IDIGIT .LT. 0, printing is done with 72 columns. +C If IDIGIT .GT. 0, printing is done with 132 columns. +C +C----------------------------------------------------------------------- +C + SUBROUTINE IGRAPHIVOUT (LOUT, N, IX, IDIGIT, IFMT) +C ... +C ... SPECIFICATIONS FOR ARGUMENTS + INTEGER IX(*), N, IDIGIT, LOUT + CHARACTER IFMT*(*) +C ... +C ... SPECIFICATIONS FOR LOCAL VARIABLES + INTEGER I, NDIGIT, K1, K2, LLL + CHARACTER*80 LINE +* ... +* ... SPECIFICATIONS INTRINSICS + INTRINSIC MIN +* +C +c$$$ LLL = MIN ( LEN ( IFMT ), 80 ) +c$$$ DO 1 I = 1, LLL +c$$$ LINE(I:I) = '-' +c$$$ 1 CONTINUE +c$$$C +c$$$ DO 2 I = LLL+1, 80 +c$$$ LINE(I:I) = ' ' +c$$$ 2 CONTINUE +c$$$C +c$$$ WRITE ( LOUT, 2000 ) IFMT, LINE(1:LLL) +c$$$ 2000 FORMAT ( /1X, A /1X, A ) +c$$$C +c$$$ IF (N .LE. 0) RETURN +c$$$ NDIGIT = IDIGIT +c$$$ IF (IDIGIT .EQ. 0) NDIGIT = 4 +c$$$C +c$$$C======================================================================= +c$$$C CODE FOR OUTPUT USING 72 COLUMNS FORMAT +c$$$C======================================================================= +c$$$C +c$$$ IF (IDIGIT .LT. 0) THEN +c$$$C +c$$$ NDIGIT = -IDIGIT +c$$$ IF (NDIGIT .LE. 4) THEN +c$$$ DO 10 K1 = 1, N, 10 +c$$$ K2 = MIN0(N,K1+9) +c$$$ WRITE(LOUT,1000) K1,K2,(IX(I),I=K1,K2) +c$$$ 10 CONTINUE +c$$$C +c$$$ ELSE IF (NDIGIT .LE. 6) THEN +c$$$ DO 30 K1 = 1, N, 7 +c$$$ K2 = MIN0(N,K1+6) +c$$$ WRITE(LOUT,1001) K1,K2,(IX(I),I=K1,K2) +c$$$ 30 CONTINUE +c$$$C +c$$$ ELSE IF (NDIGIT .LE. 10) THEN +c$$$ DO 50 K1 = 1, N, 5 +c$$$ K2 = MIN0(N,K1+4) +c$$$ WRITE(LOUT,1002) K1,K2,(IX(I),I=K1,K2) +c$$$ 50 CONTINUE +c$$$C +c$$$ ELSE +c$$$ DO 70 K1 = 1, N, 3 +c$$$ K2 = MIN0(N,K1+2) +c$$$ WRITE(LOUT,1003) K1,K2,(IX(I),I=K1,K2) +c$$$ 70 CONTINUE +c$$$ END IF +c$$$C +c$$$C======================================================================= +c$$$C CODE FOR OUTPUT USING 132 COLUMNS FORMAT +c$$$C======================================================================= +c$$$C +c$$$ ELSE +c$$$C +c$$$ IF (NDIGIT .LE. 4) THEN +c$$$ DO 90 K1 = 1, N, 20 +c$$$ K2 = MIN0(N,K1+19) +c$$$ WRITE(LOUT,1000) K1,K2,(IX(I),I=K1,K2) +c$$$ 90 CONTINUE +c$$$C +c$$$ ELSE IF (NDIGIT .LE. 6) THEN +c$$$ DO 110 K1 = 1, N, 15 +c$$$ K2 = MIN0(N,K1+14) +c$$$ WRITE(LOUT,1001) K1,K2,(IX(I),I=K1,K2) +c$$$ 110 CONTINUE +c$$$C +c$$$ ELSE IF (NDIGIT .LE. 10) THEN +c$$$ DO 130 K1 = 1, N, 10 +c$$$ K2 = MIN0(N,K1+9) +c$$$ WRITE(LOUT,1002) K1,K2,(IX(I),I=K1,K2) +c$$$ 130 CONTINUE +c$$$C +c$$$ ELSE +c$$$ DO 150 K1 = 1, N, 7 +c$$$ K2 = MIN0(N,K1+6) +c$$$ WRITE(LOUT,1003) K1,K2,(IX(I),I=K1,K2) +c$$$ 150 CONTINUE +c$$$ END IF +c$$$ END IF +c$$$ WRITE (LOUT,1004) +c$$$C +c$$$ 1000 FORMAT(1X,I4,' - ',I4,':',20(1X,I5)) +c$$$ 1001 FORMAT(1X,I4,' - ',I4,':',15(1X,I7)) +c$$$ 1002 FORMAT(1X,I4,' - ',I4,':',10(1X,I11)) +c$$$ 1003 FORMAT(1X,I4,' - ',I4,':',7(1X,I15)) +c$$$ 1004 FORMAT(1X,' ') +c$$$C + RETURN + END diff --git a/src/rigraph/vendor/arpack/second.f b/src/rigraph/vendor/arpack/second.f new file mode 100644 index 0000000..37023c3 --- /dev/null +++ b/src/rigraph/vendor/arpack/second.f @@ -0,0 +1,35 @@ + SUBROUTINE IGRAPHSECOND( T ) +* + REAL T +* +* -- 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 igraphseconds. +* This version gets the time from the system function ETIME. +* +* .. Local Scalars .. + REAL T1 +* .. +* .. Local Arrays .. + REAL TARRAY( 2 ) +* .. +* .. External Functions .. + REAL ETIME +* .. +* .. Executable Statements .. +* + TARRAY( 1 ) = 0.0 + T1 = ETIME( TARRAY ) + T = TARRAY( 1 ) + + RETURN +* +* End of SECOND +* + END diff --git a/src/rigraph/vendor/arpack/stat.h b/src/rigraph/vendor/arpack/stat.h new file mode 100644 index 0000000..ae407cb --- /dev/null +++ b/src/rigraph/vendor/arpack/stat.h @@ -0,0 +1,21 @@ +c %--------------------------------% +c | See stat.doc for documentation | +c %--------------------------------% +c +c\SCCS Information: @(#) +c FILE: stat.h SID: 2.2 DATE OF SID: 11/16/95 RELEASE: 2 +c + real t0, t1, t2, t3, t4, t5 +c save t0, t1, t2, t3, t4, t5 +c + integer nopx, nbx, nrorth, nitref, nrstrt + real tsaupd, tsaup2, tsaitr, tseigt, tsgets, tsapps, tsconv, + & tnaupd, tnaup2, tnaitr, tneigh, tngets, tnapps, tnconv, + & tcaupd, tcaup2, tcaitr, tceigh, tcgets, tcapps, tcconv, + & tmvopx, tmvbx, tgetv0, titref, trvec + common /timing/ + & nopx, nbx, nrorth, nitref, nrstrt, + & tsaupd, tsaup2, tsaitr, tseigt, tsgets, tsapps, tsconv, + & tnaupd, tnaup2, tnaitr, tneigh, tngets, tnapps, tnconv, + & tcaupd, tcaup2, tcaitr, tceigh, tcgets, tcapps, tcconv, + & tmvopx, tmvbx, tgetv0, titref, trvec diff --git a/src/rigraph/vendor/arpack/wrap.f b/src/rigraph/vendor/arpack/wrap.f new file mode 100644 index 0000000..39c4720 --- /dev/null +++ b/src/rigraph/vendor/arpack/wrap.f @@ -0,0 +1,151 @@ +c----------------------------------------------------------------------- +c Wrapper functions, so we don't need to pass logicals from +c C to Fortran, because that generates LTO warnings, as the compiler +c apparently cannot match a Fortran logical to a C type. +c----------------------------------------------------------------------- +c + subroutine igraphxdsortr (which, apply, n, x1, x2) +c + character*2 which + integer apply + integer n + Double precision + & x1(0:n-1), x2(0:n-1) +c + logical applyx +c + if (apply .eq. 1) then + applyx = .true. + else + applyx = .false. + end if +c + call igraphdsortr(which, applyx, n, x1, x2) +c + return +c + end +c +c----------------------------------------------------------------------- +c + subroutine igraphxdsortc (which, apply, n, xreal, ximag, y) +c + character*2 which + integer apply + integer n +c + Double precision + & xreal(0:n-1), ximag(0:n-1), y(0:n-1) +c + logical applyx +c + if (apply .eq. 1) then + applyx = .true. + else + applyx = .false. + end if +c + call igraphdsortc(which, applyx, n, xreal, ximag, y) +c + return +c + end +c +c----------------------------------------------------------------------- +c + subroutine igraphxdneupd (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) +c + character bmat, howmny, which*2 + integer rvec + integer info, ldz, ldv, lworkl, n, ncv, nev + Double precision + & sigmar, sigmai, tol +c + integer iparam(11), ipntr(14) + integer select(ncv) + Double precision + & dr(nev+1), di(nev+1), resid(n), v(ldv,ncv), z(ldz,*), + & workd(3*n), workl(lworkl), workev(3*ncv) +c + logical rvecx + integer i + logical selectx(ncv) +c + if (rvec .eq. 1) then + rvecx = .true. + else + rvecx = .false. + end if +c + i = 1 + 100 if (i .gt. ncv) then + go to 110 + end if + if (select(i) .eq. 1) then + selectx(i) = .true. + else + selectx(i) = .false. + end if + i = i + 1 + go to 100 + 110 continue +c + call igraphdneupd(rvecx, howmny, selectx, dr, di, z, ldz, + & sigmar, sigmai, workev, bmat, n, which, nev, tol, + & resid, ncv, v, ldv, iparam, ipntr, workd, + & workl, lworkl, info) +c + return +c + end +c +c----------------------------------------------------------------------- +c + subroutine igraphxdseupd (rvec, howmny, select, d, z, ldz, + & sigma, bmat, n, which, nev, tol, resid, ncv, v, ldv, iparam, + & ipntr, workd, workl, lworkl, info ) +c + character bmat, howmny, which*2 + integer rvec, select(ncv) + integer info, ldz, ldv, lworkl, n, ncv, nev + Double precision + & sigma, tol +c + integer iparam(7), ipntr(11) + Double precision + & d(nev), resid(n), v(ldv,ncv), z(ldz, nev), + & workd(2*n), workl(lworkl) +c + logical rvecx + integer i + logical selectx(ncv) +c + if (rvec .eq. 1) then + rvecx = .true. + else + rvecx = .false. + end if +c + i = 1 + 100 if (i .gt. ncv) then + go to 110 + end if + if (select(i) .eq. 1) then + selectx(i) = .true. + else + selectx(i) = .false. + end if + i = i + 1 + go to 100 + 110 continue +c + call igraphdseupd(rvecx, howmny, selectx, d, z, ldz, + & sigma, bmat, n, which, nev, tol, resid, ncv, v, ldv, iparam, + & ipntr, workd, workl, lworkl, info ) +c + return +c + end diff --git a/src/rigraph/vendor/cs/SuiteSparse_config.h b/src/rigraph/vendor/cs/SuiteSparse_config.h new file mode 100644 index 0000000..bd0cced --- /dev/null +++ b/src/rigraph/vendor/cs/SuiteSparse_config.h @@ -0,0 +1,221 @@ +/* ========================================================================== */ +/* === SuiteSparse_config =================================================== */ +/* ========================================================================== */ + +/* Configuration file for SuiteSparse: a Suite of Sparse matrix packages + * (AMD, COLAMD, CCOLAMD, CAMD, CHOLMOD, UMFPACK, CXSparse, and others). + * + * SuiteSparse_config.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 SuiteSparse_config.mk: + * + * CFLAGS = -O -D'SuiteSparse_long=long long' \ + * -D'SuiteSparse_long_max=9223372036854775801' -D'SuiteSparse_long_idd="lld"' + * + * This file defines SuiteSparse_long as either long (on all but _WIN64) or + * __int64 on Windows 64. The intent is that a SuiteSparse_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) 2012, Timothy A. Davis. No licensing restrictions apply + * to this file or to the SuiteSparse_config directory. + * Author: Timothy A. Davis. + */ + +#ifndef SUITESPARSE_CONFIG_H +#define SUITESPARSE_CONFIG_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +/* ========================================================================== */ +/* === SuiteSparse_long ===================================================== */ +/* ========================================================================== */ + +#ifndef SuiteSparse_long + +#ifdef _WIN64 + +#define SuiteSparse_long __int64 +#define SuiteSparse_long_max _I64_MAX +#define SuiteSparse_long_idd "I64d" + +#else + +#define SuiteSparse_long long +#define SuiteSparse_long_max LONG_MAX +#define SuiteSparse_long_idd "ld" + +#endif +#define SuiteSparse_long_id "%" SuiteSparse_long_idd +#endif + +/* Disable unneeded parts for igraph */ +#if 0 /* start comment */ + +/* ========================================================================== */ +/* === SuiteSparse_config parameters and functions ========================== */ +/* ========================================================================== */ + +/* SuiteSparse-wide parameters are placed in this struct. It is meant to be + an extern, globally-accessible struct. It is not meant to be updated + frequently by multiple threads. Rather, if an application needs to modify + SuiteSparse_config, it should do it once at the beginning of the application, + before multiple threads are launched. + + The intent of these function pointers is that they not be used in your + application directly, except to assign them to the desired user-provided + functions. Rather, you should use the + */ + +struct SuiteSparse_config_struct +{ + void *(*malloc_func) (size_t) ; /* pointer to malloc */ + void *(*calloc_func) (size_t, size_t) ; /* pointer to calloc */ + void *(*realloc_func) (void *, size_t) ; /* pointer to realloc */ + void (*free_func) (void *) ; /* pointer to free */ + int (*printf_func) (const char *, ...) ; /* pointer to printf */ + double (*hypot_func) (double, double) ; /* pointer to hypot */ + int (*divcomplex_func) (double, double, double, double, double *, double *); +} ; + +extern struct SuiteSparse_config_struct SuiteSparse_config ; + +void SuiteSparse_start ( void ) ; /* called to start SuiteSparse */ + +void SuiteSparse_finish ( void ) ; /* called to finish SuiteSparse */ + +void *SuiteSparse_malloc /* pointer to allocated block of memory */ +( + size_t nitems, /* number of items to malloc (>=1 is enforced) */ + size_t size_of_item /* sizeof each item */ +) ; + +void *SuiteSparse_calloc /* pointer to allocated block of memory */ +( + size_t nitems, /* number of items to calloc (>=1 is enforced) */ + size_t size_of_item /* sizeof each item */ +) ; + +void *SuiteSparse_realloc /* pointer to reallocated block of memory, or + to original block if the realloc failed. */ +( + size_t nitems_new, /* new number of items in the object */ + size_t nitems_old, /* old number of items in the object */ + size_t size_of_item, /* sizeof each item */ + void *p, /* old object to reallocate */ + int *ok /* 1 if successful, 0 otherwise */ +) ; + +void *SuiteSparse_free /* always returns NULL */ +( + void *p /* block to free */ +) ; + +void SuiteSparse_tic /* start the timer */ +( + double tic [2] /* output, contents undefined on input */ +) ; + +double SuiteSparse_toc /* return time in seconds since last tic */ +( + double tic [2] /* input: from last call to SuiteSparse_tic */ +) ; + +double SuiteSparse_time /* returns current wall clock time in seconds */ +( + void +) ; + +/* returns sqrt (x^2 + y^2), computed reliably */ +double SuiteSparse_hypot (double x, double y) ; + +/* complex division of c = a/b */ +int SuiteSparse_divcomplex +( + double ar, double ai, /* real and imaginary parts of a */ + double br, double bi, /* real and imaginary parts of b */ + double *cr, double *ci /* real and imaginary parts of c */ +) ; + +/* determine which timer to use, if any */ +#ifndef NTIMER +#ifdef _POSIX_C_SOURCE +#if _POSIX_C_SOURCE >= 199309L +#define SUITESPARSE_TIMER_ENABLED +#endif +#endif +#endif + +/* SuiteSparse printf macro */ +#define SUITESPARSE_PRINTF(params) \ +{ \ + if (SuiteSparse_config.printf_func != NULL) \ + { \ + (void) (SuiteSparse_config.printf_func) params ; \ + } \ +} + +/* ========================================================================== */ +/* === 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, which is also the version number of SuiteSparse_config. + */ + +int SuiteSparse_version /* returns SUITESPARSE_VERSION */ +( + /* output, not defined on input. Not used if NULL. Returns + the three version codes in version [0..2]: + version [0] is SUITESPARSE_MAIN_VERSION + version [1] is SUITESPARSE_SUB_VERSION + version [2] is SUITESPARSE_SUBSUB_VERSION + */ + int version [3] +) ; + +/* Versions prior to 4.2.0 do not have the above function. The following + code fragment will work with any version of SuiteSparse: + + #ifdef SUITESPARSE_HAS_VERSION_FUNCTION + v = SuiteSparse_version (NULL) ; + #else + v = SUITESPARSE_VERSION ; + #endif +*/ +#define SUITESPARSE_HAS_VERSION_FUNCTION + +#endif /* end comment */ + +#define SUITESPARSE_DATE "Mar 3, 2021" +#define SUITESPARSE_VER_CODE(main,sub) ((main) * 1000 + (sub)) +#define SUITESPARSE_MAIN_VERSION 5 +#define SUITESPARSE_SUB_VERSION 9 +#define SUITESPARSE_SUBSUB_VERSION 0 +#define SUITESPARSE_VERSION \ + SUITESPARSE_VER_CODE(SUITESPARSE_MAIN_VERSION,SUITESPARSE_SUB_VERSION) + +#ifdef __cplusplus +} +#endif +#endif diff --git a/src/rigraph/vendor/cs/cs.h b/src/rigraph/vendor/cs/cs.h new file mode 100644 index 0000000..0e58521 --- /dev/null +++ b/src/rigraph/vendor/cs/cs.h @@ -0,0 +1,758 @@ +/* ========================================================================== */ +/* CXSparse/Include/cs.h file */ +/* ========================================================================== */ + +/* This is the CXSparse/Include/cs.h file. It has the same name (cs.h) as + the CSparse/Include/cs.h file. The 'make install' for SuiteSparse installs + CXSparse, and this file, instead of CSparse. The two packages have the same + cs.h include filename, because CXSparse is a superset of CSparse. Any user + program that uses CSparse can rely on CXSparse instead, with no change to the + user code. The #include "cs.h" line will work for both versions, in user + code, and the function names and user-visible typedefs from CSparse all + appear in CXSparse. For experimenting and changing the package itself, I + recommend using CSparse since it's simpler and easier to modify. For + using the package in production codes, I recommend CXSparse since it has + more features (support for complex matrices, and both int and long + versions). + */ + +/* ========================================================================== */ + +#ifndef _CXS_H +#define _CXS_H +#include +#include +#include +#include +#ifdef MATLAB_MEX_FILE +#include "mex.h" +#endif + + +#ifdef __cplusplus +#ifndef NCOMPLEX +#include +typedef std::complex cs_complex_t ; +#endif +extern "C" { +#else +#ifndef NCOMPLEX +#include +#define cs_complex_t double _Complex +#endif +#endif + +#define CS_VER 3 /* CXSparse Version */ +#define CS_SUBVER 2 +#define CS_SUBSUB 0 +#define CS_DATE "Sept 12, 2017" /* CSparse release date */ +#define CS_COPYRIGHT "Copyright (c) Timothy A. Davis, 2006-2016" +#define CXSPARSE + +#include "SuiteSparse_config.h" +#define cs_long_t SuiteSparse_long +#define cs_long_t_id SuiteSparse_long_id +#define cs_long_t_max SuiteSparse_long_max + +/* -------------------------------------------------------------------------- */ +/* 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/cs_long_t version of CXSparse */ +/* -------------------------------------------------------------------------- */ + +/* --- primary CSparse routines and data structures ------------------------- */ + +typedef struct cs_dl_sparse /* matrix in compressed-column or triplet form */ +{ + cs_long_t nzmax ; /* maximum number of entries */ + cs_long_t m ; /* number of rows */ + cs_long_t n ; /* number of columns */ + cs_long_t *p ; /* column pointers (size n+1) or col indlces (size nzmax) */ + cs_long_t *i ; /* row indices, size nzmax */ + double *x ; /* numerical values, size nzmax */ + cs_long_t 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) ; +cs_long_t cs_dl_cholsol (cs_long_t order, const cs_dl *A, double *b) ; +cs_long_t cs_dl_dupl (cs_dl *A) ; +cs_long_t cs_dl_entry (cs_dl *T, cs_long_t i, cs_long_t j, double x) ; +cs_long_t cs_dl_lusol (cs_long_t order, const cs_dl *A, double *b, double tol) ; +cs_long_t 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) ; +cs_long_t cs_dl_qrsol (cs_long_t order, const cs_dl *A, double *b) ; +cs_dl *cs_dl_transpose (const cs_dl *A, cs_long_t values) ; +cs_dl *cs_dl_compress (const cs_dl *T) ; +double cs_dl_norm (const cs_dl *A) ; +/*cs_long_t cs_dl_print (const cs_dl *A, cs_long_t brief) ;*/ +cs_dl *cs_dl_load (FILE *f) ; + +/* utilities */ +void *cs_dl_calloc (cs_long_t n, size_t size) ; +void *cs_dl_free (void *p) ; +void *cs_dl_realloc (void *p, cs_long_t n, size_t size, cs_long_t *ok) ; +cs_dl *cs_dl_spalloc (cs_long_t m, cs_long_t n, cs_long_t nzmax, cs_long_t values, + cs_long_t t) ; +cs_dl *cs_dl_spfree (cs_dl *A) ; +cs_long_t cs_dl_sprealloc (cs_dl *A, cs_long_t nzmax) ; +void *cs_dl_malloc (cs_long_t n, size_t size) ; + +/* --- secondary CSparse routines and data structures ----------------------- */ + +typedef struct cs_dl_symbolic /* symbolic Cholesky, LU, or QR analysis */ +{ + cs_long_t *pinv ; /* inverse row perm. for QR, fill red. perm for Chol */ + cs_long_t *q ; /* fill-reducing column permutation for LU and QR */ + cs_long_t *parent ; /* elimination tree for Cholesky and QR */ + cs_long_t *cp ; /* column pointers for Cholesky, row counts for QR */ + cs_long_t *leftmost ; /* leftmost[i] = min(find(A(i,:))), for QR */ + cs_long_t 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 */ + cs_long_t *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 */ +{ + cs_long_t *p ; /* size m, row permutation */ + cs_long_t *q ; /* size n, column permutation */ + cs_long_t *r ; /* size nb+1, block k is rows r[k] to r[k+1]-1 in A(p,q) */ + cs_long_t *s ; /* size nb+1, block k is cols s[k] to s[k+1]-1 in A(p,q) */ + cs_long_t nb ; /* # of blocks in fine dmperm decomposition */ + cs_long_t rr [5] ; /* coarse row decomposition */ + cs_long_t cc [5] ; /* coarse column decomposition */ +} cs_dld ; + +cs_long_t *cs_dl_amd (cs_long_t 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, cs_long_t seed) ; +cs_long_t cs_dl_droptol (cs_dl *A, double tol) ; +cs_long_t cs_dl_dropzeros (cs_dl *A) ; +cs_long_t cs_dl_happly (const cs_dl *V, cs_long_t i, double beta, double *x) ; +cs_long_t cs_dl_ipvec (const cs_long_t *p, const double *b, double *x, cs_long_t n) ; +cs_long_t cs_dl_lsolve (const cs_dl *L, double *x) ; +cs_long_t 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 cs_long_t *pinv, const cs_long_t *q, + cs_long_t values) ; +cs_long_t *cs_dl_pinv (const cs_long_t *p, cs_long_t n) ; +cs_long_t cs_dl_pvec (const cs_long_t *p, const double *b, double *x, cs_long_t n) ; +cs_dln *cs_dl_qr (const cs_dl *A, const cs_dls *S) ; +cs_dls *cs_dl_schol (cs_long_t order, const cs_dl *A) ; +cs_dls *cs_dl_sqr (cs_long_t order, const cs_dl *A, cs_long_t qr) ; +cs_dl *cs_dl_symperm (const cs_dl *A, const cs_long_t *pinv, cs_long_t values) ; +cs_long_t cs_dl_usolve (const cs_dl *U, double *x) ; +cs_long_t cs_dl_utsolve (const cs_dl *U, double *x) ; +cs_long_t cs_dl_updown (cs_dl *L, cs_long_t sigma, const cs_dl *C, + const cs_long_t *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 -------------------------------------------- */ + +cs_long_t *cs_dl_counts (const cs_dl *A, const cs_long_t *parent, + const cs_long_t *post, cs_long_t ata) ; +double cs_dl_cumsum (cs_long_t *p, cs_long_t *c, cs_long_t n) ; +cs_long_t cs_dl_dfs (cs_long_t j, cs_dl *G, cs_long_t top, cs_long_t *xi, + cs_long_t *pstack, const cs_long_t *pinv) ; +cs_long_t *cs_dl_etree (const cs_dl *A, cs_long_t ata) ; +cs_long_t cs_dl_fkeep (cs_dl *A, + cs_long_t (*fkeep) (cs_long_t, cs_long_t, double, void *), void *other) ; +double cs_dl_house (double *x, double *beta, cs_long_t n) ; +cs_long_t *cs_dl_maxtrans (const cs_dl *A, cs_long_t seed) ; +cs_long_t *cs_dl_post (const cs_long_t *parent, cs_long_t n) ; +cs_dld *cs_dl_scc (cs_dl *A) ; +cs_long_t cs_dl_scatter (const cs_dl *A, cs_long_t j, double beta, cs_long_t *w, + double *x, cs_long_t mark,cs_dl *C, cs_long_t nz) ; +cs_long_t cs_dl_tdfs (cs_long_t j, cs_long_t k, cs_long_t *head, const cs_long_t *next, + cs_long_t *post, cs_long_t *stack) ; +cs_long_t cs_dl_leaf (cs_long_t i, cs_long_t j, const cs_long_t *first, + cs_long_t *maxfirst, cs_long_t *prevleaf, cs_long_t *ancestor, cs_long_t *jleaf) ; +cs_long_t cs_dl_reach (cs_dl *G, const cs_dl *B, cs_long_t k, cs_long_t *xi, + const cs_long_t *pinv) ; +cs_long_t cs_dl_spsolve (cs_dl *L, const cs_dl *B, cs_long_t k, cs_long_t *xi, + double *x, const cs_long_t *pinv, cs_long_t lo) ; +cs_long_t cs_dl_ereach (const cs_dl *A, cs_long_t k, const cs_long_t *parent, + cs_long_t *s, cs_long_t *w) ; +cs_long_t *cs_dl_randperm (cs_long_t n, cs_long_t seed) ; + +/* utilities */ +cs_dld *cs_dl_dalloc (cs_long_t m, cs_long_t n) ; +cs_dl *cs_dl_done (cs_dl *C, void *w, void *x, cs_long_t ok) ; +cs_long_t *cs_dl_idone (cs_long_t *p, cs_dl *C, void *w, cs_long_t ok) ; +cs_dln *cs_dl_ndone (cs_dln *N, cs_dl *C, void *w, void *x, cs_long_t ok) ; +cs_dld *cs_dl_ddone (cs_dld *D, cs_dl *C, void *w, cs_long_t 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/cs_long_t version of CXSparse */ +/* -------------------------------------------------------------------------- */ + +/* --- primary CSparse routines and data structures ------------------------- */ + +typedef struct cs_cl_sparse /* matrix in compressed-column or triplet form */ +{ + cs_long_t nzmax ; /* maximum number of entries */ + cs_long_t m ; /* number of rows */ + cs_long_t n ; /* number of columns */ + cs_long_t *p ; /* column pointers (size n+1) or col indlces (size nzmax) */ + cs_long_t *i ; /* row indices, size nzmax */ + cs_complex_t *x ; /* numerical values, size nzmax */ + cs_long_t 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) ; +cs_long_t cs_cl_cholsol (cs_long_t order, const cs_cl *A, cs_complex_t *b) ; +cs_long_t cs_cl_dupl (cs_cl *A) ; +cs_long_t cs_cl_entry (cs_cl *T, cs_long_t i, cs_long_t j, cs_complex_t x) ; +cs_long_t cs_cl_lusol (cs_long_t order, const cs_cl *A, cs_complex_t *b, + double tol) ; +cs_long_t 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) ; +cs_long_t cs_cl_qrsol (cs_long_t order, const cs_cl *A, cs_complex_t *b) ; +cs_cl *cs_cl_transpose (const cs_cl *A, cs_long_t values) ; +cs_cl *cs_cl_compress (const cs_cl *T) ; +double cs_cl_norm (const cs_cl *A) ; +/*cs_long_t cs_cl_print (const cs_cl *A, cs_long_t brief) ;*/ +cs_cl *cs_cl_load (FILE *f) ; + +/* utilities */ +void *cs_cl_calloc (cs_long_t n, size_t size) ; +void *cs_cl_free (void *p) ; +void *cs_cl_realloc (void *p, cs_long_t n, size_t size, cs_long_t *ok) ; +cs_cl *cs_cl_spalloc (cs_long_t m, cs_long_t n, cs_long_t nzmax, cs_long_t values, + cs_long_t t) ; +cs_cl *cs_cl_spfree (cs_cl *A) ; +cs_long_t cs_cl_sprealloc (cs_cl *A, cs_long_t nzmax) ; +void *cs_cl_malloc (cs_long_t n, size_t size) ; + +/* --- secondary CSparse routines and data structures ----------------------- */ + +typedef struct cs_cl_symbolic /* symbolic Cholesky, LU, or QR analysis */ +{ + cs_long_t *pinv ; /* inverse row perm. for QR, fill red. perm for Chol */ + cs_long_t *q ; /* fill-reducing column permutation for LU and QR */ + cs_long_t *parent ; /* elimination tree for Cholesky and QR */ + cs_long_t *cp ; /* column pointers for Cholesky, row counts for QR */ + cs_long_t *leftmost ; /* leftmost[i] = min(find(A(i,:))), for QR */ + cs_long_t 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 */ + cs_long_t *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 */ +{ + cs_long_t *p ; /* size m, row permutation */ + cs_long_t *q ; /* size n, column permutation */ + cs_long_t *r ; /* size nb+1, block k is rows r[k] to r[k+1]-1 in A(p,q) */ + cs_long_t *s ; /* size nb+1, block k is cols s[k] to s[k+1]-1 in A(p,q) */ + cs_long_t nb ; /* # of blocks in fine dmperm decomposition */ + cs_long_t rr [5] ; /* coarse row decomposition */ + cs_long_t cc [5] ; /* coarse column decomposition */ +} cs_cld ; + +cs_long_t *cs_cl_amd (cs_long_t 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, cs_long_t seed) ; +cs_long_t cs_cl_droptol (cs_cl *A, double tol) ; +cs_long_t cs_cl_dropzeros (cs_cl *A) ; +cs_long_t cs_cl_happly (const cs_cl *V, cs_long_t i, double beta, cs_complex_t *x) ; +cs_long_t cs_cl_ipvec (const cs_long_t *p, const cs_complex_t *b, + cs_complex_t *x, cs_long_t n) ; +cs_long_t cs_cl_lsolve (const cs_cl *L, cs_complex_t *x) ; +cs_long_t 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 cs_long_t *pinv, const cs_long_t *q, + cs_long_t values) ; +cs_long_t *cs_cl_pinv (const cs_long_t *p, cs_long_t n) ; +cs_long_t cs_cl_pvec (const cs_long_t *p, const cs_complex_t *b, + cs_complex_t *x, cs_long_t n) ; +cs_cln *cs_cl_qr (const cs_cl *A, const cs_cls *S) ; +cs_cls *cs_cl_schol (cs_long_t order, const cs_cl *A) ; +cs_cls *cs_cl_sqr (cs_long_t order, const cs_cl *A, cs_long_t qr) ; +cs_cl *cs_cl_symperm (const cs_cl *A, const cs_long_t *pinv, cs_long_t values) ; +cs_long_t cs_cl_usolve (const cs_cl *U, cs_complex_t *x) ; +cs_long_t cs_cl_utsolve (const cs_cl *U, cs_complex_t *x) ; +cs_long_t cs_cl_updown (cs_cl *L, cs_long_t sigma, const cs_cl *C, + const cs_long_t *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 -------------------------------------------- */ + +cs_long_t *cs_cl_counts (const cs_cl *A, const cs_long_t *parent, + const cs_long_t *post, cs_long_t ata) ; +double cs_cl_cumsum (cs_long_t *p, cs_long_t *c, cs_long_t n) ; +cs_long_t cs_cl_dfs (cs_long_t j, cs_cl *G, cs_long_t top, cs_long_t *xi, + cs_long_t *pstack, const cs_long_t *pinv) ; +cs_long_t *cs_cl_etree (const cs_cl *A, cs_long_t ata) ; +cs_long_t cs_cl_fkeep (cs_cl *A, + cs_long_t (*fkeep) (cs_long_t, cs_long_t, cs_complex_t, void *), void *other) ; +cs_complex_t cs_cl_house (cs_complex_t *x, double *beta, cs_long_t n) ; +cs_long_t *cs_cl_maxtrans (const cs_cl *A, cs_long_t seed) ; +cs_long_t *cs_cl_post (const cs_long_t *parent, cs_long_t n) ; +cs_cld *cs_cl_scc (cs_cl *A) ; +cs_long_t cs_cl_scatter (const cs_cl *A, cs_long_t j, cs_complex_t beta, + cs_long_t *w, cs_complex_t *x, cs_long_t mark,cs_cl *C, cs_long_t nz) ; +cs_long_t cs_cl_tdfs (cs_long_t j, cs_long_t k, cs_long_t *head, const cs_long_t *next, + cs_long_t *post, cs_long_t *stack) ; +cs_long_t cs_cl_leaf (cs_long_t i, cs_long_t j, const cs_long_t *first, + cs_long_t *maxfirst, cs_long_t *prevleaf, cs_long_t *ancestor, cs_long_t *jleaf) ; +cs_long_t cs_cl_reach (cs_cl *G, const cs_cl *B, cs_long_t k, cs_long_t *xi, + const cs_long_t *pinv) ; +cs_long_t cs_cl_spsolve (cs_cl *L, const cs_cl *B, cs_long_t k, cs_long_t *xi, + cs_complex_t *x, const cs_long_t *pinv, cs_long_t lo) ; +cs_long_t cs_cl_ereach (const cs_cl *A, cs_long_t k, const cs_long_t *parent, + cs_long_t *s, cs_long_t *w) ; +cs_long_t *cs_cl_randperm (cs_long_t n, cs_long_t seed) ; + +/* utilities */ +cs_cld *cs_cl_dalloc (cs_long_t m, cs_long_t n) ; +cs_cl *cs_cl_done (cs_cl *C, void *w, void *x, cs_long_t ok) ; +cs_long_t *cs_cl_idone (cs_long_t *p, cs_cl *C, void *w, cs_long_t ok) ; +cs_cln *cs_cl_ndone (cs_cln *N, cs_cl *C, void *w, void *x, cs_long_t ok) ; +cs_cld *cs_cl_ddone (cs_cld *D, cs_cl *C, void *w, cs_long_t ok) ; + +#endif + +/* -------------------------------------------------------------------------- */ +/* Macros for constructing each version of CSparse */ +/* -------------------------------------------------------------------------- */ + +#ifdef CS_LONG +#define CS_INT cs_long_t +#define CS_INT_MAX cs_long_t_max +#define CS_ID cs_long_t_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, cs_long_t real) ; +cs_cl *cs_l_complex (cs_dl *A, cs_long_t real) ; +#endif + +#ifdef __cplusplus +} +#endif +#endif diff --git a/src/rigraph/vendor/cs/cs_add.c b/src/rigraph/vendor/cs/cs_add.c new file mode 100644 index 0000000..44b0d3f --- /dev/null +++ b/src/rigraph/vendor/cs/cs_add.c @@ -0,0 +1,28 @@ +#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/src/rigraph/vendor/cs/cs_amd.c b/src/rigraph/vendor/cs/cs_amd.c new file mode 100644 index 0000000..3f5c702 --- /dev/null +++ b/src/rigraph/vendor/cs/cs_amd.c @@ -0,0 +1,364 @@ +#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 ; + 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 = ((h<0) ? (-h):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/src/rigraph/vendor/cs/cs_chol.c b/src/rigraph/vendor/cs/cs_chol.c new file mode 100644 index 0000000..535809a --- /dev/null +++ b/src/rigraph/vendor/cs/cs_chol.c @@ -0,0 +1,59 @@ +#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/src/rigraph/vendor/cs/cs_cholsol.c b/src/rigraph/vendor/cs/cs_cholsol.c new file mode 100644 index 0000000..6e7dc4f --- /dev/null +++ b/src/rigraph/vendor/cs/cs_cholsol.c @@ -0,0 +1,26 @@ +#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/src/rigraph/vendor/cs/cs_compress.c b/src/rigraph/vendor/cs/cs_compress.c new file mode 100644 index 0000000..dc62eba --- /dev/null +++ b/src/rigraph/vendor/cs/cs_compress.c @@ -0,0 +1,22 @@ +#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/src/rigraph/vendor/cs/cs_counts.c b/src/rigraph/vendor/cs/cs_counts.c new file mode 100644 index 0000000..a1b3de0 --- /dev/null +++ b/src/rigraph/vendor/cs/cs_counts.c @@ -0,0 +1,61 @@ +#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/src/rigraph/vendor/cs/cs_cumsum.c b/src/rigraph/vendor/cs/cs_cumsum.c new file mode 100644 index 0000000..e839497 --- /dev/null +++ b/src/rigraph/vendor/cs/cs_cumsum.c @@ -0,0 +1,17 @@ +#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/src/rigraph/vendor/cs/cs_dfs.c b/src/rigraph/vendor/cs/cs_dfs.c new file mode 100644 index 0000000..6c7115d --- /dev/null +++ b/src/rigraph/vendor/cs/cs_dfs.c @@ -0,0 +1,36 @@ +#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/src/rigraph/vendor/cs/cs_dmperm.c b/src/rigraph/vendor/cs/cs_dmperm.c new file mode 100644 index 0000000..e213845 --- /dev/null +++ b/src/rigraph/vendor/cs/cs_dmperm.c @@ -0,0 +1,144 @@ +#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/src/rigraph/vendor/cs/cs_droptol.c b/src/rigraph/vendor/cs/cs_droptol.c new file mode 100644 index 0000000..59b8df2 --- /dev/null +++ b/src/rigraph/vendor/cs/cs_droptol.c @@ -0,0 +1,9 @@ +#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/src/rigraph/vendor/cs/cs_dropzeros.c b/src/rigraph/vendor/cs/cs_dropzeros.c new file mode 100644 index 0000000..d93f605 --- /dev/null +++ b/src/rigraph/vendor/cs/cs_dropzeros.c @@ -0,0 +1,9 @@ +#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/src/rigraph/vendor/cs/cs_dupl.c b/src/rigraph/vendor/cs/cs_dupl.c new file mode 100644 index 0000000..fdf2e1e --- /dev/null +++ b/src/rigraph/vendor/cs/cs_dupl.c @@ -0,0 +1,34 @@ +#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/src/rigraph/vendor/cs/cs_entry.c b/src/rigraph/vendor/cs/cs_entry.c new file mode 100644 index 0000000..f712ba7 --- /dev/null +++ b/src/rigraph/vendor/cs/cs_entry.c @@ -0,0 +1,13 @@ +#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/src/rigraph/vendor/cs/cs_ereach.c b/src/rigraph/vendor/cs/cs_ereach.c new file mode 100644 index 0000000..9edad52 --- /dev/null +++ b/src/rigraph/vendor/cs/cs_ereach.c @@ -0,0 +1,23 @@ +#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/src/rigraph/vendor/cs/cs_etree.c b/src/rigraph/vendor/cs/cs_etree.c new file mode 100644 index 0000000..5620928 --- /dev/null +++ b/src/rigraph/vendor/cs/cs_etree.c @@ -0,0 +1,30 @@ +#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/src/rigraph/vendor/cs/cs_fkeep.c b/src/rigraph/vendor/cs/cs_fkeep.c new file mode 100644 index 0000000..09219e8 --- /dev/null +++ b/src/rigraph/vendor/cs/cs_fkeep.c @@ -0,0 +1,25 @@ +#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/src/rigraph/vendor/cs/cs_gaxpy.c b/src/rigraph/vendor/cs/cs_gaxpy.c new file mode 100644 index 0000000..db93cbc --- /dev/null +++ b/src/rigraph/vendor/cs/cs_gaxpy.c @@ -0,0 +1,17 @@ +#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/src/rigraph/vendor/cs/cs_happly.c b/src/rigraph/vendor/cs/cs_happly.c new file mode 100644 index 0000000..98a306c --- /dev/null +++ b/src/rigraph/vendor/cs/cs_happly.c @@ -0,0 +1,19 @@ +#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/src/rigraph/vendor/cs/cs_house.c b/src/rigraph/vendor/cs/cs_house.c new file mode 100644 index 0000000..e825c89 --- /dev/null +++ b/src/rigraph/vendor/cs/cs_house.c @@ -0,0 +1,30 @@ +#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/src/rigraph/vendor/cs/cs_ipvec.c b/src/rigraph/vendor/cs/cs_ipvec.c new file mode 100644 index 0000000..4935ace --- /dev/null +++ b/src/rigraph/vendor/cs/cs_ipvec.c @@ -0,0 +1,9 @@ +#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/src/rigraph/vendor/cs/cs_leaf.c b/src/rigraph/vendor/cs/cs_leaf.c new file mode 100644 index 0000000..bd93bda --- /dev/null +++ b/src/rigraph/vendor/cs/cs_leaf.c @@ -0,0 +1,22 @@ +#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/src/rigraph/vendor/cs/cs_load.c b/src/rigraph/vendor/cs/cs_load.c new file mode 100644 index 0000000..91e1f37 --- /dev/null +++ b/src/rigraph/vendor/cs/cs_load.c @@ -0,0 +1,26 @@ +#include "cs.h" +/* load a triplet matrix from a file */ +cs *cs_load (FILE *f) +{ + double i, j ; /* use double for integers to avoid csi conflicts */ + 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, "%lg %lg %lg %lg\n", &i, &j, &x, &xi) == 4) +#else + while (fscanf (f, "%lg %lg %lg\n", &i, &j, &x) == 3) +#endif + { +#ifdef CS_COMPLEX + if (!cs_entry (T, (CS_INT) i, (CS_INT) j, x + xi*I)) return (cs_spfree (T)) ; +#else + if (!cs_entry (T, (CS_INT) i, (CS_INT) j, x)) return (cs_spfree (T)) ; +#endif + } + return (T) ; +} diff --git a/src/rigraph/vendor/cs/cs_lsolve.c b/src/rigraph/vendor/cs/cs_lsolve.c new file mode 100644 index 0000000..099b0c5 --- /dev/null +++ b/src/rigraph/vendor/cs/cs_lsolve.c @@ -0,0 +1,18 @@ +#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/src/rigraph/vendor/cs/cs_ltsolve.c b/src/rigraph/vendor/cs/cs_ltsolve.c new file mode 100644 index 0000000..29b1ca2 --- /dev/null +++ b/src/rigraph/vendor/cs/cs_ltsolve.c @@ -0,0 +1,18 @@ +#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/src/rigraph/vendor/cs/cs_lu.c b/src/rigraph/vendor/cs/cs_lu.c new file mode 100644 index 0000000..270172c --- /dev/null +++ b/src/rigraph/vendor/cs/cs_lu.c @@ -0,0 +1,88 @@ +#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)) ; + /* tol=1 for partial pivoting; tol<1 gives preference to diagonal */ + 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/src/rigraph/vendor/cs/cs_lusol.c b/src/rigraph/vendor/cs/cs_lusol.c new file mode 100644 index 0000000..e0727e2 --- /dev/null +++ b/src/rigraph/vendor/cs/cs_lusol.c @@ -0,0 +1,26 @@ +#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/src/rigraph/vendor/cs/cs_malloc.c b/src/rigraph/vendor/cs/cs_malloc.c new file mode 100644 index 0000000..2a3f6da --- /dev/null +++ b/src/rigraph/vendor/cs/cs_malloc.c @@ -0,0 +1,35 @@ +#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/src/rigraph/vendor/cs/cs_maxtrans.c b/src/rigraph/vendor/cs/cs_maxtrans.c new file mode 100644 index 0000000..4947cee --- /dev/null +++ b/src/rigraph/vendor/cs/cs_maxtrans.c @@ -0,0 +1,92 @@ +#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/src/rigraph/vendor/cs/cs_multiply.c b/src/rigraph/vendor/cs/cs_multiply.c new file mode 100644 index 0000000..34e3a36 --- /dev/null +++ b/src/rigraph/vendor/cs/cs_multiply.c @@ -0,0 +1,35 @@ +#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/src/rigraph/vendor/cs/cs_norm.c b/src/rigraph/vendor/cs/cs_norm.c new file mode 100644 index 0000000..0e7b3c6 --- /dev/null +++ b/src/rigraph/vendor/cs/cs_norm.c @@ -0,0 +1,16 @@ +#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/src/rigraph/vendor/cs/cs_permute.c b/src/rigraph/vendor/cs/cs_permute.c new file mode 100644 index 0000000..9adae45 --- /dev/null +++ b/src/rigraph/vendor/cs/cs_permute.c @@ -0,0 +1,25 @@ +#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/src/rigraph/vendor/cs/cs_pinv.c b/src/rigraph/vendor/cs/cs_pinv.c new file mode 100644 index 0000000..de0660e --- /dev/null +++ b/src/rigraph/vendor/cs/cs_pinv.c @@ -0,0 +1,11 @@ +#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/src/rigraph/vendor/cs/cs_post.c b/src/rigraph/vendor/cs/cs_post.c new file mode 100644 index 0000000..0f61203 --- /dev/null +++ b/src/rigraph/vendor/cs/cs_post.c @@ -0,0 +1,24 @@ +#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/src/rigraph/vendor/cs/cs_print.c b/src/rigraph/vendor/cs/cs_print.c new file mode 100644 index 0000000..7e7b16d --- /dev/null +++ b/src/rigraph/vendor/cs/cs_print.c @@ -0,0 +1,55 @@ +#include "cs.h" +/* print a sparse matrix; use %g for integers to avoid differences with CS_INT */ + +/* Disabled for igraph as it prints to stdio */ +#if 0 +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 ("%g-by-%g, nzmax: %g nnz: %g, 1-norm: %g\n", (double) m, + (double) n, (double) nzmax, (double) (Ap [n]), cs_norm (A)) ; + for (j = 0 ; j < n ; j++) + { + printf (" col %g : locations %g to %g\n", (double) j, + (double) (Ap [j]), (double) (Ap [j+1]-1)) ; + for (p = Ap [j] ; p < Ap [j+1] ; p++) + { + printf (" %g : ", (double) (Ai [p])) ; +#ifdef CS_COMPLEX + printf ("(%g, %g)\n", + Ax ? CS_REAL (Ax [p]) : 1, Ax ? CS_IMAG (Ax [p]) : 0) ; +#else + printf ("%g\n", Ax ? Ax [p] : 1) ; +#endif + if (brief && p > 20) { printf (" ...\n") ; return (1) ; } + } + } + } + else + { + printf ("triplet: %g-by-%g, nzmax: %g nnz: %g\n", (double) m, + (double) n, (double) nzmax, (double) nz) ; + for (p = 0 ; p < nz ; p++) + { + + printf (" %g %g : ", (double) (Ai [p]), (double) (Ap [p])) ; +#ifdef CS_COMPLEX + printf ("(%g, %g)\n", + Ax ? CS_REAL (Ax [p]) : 1, Ax ? CS_IMAG (Ax [p]) : 0) ; +#else + printf ("%g\n", Ax ? Ax [p] : 1) ; +#endif + if (brief && p > 20) { printf (" ...\n") ; return (1) ; } + } + } + return (1) ; +} +#endif diff --git a/src/rigraph/vendor/cs/cs_pvec.c b/src/rigraph/vendor/cs/cs_pvec.c new file mode 100644 index 0000000..1254c2a --- /dev/null +++ b/src/rigraph/vendor/cs/cs_pvec.c @@ -0,0 +1,9 @@ +#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/src/rigraph/vendor/cs/cs_qr.c b/src/rigraph/vendor/cs/cs_qr.c new file mode 100644 index 0000000..8bce32e --- /dev/null +++ b/src/rigraph/vendor/cs/cs_qr.c @@ -0,0 +1,74 @@ +#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, 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) ; + 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/src/rigraph/vendor/cs/cs_qrsol.c b/src/rigraph/vendor/cs/cs_qrsol.c new file mode 100644 index 0000000..d817ef2 --- /dev/null +++ b/src/rigraph/vendor/cs/cs_qrsol.c @@ -0,0 +1,53 @@ +#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/src/rigraph/vendor/cs/cs_randperm.c b/src/rigraph/vendor/cs/cs_randperm.c new file mode 100644 index 0000000..4570da0 --- /dev/null +++ b/src/rigraph/vendor/cs/cs_randperm.c @@ -0,0 +1,28 @@ +#include "cs.h" + +#include "igraph_random.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 = 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/src/rigraph/vendor/cs/cs_reach.c b/src/rigraph/vendor/cs/cs_reach.c new file mode 100644 index 0000000..0efb342 --- /dev/null +++ b/src/rigraph/vendor/cs/cs_reach.c @@ -0,0 +1,19 @@ +#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/src/rigraph/vendor/cs/cs_scatter.c b/src/rigraph/vendor/cs/cs_scatter.c new file mode 100644 index 0000000..734fdb2 --- /dev/null +++ b/src/rigraph/vendor/cs/cs_scatter.c @@ -0,0 +1,22 @@ +#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/src/rigraph/vendor/cs/cs_scc.c b/src/rigraph/vendor/cs/cs_scc.c new file mode 100644 index 0000000..cc6d805 --- /dev/null +++ b/src/rigraph/vendor/cs/cs_scc.c @@ -0,0 +1,41 @@ +#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/src/rigraph/vendor/cs/cs_schol.c b/src/rigraph/vendor/cs/cs_schol.c new file mode 100644 index 0000000..7da2a57 --- /dev/null +++ b/src/rigraph/vendor/cs/cs_schol.c @@ -0,0 +1,26 @@ +#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/src/rigraph/vendor/cs/cs_spsolve.c b/src/rigraph/vendor/cs/cs_spsolve.c new file mode 100644 index 0000000..8c6ecce --- /dev/null +++ b/src/rigraph/vendor/cs/cs_spsolve.c @@ -0,0 +1,28 @@ +#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/src/rigraph/vendor/cs/cs_sqr.c b/src/rigraph/vendor/cs/cs_sqr.c new file mode 100644 index 0000000..1b14ca4 --- /dev/null +++ b/src/rigraph/vendor/cs/cs_sqr.c @@ -0,0 +1,87 @@ +#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] ; + 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/src/rigraph/vendor/cs/cs_symperm.c b/src/rigraph/vendor/cs/cs_symperm.c new file mode 100644 index 0000000..7bbd6fe --- /dev/null +++ b/src/rigraph/vendor/cs/cs_symperm.c @@ -0,0 +1,39 @@ +#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/src/rigraph/vendor/cs/cs_tdfs.c b/src/rigraph/vendor/cs/cs_tdfs.c new file mode 100644 index 0000000..f32077b --- /dev/null +++ b/src/rigraph/vendor/cs/cs_tdfs.c @@ -0,0 +1,24 @@ +#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/src/rigraph/vendor/cs/cs_transpose.c b/src/rigraph/vendor/cs/cs_transpose.c new file mode 100644 index 0000000..15a6c00 --- /dev/null +++ b/src/rigraph/vendor/cs/cs_transpose.c @@ -0,0 +1,25 @@ +#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/src/rigraph/vendor/cs/cs_updown.c b/src/rigraph/vendor/cs/cs_updown.c new file mode 100644 index 0000000..e49af83 --- /dev/null +++ b/src/rigraph/vendor/cs/cs_updown.c @@ -0,0 +1,48 @@ +#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/src/rigraph/vendor/cs/cs_usolve.c b/src/rigraph/vendor/cs/cs_usolve.c new file mode 100644 index 0000000..6e89c83 --- /dev/null +++ b/src/rigraph/vendor/cs/cs_usolve.c @@ -0,0 +1,18 @@ +#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/src/rigraph/vendor/cs/cs_util.c b/src/rigraph/vendor/cs/cs_util.c new file mode 100644 index 0000000..bb887ea --- /dev/null +++ b/src/rigraph/vendor/cs/cs_util.c @@ -0,0 +1,120 @@ +#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 ; + nzmax = CS_MAX (nzmax, 1) ; + 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 *) 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 ((csn *) 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 ((css *) 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 ((csd *) cs_free (D)) ; /* free the csd struct and return NULL */ +} + +/* 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_INT *) cs_free (p)) ; /* return result, or 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/src/rigraph/vendor/cs/cs_utsolve.c b/src/rigraph/vendor/cs/cs_utsolve.c new file mode 100644 index 0000000..d879fae --- /dev/null +++ b/src/rigraph/vendor/cs/cs_utsolve.c @@ -0,0 +1,18 @@ +#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/src/rigraph/vendor/mini-gmp/mini-gmp.c b/src/rigraph/vendor/mini-gmp/mini-gmp.c new file mode 100644 index 0000000..76a691b --- /dev/null +++ b/src/rigraph/vendor/mini-gmp/mini-gmp.c @@ -0,0 +1,4578 @@ +/* mini-gmp, a minimalistic implementation of a GNU GMP subset. + + Contributed to the GNU project by Niels Möller + +Copyright 1991-1997, 1999-2019 Free Software Foundation, Inc. + +This file is part of the GNU MP Library. + +The GNU MP Library is free software; you can redistribute it and/or modify +it under the terms of either: + + * the GNU Lesser General Public License as published by the Free + Software Foundation; either version 3 of the License, or (at your + option) any later version. + +or + + * the GNU General Public License as published by the Free Software + Foundation; either version 2 of the License, or (at your option) any + later version. + +or both in parallel, as here. + +The GNU MP Library is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received copies of the GNU General Public License and the +GNU Lesser General Public License along with the GNU MP Library. If not, +see https://www.gnu.org/licenses/. */ + +/* NOTE: All functions in this file which are not declared in + mini-gmp.h are internal, and are not intended to be compatible + with GMP or with future versions of mini-gmp. */ + +/* Much of the material copied from GMP files, including: gmp-impl.h, + longlong.h, mpn/generic/add_n.c, mpn/generic/addmul_1.c, + mpn/generic/lshift.c, mpn/generic/mul_1.c, + mpn/generic/mul_basecase.c, mpn/generic/rshift.c, + mpn/generic/sbpi1_div_qr.c, mpn/generic/sub_n.c, + mpn/generic/submul_1.c. */ + +#include +#include +#include +#include +#include +#include + +#include "mini-gmp.h" + +#if !defined(MINI_GMP_DONT_USE_FLOAT_H) +#include +#endif + +#include "igraph_error.h" + + +/* Macros */ +#define GMP_LIMB_BITS (sizeof(mp_limb_t) * CHAR_BIT) + +#define GMP_LIMB_MAX ((mp_limb_t) ~ (mp_limb_t) 0) +#define GMP_LIMB_HIGHBIT ((mp_limb_t) 1 << (GMP_LIMB_BITS - 1)) + +#define GMP_HLIMB_BIT ((mp_limb_t) 1 << (GMP_LIMB_BITS / 2)) +#define GMP_LLIMB_MASK (GMP_HLIMB_BIT - 1) + +#define GMP_ULONG_BITS (sizeof(unsigned long) * CHAR_BIT) +#define GMP_ULONG_HIGHBIT ((unsigned long) 1 << (GMP_ULONG_BITS - 1)) + +#define GMP_ABS(x) ((x) >= 0 ? (x) : -(x)) +#define GMP_NEG_CAST(T,x) (-((T)((x) + 1) - 1)) + +#define GMP_MIN(a, b) ((a) < (b) ? (a) : (b)) +#define GMP_MAX(a, b) ((a) > (b) ? (a) : (b)) + +#define GMP_CMP(a,b) (((a) > (b)) - ((a) < (b))) + +#if defined(DBL_MANT_DIG) && FLT_RADIX == 2 +#define GMP_DBL_MANT_BITS DBL_MANT_DIG +#else +#define GMP_DBL_MANT_BITS (53) +#endif + +/* Return non-zero if xp,xsize and yp,ysize overlap. + If xp+xsize<=yp there's no overlap, or if yp+ysize<=xp there's no + overlap. If both these are false, there's an overlap. */ +#define GMP_MPN_OVERLAP_P(xp, xsize, yp, ysize) \ + ((xp) + (xsize) > (yp) && (yp) + (ysize) > (xp)) + +#define gmp_assert_nocarry(x) do { \ + mp_limb_t __cy = (x); \ + assert (__cy == 0); \ + } while (0) + +#define gmp_clz(count, x) do { \ + mp_limb_t __clz_x = (x); \ + unsigned __clz_c = 0; \ + int LOCAL_SHIFT_BITS = 8; \ + if (GMP_LIMB_BITS > LOCAL_SHIFT_BITS) \ + for (; \ + (__clz_x & ((mp_limb_t) 0xff << (GMP_LIMB_BITS - 8))) == 0; \ + __clz_c += 8) \ + { __clz_x <<= LOCAL_SHIFT_BITS; } \ + for (; (__clz_x & GMP_LIMB_HIGHBIT) == 0; __clz_c++) \ + __clz_x <<= 1; \ + (count) = __clz_c; \ + } while (0) + +#define gmp_ctz(count, x) do { \ + mp_limb_t __ctz_x = (x); \ + unsigned __ctz_c = 0; \ + gmp_clz (__ctz_c, __ctz_x & - __ctz_x); \ + (count) = GMP_LIMB_BITS - 1 - __ctz_c; \ + } while (0) + +#define gmp_add_ssaaaa(sh, sl, ah, al, bh, bl) \ + do { \ + mp_limb_t __x; \ + __x = (al) + (bl); \ + (sh) = (ah) + (bh) + (__x < (al)); \ + (sl) = __x; \ + } while (0) + +#define gmp_sub_ddmmss(sh, sl, ah, al, bh, bl) \ + do { \ + mp_limb_t __x; \ + __x = (al) - (bl); \ + (sh) = (ah) - (bh) - ((al) < (bl)); \ + (sl) = __x; \ + } while (0) + +#define gmp_umul_ppmm(w1, w0, u, v) \ + do { \ + int LOCAL_GMP_LIMB_BITS = GMP_LIMB_BITS; \ + if (sizeof(unsigned int) * CHAR_BIT >= 2 * GMP_LIMB_BITS) \ + { \ + unsigned int __ww = (unsigned int) (u) * (v); \ + w0 = (mp_limb_t) __ww; \ + w1 = (mp_limb_t) (__ww >> LOCAL_GMP_LIMB_BITS); \ + } \ + else if (GMP_ULONG_BITS >= 2 * GMP_LIMB_BITS) \ + { \ + unsigned long int __ww = (unsigned long int) (u) * (v); \ + w0 = (mp_limb_t) __ww; \ + w1 = (mp_limb_t) (__ww >> LOCAL_GMP_LIMB_BITS); \ + } \ + else { \ + mp_limb_t __x0, __x1, __x2, __x3; \ + unsigned __ul, __vl, __uh, __vh; \ + mp_limb_t __u = (u), __v = (v); \ + \ + __ul = __u & GMP_LLIMB_MASK; \ + __uh = __u >> (GMP_LIMB_BITS / 2); \ + __vl = __v & GMP_LLIMB_MASK; \ + __vh = __v >> (GMP_LIMB_BITS / 2); \ + \ + __x0 = (mp_limb_t) __ul * __vl; \ + __x1 = (mp_limb_t) __ul * __vh; \ + __x2 = (mp_limb_t) __uh * __vl; \ + __x3 = (mp_limb_t) __uh * __vh; \ + \ + __x1 += __x0 >> (GMP_LIMB_BITS / 2);/* this can't give carry */ \ + __x1 += __x2; /* but this indeed can */ \ + if (__x1 < __x2) /* did we get it? */ \ + __x3 += GMP_HLIMB_BIT; /* yes, add it in the proper pos. */ \ + \ + (w1) = __x3 + (__x1 >> (GMP_LIMB_BITS / 2)); \ + (w0) = (__x1 << (GMP_LIMB_BITS / 2)) + (__x0 & GMP_LLIMB_MASK); \ + } \ + } while (0) + +#define gmp_udiv_qrnnd_preinv(q, r, nh, nl, d, di) \ + do { \ + mp_limb_t _qh, _ql, _r, _mask; \ + gmp_umul_ppmm (_qh, _ql, (nh), (di)); \ + gmp_add_ssaaaa (_qh, _ql, _qh, _ql, (nh) + 1, (nl)); \ + _r = (nl) - _qh * (d); \ + _mask = -(mp_limb_t) (_r > _ql); /* both > and >= are OK */ \ + _qh += _mask; \ + _r += _mask & (d); \ + if (_r >= (d)) \ + { \ + _r -= (d); \ + _qh++; \ + } \ + \ + (r) = _r; \ + (q) = _qh; \ + } while (0) + +#define gmp_udiv_qr_3by2(q, r1, r0, n2, n1, n0, d1, d0, dinv) \ + do { \ + mp_limb_t _q0, _t1, _t0, _mask; \ + gmp_umul_ppmm ((q), _q0, (n2), (dinv)); \ + gmp_add_ssaaaa ((q), _q0, (q), _q0, (n2), (n1)); \ + \ + /* Compute the two most significant limbs of n - q'd */ \ + (r1) = (n1) - (d1) * (q); \ + gmp_sub_ddmmss ((r1), (r0), (r1), (n0), (d1), (d0)); \ + gmp_umul_ppmm (_t1, _t0, (d0), (q)); \ + gmp_sub_ddmmss ((r1), (r0), (r1), (r0), _t1, _t0); \ + (q)++; \ + \ + /* Conditionally adjust q and the remainders */ \ + _mask = - (mp_limb_t) ((r1) >= _q0); \ + (q) += _mask; \ + gmp_add_ssaaaa ((r1), (r0), (r1), (r0), _mask & (d1), _mask & (d0)); \ + if ((r1) >= (d1)) \ + { \ + if ((r1) > (d1) || (r0) >= (d0)) \ + { \ + (q)++; \ + gmp_sub_ddmmss ((r1), (r0), (r1), (r0), (d1), (d0)); \ + } \ + } \ + } while (0) + +/* Swap macros. */ +#define MP_LIMB_T_SWAP(x, y) \ + do { \ + mp_limb_t __mp_limb_t_swap__tmp = (x); \ + (x) = (y); \ + (y) = __mp_limb_t_swap__tmp; \ + } while (0) +#define MP_SIZE_T_SWAP(x, y) \ + do { \ + mp_size_t __mp_size_t_swap__tmp = (x); \ + (x) = (y); \ + (y) = __mp_size_t_swap__tmp; \ + } while (0) +#define MP_BITCNT_T_SWAP(x,y) \ + do { \ + mp_bitcnt_t __mp_bitcnt_t_swap__tmp = (x); \ + (x) = (y); \ + (y) = __mp_bitcnt_t_swap__tmp; \ + } while (0) +#define MP_PTR_SWAP(x, y) \ + do { \ + mp_ptr __mp_ptr_swap__tmp = (x); \ + (x) = (y); \ + (y) = __mp_ptr_swap__tmp; \ + } while (0) +#define MP_SRCPTR_SWAP(x, y) \ + do { \ + mp_srcptr __mp_srcptr_swap__tmp = (x); \ + (x) = (y); \ + (y) = __mp_srcptr_swap__tmp; \ + } while (0) + +#define MPN_PTR_SWAP(xp,xs, yp,ys) \ + do { \ + MP_PTR_SWAP (xp, yp); \ + MP_SIZE_T_SWAP (xs, ys); \ + } while(0) +#define MPN_SRCPTR_SWAP(xp,xs, yp,ys) \ + do { \ + MP_SRCPTR_SWAP (xp, yp); \ + MP_SIZE_T_SWAP (xs, ys); \ + } while(0) + +#define MPZ_PTR_SWAP(x, y) \ + do { \ + mpz_ptr __mpz_ptr_swap__tmp = (x); \ + (x) = (y); \ + (y) = __mpz_ptr_swap__tmp; \ + } while (0) +#define MPZ_SRCPTR_SWAP(x, y) \ + do { \ + mpz_srcptr __mpz_srcptr_swap__tmp = (x); \ + (x) = (y); \ + (y) = __mpz_srcptr_swap__tmp; \ + } while (0) + +const int mp_bits_per_limb = GMP_LIMB_BITS; + + +/* Memory allocation and other helper functions. */ +static void +gmp_die (const char *msg) +{ + /* + fprintf (stderr, "%s\n", msg); + abort(); + */ + IGRAPH_FATAL(msg); +} + +static void * +gmp_default_alloc (size_t size) +{ + void *p; + + assert (size > 0); + + p = malloc (size); + if (!p) + gmp_die("gmp_default_alloc: Virtual memory exhausted."); + + return p; +} + +static void * +gmp_default_realloc (void *old, size_t unused_old_size, size_t new_size) +{ + void * p; + + p = realloc (old, new_size); + + if (!p) + gmp_die("gmp_default_realloc: Virtual memory exhausted."); + + return p; +} + +static void +gmp_default_free (void *p, size_t unused_size) +{ + free (p); +} + +static void * (*gmp_allocate_func) (size_t) = gmp_default_alloc; +static void * (*gmp_reallocate_func) (void *, size_t, size_t) = gmp_default_realloc; +static void (*gmp_free_func) (void *, size_t) = gmp_default_free; + +void +mp_get_memory_functions (void *(**alloc_func) (size_t), + void *(**realloc_func) (void *, size_t, size_t), + void (**free_func) (void *, size_t)) +{ + if (alloc_func) + *alloc_func = gmp_allocate_func; + + if (realloc_func) + *realloc_func = gmp_reallocate_func; + + if (free_func) + *free_func = gmp_free_func; +} + +void +mp_set_memory_functions (void *(*alloc_func) (size_t), + void *(*realloc_func) (void *, size_t, size_t), + void (*free_func) (void *, size_t)) +{ + if (!alloc_func) + alloc_func = gmp_default_alloc; + if (!realloc_func) + realloc_func = gmp_default_realloc; + if (!free_func) + free_func = gmp_default_free; + + gmp_allocate_func = alloc_func; + gmp_reallocate_func = realloc_func; + gmp_free_func = free_func; +} + +#define gmp_xalloc(size) ((*gmp_allocate_func)((size))) +#define gmp_free(p) ((*gmp_free_func) ((p), 0)) + +static mp_ptr +gmp_xalloc_limbs (mp_size_t size) +{ + return (mp_ptr) gmp_xalloc (size * sizeof (mp_limb_t)); +} + +static mp_ptr +gmp_xrealloc_limbs (mp_ptr old, mp_size_t size) +{ + assert (size > 0); + return (mp_ptr) (*gmp_reallocate_func) (old, 0, size * sizeof (mp_limb_t)); +} + + +/* MPN interface */ + +void +mpn_copyi (mp_ptr d, mp_srcptr s, mp_size_t n) +{ + mp_size_t i; + for (i = 0; i < n; i++) + d[i] = s[i]; +} + +void +mpn_copyd (mp_ptr d, mp_srcptr s, mp_size_t n) +{ + while (--n >= 0) + d[n] = s[n]; +} + +int +mpn_cmp (mp_srcptr ap, mp_srcptr bp, mp_size_t n) +{ + while (--n >= 0) + { + if (ap[n] != bp[n]) + return ap[n] > bp[n] ? 1 : -1; + } + return 0; +} + +static int +mpn_cmp4 (mp_srcptr ap, mp_size_t an, mp_srcptr bp, mp_size_t bn) +{ + if (an != bn) + return an < bn ? -1 : 1; + else + return mpn_cmp (ap, bp, an); +} + +static mp_size_t +mpn_normalized_size (mp_srcptr xp, mp_size_t n) +{ + while (n > 0 && xp[n-1] == 0) + --n; + return n; +} + +int +mpn_zero_p(mp_srcptr rp, mp_size_t n) +{ + return mpn_normalized_size (rp, n) == 0; +} + +void +mpn_zero (mp_ptr rp, mp_size_t n) +{ + while (--n >= 0) + rp[n] = 0; +} + +mp_limb_t +mpn_add_1 (mp_ptr rp, mp_srcptr ap, mp_size_t n, mp_limb_t b) +{ + mp_size_t i; + + assert (n > 0); + i = 0; + do + { + mp_limb_t r = ap[i] + b; + /* Carry out */ + b = (r < b); + rp[i] = r; + } + while (++i < n); + + return b; +} + +mp_limb_t +mpn_add_n (mp_ptr rp, mp_srcptr ap, mp_srcptr bp, mp_size_t n) +{ + mp_size_t i; + mp_limb_t cy; + + for (i = 0, cy = 0; i < n; i++) + { + mp_limb_t a, b, r; + a = ap[i]; b = bp[i]; + r = a + cy; + cy = (r < cy); + r += b; + cy += (r < b); + rp[i] = r; + } + return cy; +} + +mp_limb_t +mpn_add (mp_ptr rp, mp_srcptr ap, mp_size_t an, mp_srcptr bp, mp_size_t bn) +{ + mp_limb_t cy; + + assert (an >= bn); + + cy = mpn_add_n (rp, ap, bp, bn); + if (an > bn) + cy = mpn_add_1 (rp + bn, ap + bn, an - bn, cy); + return cy; +} + +mp_limb_t +mpn_sub_1 (mp_ptr rp, mp_srcptr ap, mp_size_t n, mp_limb_t b) +{ + mp_size_t i; + + assert (n > 0); + + i = 0; + do + { + mp_limb_t a = ap[i]; + /* Carry out */ + mp_limb_t cy = a < b; + rp[i] = a - b; + b = cy; + } + while (++i < n); + + return b; +} + +mp_limb_t +mpn_sub_n (mp_ptr rp, mp_srcptr ap, mp_srcptr bp, mp_size_t n) +{ + mp_size_t i; + mp_limb_t cy; + + for (i = 0, cy = 0; i < n; i++) + { + mp_limb_t a, b; + a = ap[i]; b = bp[i]; + b += cy; + cy = (b < cy); + cy += (a < b); + rp[i] = a - b; + } + return cy; +} + +mp_limb_t +mpn_sub (mp_ptr rp, mp_srcptr ap, mp_size_t an, mp_srcptr bp, mp_size_t bn) +{ + mp_limb_t cy; + + assert (an >= bn); + + cy = mpn_sub_n (rp, ap, bp, bn); + if (an > bn) + cy = mpn_sub_1 (rp + bn, ap + bn, an - bn, cy); + return cy; +} + +mp_limb_t +mpn_mul_1 (mp_ptr rp, mp_srcptr up, mp_size_t n, mp_limb_t vl) +{ + mp_limb_t ul, cl, hpl, lpl; + + assert (n >= 1); + + cl = 0; + do + { + ul = *up++; + gmp_umul_ppmm (hpl, lpl, ul, vl); + + lpl += cl; + cl = (lpl < cl) + hpl; + + *rp++ = lpl; + } + while (--n != 0); + + return cl; +} + +mp_limb_t +mpn_addmul_1 (mp_ptr rp, mp_srcptr up, mp_size_t n, mp_limb_t vl) +{ + mp_limb_t ul, cl, hpl, lpl, rl; + + assert (n >= 1); + + cl = 0; + do + { + ul = *up++; + gmp_umul_ppmm (hpl, lpl, ul, vl); + + lpl += cl; + cl = (lpl < cl) + hpl; + + rl = *rp; + lpl = rl + lpl; + cl += lpl < rl; + *rp++ = lpl; + } + while (--n != 0); + + return cl; +} + +mp_limb_t +mpn_submul_1 (mp_ptr rp, mp_srcptr up, mp_size_t n, mp_limb_t vl) +{ + mp_limb_t ul, cl, hpl, lpl, rl; + + assert (n >= 1); + + cl = 0; + do + { + ul = *up++; + gmp_umul_ppmm (hpl, lpl, ul, vl); + + lpl += cl; + cl = (lpl < cl) + hpl; + + rl = *rp; + lpl = rl - lpl; + cl += lpl > rl; + *rp++ = lpl; + } + while (--n != 0); + + return cl; +} + +mp_limb_t +mpn_mul (mp_ptr rp, mp_srcptr up, mp_size_t un, mp_srcptr vp, mp_size_t vn) +{ + assert (un >= vn); + assert (vn >= 1); + assert (!GMP_MPN_OVERLAP_P(rp, un + vn, up, un)); + assert (!GMP_MPN_OVERLAP_P(rp, un + vn, vp, vn)); + + /* We first multiply by the low order limb. This result can be + stored, not added, to rp. We also avoid a loop for zeroing this + way. */ + + rp[un] = mpn_mul_1 (rp, up, un, vp[0]); + + /* Now accumulate the product of up[] and the next higher limb from + vp[]. */ + + while (--vn >= 1) + { + rp += 1, vp += 1; + rp[un] = mpn_addmul_1 (rp, up, un, vp[0]); + } + return rp[un]; +} + +void +mpn_mul_n (mp_ptr rp, mp_srcptr ap, mp_srcptr bp, mp_size_t n) +{ + mpn_mul (rp, ap, n, bp, n); +} + +void +mpn_sqr (mp_ptr rp, mp_srcptr ap, mp_size_t n) +{ + mpn_mul (rp, ap, n, ap, n); +} + +mp_limb_t +mpn_lshift (mp_ptr rp, mp_srcptr up, mp_size_t n, unsigned int cnt) +{ + mp_limb_t high_limb, low_limb; + unsigned int tnc; + mp_limb_t retval; + + assert (n >= 1); + assert (cnt >= 1); + assert (cnt < GMP_LIMB_BITS); + + up += n; + rp += n; + + tnc = GMP_LIMB_BITS - cnt; + low_limb = *--up; + retval = low_limb >> tnc; + high_limb = (low_limb << cnt); + + while (--n != 0) + { + low_limb = *--up; + *--rp = high_limb | (low_limb >> tnc); + high_limb = (low_limb << cnt); + } + *--rp = high_limb; + + return retval; +} + +mp_limb_t +mpn_rshift (mp_ptr rp, mp_srcptr up, mp_size_t n, unsigned int cnt) +{ + mp_limb_t high_limb, low_limb; + unsigned int tnc; + mp_limb_t retval; + + assert (n >= 1); + assert (cnt >= 1); + assert (cnt < GMP_LIMB_BITS); + + tnc = GMP_LIMB_BITS - cnt; + high_limb = *up++; + retval = (high_limb << tnc); + low_limb = high_limb >> cnt; + + while (--n != 0) + { + high_limb = *up++; + *rp++ = low_limb | (high_limb << tnc); + low_limb = high_limb >> cnt; + } + *rp = low_limb; + + return retval; +} + +static mp_bitcnt_t +mpn_common_scan (mp_limb_t limb, mp_size_t i, mp_srcptr up, mp_size_t un, + mp_limb_t ux) +{ + unsigned cnt; + + assert (ux == 0 || ux == GMP_LIMB_MAX); + assert (0 <= i && i <= un ); + + while (limb == 0) + { + i++; + if (i == un) + return (ux == 0 ? ~(mp_bitcnt_t) 0 : un * GMP_LIMB_BITS); + limb = ux ^ up[i]; + } + gmp_ctz (cnt, limb); + return (mp_bitcnt_t) i * GMP_LIMB_BITS + cnt; +} + +mp_bitcnt_t +mpn_scan1 (mp_srcptr ptr, mp_bitcnt_t bit) +{ + mp_size_t i; + i = bit / GMP_LIMB_BITS; + + return mpn_common_scan ( ptr[i] & (GMP_LIMB_MAX << (bit % GMP_LIMB_BITS)), + i, ptr, i, 0); +} + +mp_bitcnt_t +mpn_scan0 (mp_srcptr ptr, mp_bitcnt_t bit) +{ + mp_size_t i; + i = bit / GMP_LIMB_BITS; + + return mpn_common_scan (~ptr[i] & (GMP_LIMB_MAX << (bit % GMP_LIMB_BITS)), + i, ptr, i, GMP_LIMB_MAX); +} + +void +mpn_com (mp_ptr rp, mp_srcptr up, mp_size_t n) +{ + while (--n >= 0) + *rp++ = ~ *up++; +} + +mp_limb_t +mpn_neg (mp_ptr rp, mp_srcptr up, mp_size_t n) +{ + while (*up == 0) + { + *rp = 0; + if (!--n) + return 0; + ++up; ++rp; + } + *rp = - *up; + mpn_com (++rp, ++up, --n); + return 1; +} + + +/* MPN division interface. */ + +/* The 3/2 inverse is defined as + + m = floor( (B^3-1) / (B u1 + u0)) - B +*/ +mp_limb_t +mpn_invert_3by2 (mp_limb_t u1, mp_limb_t u0) +{ + mp_limb_t r, m; + + { + mp_limb_t p, ql; + unsigned ul, uh, qh; + + /* For notation, let b denote the half-limb base, so that B = b^2. + Split u1 = b uh + ul. */ + ul = u1 & GMP_LLIMB_MASK; + uh = u1 >> (GMP_LIMB_BITS / 2); + + /* Approximation of the high half of quotient. Differs from the 2/1 + inverse of the half limb uh, since we have already subtracted + u0. */ + qh = (u1 ^ GMP_LIMB_MAX) / uh; + + /* Adjust to get a half-limb 3/2 inverse, i.e., we want + + qh' = floor( (b^3 - 1) / u) - b = floor ((b^3 - b u - 1) / u + = floor( (b (~u) + b-1) / u), + + and the remainder + + r = b (~u) + b-1 - qh (b uh + ul) + = b (~u - qh uh) + b-1 - qh ul + + Subtraction of qh ul may underflow, which implies adjustments. + But by normalization, 2 u >= B > qh ul, so we need to adjust by + at most 2. + */ + + r = ((~u1 - (mp_limb_t) qh * uh) << (GMP_LIMB_BITS / 2)) | GMP_LLIMB_MASK; + + p = (mp_limb_t) qh * ul; + /* Adjustment steps taken from udiv_qrnnd_c */ + if (r < p) + { + qh--; + r += u1; + if (r >= u1) /* i.e. we didn't get carry when adding to r */ + if (r < p) + { + qh--; + r += u1; + } + } + r -= p; + + /* Low half of the quotient is + + ql = floor ( (b r + b-1) / u1). + + This is a 3/2 division (on half-limbs), for which qh is a + suitable inverse. */ + + p = (r >> (GMP_LIMB_BITS / 2)) * qh + r; + /* Unlike full-limb 3/2, we can add 1 without overflow. For this to + work, it is essential that ql is a full mp_limb_t. */ + ql = (p >> (GMP_LIMB_BITS / 2)) + 1; + + /* By the 3/2 trick, we don't need the high half limb. */ + r = (r << (GMP_LIMB_BITS / 2)) + GMP_LLIMB_MASK - ql * u1; + + if (r >= (GMP_LIMB_MAX & (p << (GMP_LIMB_BITS / 2)))) + { + ql--; + r += u1; + } + m = ((mp_limb_t) qh << (GMP_LIMB_BITS / 2)) + ql; + if (r >= u1) + { + m++; + r -= u1; + } + } + + /* Now m is the 2/1 inverse of u1. If u0 > 0, adjust it to become a + 3/2 inverse. */ + if (u0 > 0) + { + mp_limb_t th, tl; + r = ~r; + r += u0; + if (r < u0) + { + m--; + if (r >= u1) + { + m--; + r -= u1; + } + r -= u1; + } + gmp_umul_ppmm (th, tl, u0, m); + r += th; + if (r < th) + { + m--; + m -= ((r > u1) | ((r == u1) & (tl > u0))); + } + } + + return m; +} + +struct gmp_div_inverse +{ + /* Normalization shift count. */ + unsigned shift; + /* Normalized divisor (d0 unused for mpn_div_qr_1) */ + mp_limb_t d1, d0; + /* Inverse, for 2/1 or 3/2. */ + mp_limb_t di; +}; + +static void +mpn_div_qr_1_invert (struct gmp_div_inverse *inv, mp_limb_t d) +{ + unsigned shift; + + assert (d > 0); + gmp_clz (shift, d); + inv->shift = shift; + inv->d1 = d << shift; + inv->di = mpn_invert_limb (inv->d1); +} + +static void +mpn_div_qr_2_invert (struct gmp_div_inverse *inv, + mp_limb_t d1, mp_limb_t d0) +{ + unsigned shift; + + assert (d1 > 0); + gmp_clz (shift, d1); + inv->shift = shift; + if (shift > 0) + { + d1 = (d1 << shift) | (d0 >> (GMP_LIMB_BITS - shift)); + d0 <<= shift; + } + inv->d1 = d1; + inv->d0 = d0; + inv->di = mpn_invert_3by2 (d1, d0); +} + +static void +mpn_div_qr_invert (struct gmp_div_inverse *inv, + mp_srcptr dp, mp_size_t dn) +{ + assert (dn > 0); + + if (dn == 1) + mpn_div_qr_1_invert (inv, dp[0]); + else if (dn == 2) + mpn_div_qr_2_invert (inv, dp[1], dp[0]); + else + { + unsigned shift; + mp_limb_t d1, d0; + + d1 = dp[dn-1]; + d0 = dp[dn-2]; + assert (d1 > 0); + gmp_clz (shift, d1); + inv->shift = shift; + if (shift > 0) + { + d1 = (d1 << shift) | (d0 >> (GMP_LIMB_BITS - shift)); + d0 = (d0 << shift) | (dp[dn-3] >> (GMP_LIMB_BITS - shift)); + } + inv->d1 = d1; + inv->d0 = d0; + inv->di = mpn_invert_3by2 (d1, d0); + } +} + +/* Not matching current public gmp interface, rather corresponding to + the sbpi1_div_* functions. */ +static mp_limb_t +mpn_div_qr_1_preinv (mp_ptr qp, mp_srcptr np, mp_size_t nn, + const struct gmp_div_inverse *inv) +{ + mp_limb_t d, di; + mp_limb_t r; + mp_ptr tp = NULL; + + if (inv->shift > 0) + { + /* Shift, reusing qp area if possible. In-place shift if qp == np. */ + tp = qp ? qp : gmp_xalloc_limbs (nn); + r = mpn_lshift (tp, np, nn, inv->shift); + np = tp; + } + else + r = 0; + + d = inv->d1; + di = inv->di; + while (--nn >= 0) + { + mp_limb_t q; + + gmp_udiv_qrnnd_preinv (q, r, r, np[nn], d, di); + if (qp) + qp[nn] = q; + } + if ((inv->shift > 0) && (tp != qp)) + gmp_free (tp); + + return r >> inv->shift; +} + +static void +mpn_div_qr_2_preinv (mp_ptr qp, mp_ptr np, mp_size_t nn, + const struct gmp_div_inverse *inv) +{ + unsigned shift; + mp_size_t i; + mp_limb_t d1, d0, di, r1, r0; + + assert (nn >= 2); + shift = inv->shift; + d1 = inv->d1; + d0 = inv->d0; + di = inv->di; + + if (shift > 0) + r1 = mpn_lshift (np, np, nn, shift); + else + r1 = 0; + + r0 = np[nn - 1]; + + i = nn - 2; + do + { + mp_limb_t n0, q; + n0 = np[i]; + gmp_udiv_qr_3by2 (q, r1, r0, r1, r0, n0, d1, d0, di); + + if (qp) + qp[i] = q; + } + while (--i >= 0); + + if (shift > 0) + { + assert ((r0 & (GMP_LIMB_MAX >> (GMP_LIMB_BITS - shift))) == 0); + r0 = (r0 >> shift) | (r1 << (GMP_LIMB_BITS - shift)); + r1 >>= shift; + } + + np[1] = r1; + np[0] = r0; +} + +static void +mpn_div_qr_pi1 (mp_ptr qp, + mp_ptr np, mp_size_t nn, mp_limb_t n1, + mp_srcptr dp, mp_size_t dn, + mp_limb_t dinv) +{ + mp_size_t i; + + mp_limb_t d1, d0; + mp_limb_t cy, cy1; + mp_limb_t q; + + assert (dn > 2); + assert (nn >= dn); + + d1 = dp[dn - 1]; + d0 = dp[dn - 2]; + + assert ((d1 & GMP_LIMB_HIGHBIT) != 0); + /* Iteration variable is the index of the q limb. + * + * We divide + * by + */ + + i = nn - dn; + do + { + mp_limb_t n0 = np[dn-1+i]; + + if (n1 == d1 && n0 == d0) + { + q = GMP_LIMB_MAX; + mpn_submul_1 (np+i, dp, dn, q); + n1 = np[dn-1+i]; /* update n1, last loop's value will now be invalid */ + } + else + { + gmp_udiv_qr_3by2 (q, n1, n0, n1, n0, np[dn-2+i], d1, d0, dinv); + + cy = mpn_submul_1 (np + i, dp, dn-2, q); + + cy1 = n0 < cy; + n0 = n0 - cy; + cy = n1 < cy1; + n1 = n1 - cy1; + np[dn-2+i] = n0; + + if (cy != 0) + { + n1 += d1 + mpn_add_n (np + i, np + i, dp, dn - 1); + q--; + } + } + + if (qp) + qp[i] = q; + } + while (--i >= 0); + + np[dn - 1] = n1; +} + +static void +mpn_div_qr_preinv (mp_ptr qp, mp_ptr np, mp_size_t nn, + mp_srcptr dp, mp_size_t dn, + const struct gmp_div_inverse *inv) +{ + assert (dn > 0); + assert (nn >= dn); + + if (dn == 1) + np[0] = mpn_div_qr_1_preinv (qp, np, nn, inv); + else if (dn == 2) + mpn_div_qr_2_preinv (qp, np, nn, inv); + else + { + mp_limb_t nh; + unsigned shift; + + assert (inv->d1 == dp[dn-1]); + assert (inv->d0 == dp[dn-2]); + assert ((inv->d1 & GMP_LIMB_HIGHBIT) != 0); + + shift = inv->shift; + if (shift > 0) + nh = mpn_lshift (np, np, nn, shift); + else + nh = 0; + + mpn_div_qr_pi1 (qp, np, nn, nh, dp, dn, inv->di); + + if (shift > 0) + gmp_assert_nocarry (mpn_rshift (np, np, dn, shift)); + } +} + +static void +mpn_div_qr (mp_ptr qp, mp_ptr np, mp_size_t nn, mp_srcptr dp, mp_size_t dn) +{ + struct gmp_div_inverse inv; + mp_ptr tp = NULL; + + assert (dn > 0); + assert (nn >= dn); + + mpn_div_qr_invert (&inv, dp, dn); + if (dn > 2 && inv.shift > 0) + { + tp = gmp_xalloc_limbs (dn); + gmp_assert_nocarry (mpn_lshift (tp, dp, dn, inv.shift)); + dp = tp; + } + mpn_div_qr_preinv (qp, np, nn, dp, dn, &inv); + if (tp) + gmp_free (tp); +} + + +/* MPN base conversion. */ +static unsigned +mpn_base_power_of_two_p (unsigned b) +{ + switch (b) + { + case 2: return 1; + case 4: return 2; + case 8: return 3; + case 16: return 4; + case 32: return 5; + case 64: return 6; + case 128: return 7; + case 256: return 8; + default: return 0; + } +} + +struct mpn_base_info +{ + /* bb is the largest power of the base which fits in one limb, and + exp is the corresponding exponent. */ + unsigned exp; + mp_limb_t bb; +}; + +static void +mpn_get_base_info (struct mpn_base_info *info, mp_limb_t b) +{ + mp_limb_t m; + mp_limb_t p; + unsigned exp; + + m = GMP_LIMB_MAX / b; + for (exp = 1, p = b; p <= m; exp++) + p *= b; + + info->exp = exp; + info->bb = p; +} + +static mp_bitcnt_t +mpn_limb_size_in_base_2 (mp_limb_t u) +{ + unsigned shift; + + assert (u > 0); + gmp_clz (shift, u); + return GMP_LIMB_BITS - shift; +} + +static size_t +mpn_get_str_bits (unsigned char *sp, unsigned bits, mp_srcptr up, mp_size_t un) +{ + unsigned char mask; + size_t sn, j; + mp_size_t i; + unsigned shift; + + sn = ((un - 1) * GMP_LIMB_BITS + mpn_limb_size_in_base_2 (up[un-1]) + + bits - 1) / bits; + + mask = (1U << bits) - 1; + + for (i = 0, j = sn, shift = 0; j-- > 0;) + { + unsigned char digit = up[i] >> shift; + + shift += bits; + + if (shift >= GMP_LIMB_BITS && ++i < un) + { + shift -= GMP_LIMB_BITS; + digit |= up[i] << (bits - shift); + } + sp[j] = digit & mask; + } + return sn; +} + +/* We generate digits from the least significant end, and reverse at + the end. */ +static size_t +mpn_limb_get_str (unsigned char *sp, mp_limb_t w, + const struct gmp_div_inverse *binv) +{ + mp_size_t i; + for (i = 0; w > 0; i++) + { + mp_limb_t h, l, r; + + h = w >> (GMP_LIMB_BITS - binv->shift); + l = w << binv->shift; + + gmp_udiv_qrnnd_preinv (w, r, h, l, binv->d1, binv->di); + assert ((r & (GMP_LIMB_MAX >> (GMP_LIMB_BITS - binv->shift))) == 0); + r >>= binv->shift; + + sp[i] = r; + } + return i; +} + +static size_t +mpn_get_str_other (unsigned char *sp, + int base, const struct mpn_base_info *info, + mp_ptr up, mp_size_t un) +{ + struct gmp_div_inverse binv; + size_t sn; + size_t i; + + mpn_div_qr_1_invert (&binv, base); + + sn = 0; + + if (un > 1) + { + struct gmp_div_inverse bbinv; + mpn_div_qr_1_invert (&bbinv, info->bb); + + do + { + mp_limb_t w; + size_t done; + w = mpn_div_qr_1_preinv (up, up, un, &bbinv); + un -= (up[un-1] == 0); + done = mpn_limb_get_str (sp + sn, w, &binv); + + for (sn += done; done < info->exp; done++) + sp[sn++] = 0; + } + while (un > 1); + } + sn += mpn_limb_get_str (sp + sn, up[0], &binv); + + /* Reverse order */ + for (i = 0; 2*i + 1 < sn; i++) + { + unsigned char t = sp[i]; + sp[i] = sp[sn - i - 1]; + sp[sn - i - 1] = t; + } + + return sn; +} + +size_t +mpn_get_str (unsigned char *sp, int base, mp_ptr up, mp_size_t un) +{ + unsigned bits; + + assert (un > 0); + assert (up[un-1] > 0); + + bits = mpn_base_power_of_two_p (base); + if (bits) + return mpn_get_str_bits (sp, bits, up, un); + else + { + struct mpn_base_info info; + + mpn_get_base_info (&info, base); + return mpn_get_str_other (sp, base, &info, up, un); + } +} + +static mp_size_t +mpn_set_str_bits (mp_ptr rp, const unsigned char *sp, size_t sn, + unsigned bits) +{ + mp_size_t rn; + size_t j; + unsigned shift; + + for (j = sn, rn = 0, shift = 0; j-- > 0; ) + { + if (shift == 0) + { + rp[rn++] = sp[j]; + shift += bits; + } + else + { + rp[rn-1] |= (mp_limb_t) sp[j] << shift; + shift += bits; + if (shift >= GMP_LIMB_BITS) + { + shift -= GMP_LIMB_BITS; + if (shift > 0) + rp[rn++] = (mp_limb_t) sp[j] >> (bits - shift); + } + } + } + rn = mpn_normalized_size (rp, rn); + return rn; +} + +/* Result is usually normalized, except for all-zero input, in which + case a single zero limb is written at *RP, and 1 is returned. */ +static mp_size_t +mpn_set_str_other (mp_ptr rp, const unsigned char *sp, size_t sn, + mp_limb_t b, const struct mpn_base_info *info) +{ + mp_size_t rn; + mp_limb_t w; + unsigned k; + size_t j; + + assert (sn > 0); + + k = 1 + (sn - 1) % info->exp; + + j = 0; + w = sp[j++]; + while (--k != 0) + w = w * b + sp[j++]; + + rp[0] = w; + + for (rn = 1; j < sn;) + { + mp_limb_t cy; + + w = sp[j++]; + for (k = 1; k < info->exp; k++) + w = w * b + sp[j++]; + + cy = mpn_mul_1 (rp, rp, rn, info->bb); + cy += mpn_add_1 (rp, rp, rn, w); + if (cy > 0) + rp[rn++] = cy; + } + assert (j == sn); + + return rn; +} + +mp_size_t +mpn_set_str (mp_ptr rp, const unsigned char *sp, size_t sn, int base) +{ + unsigned bits; + + if (sn == 0) + return 0; + + bits = mpn_base_power_of_two_p (base); + if (bits) + return mpn_set_str_bits (rp, sp, sn, bits); + else + { + struct mpn_base_info info; + + mpn_get_base_info (&info, base); + return mpn_set_str_other (rp, sp, sn, base, &info); + } +} + + +/* MPZ interface */ +void +mpz_init (mpz_t r) +{ + static const mp_limb_t dummy_limb = GMP_LIMB_MAX & 0xc1a0; + + r->_mp_alloc = 0; + r->_mp_size = 0; + r->_mp_d = (mp_ptr) &dummy_limb; +} + +/* The utility of this function is a bit limited, since many functions + assigns the result variable using mpz_swap. */ +void +mpz_init2 (mpz_t r, mp_bitcnt_t bits) +{ + mp_size_t rn; + + bits -= (bits != 0); /* Round down, except if 0 */ + rn = 1 + bits / GMP_LIMB_BITS; + + r->_mp_alloc = rn; + r->_mp_size = 0; + r->_mp_d = gmp_xalloc_limbs (rn); +} + +void +mpz_clear (mpz_t r) +{ + if (r->_mp_alloc) + gmp_free (r->_mp_d); +} + +static mp_ptr +mpz_realloc (mpz_t r, mp_size_t size) +{ + size = GMP_MAX (size, 1); + + if (r->_mp_alloc) + r->_mp_d = gmp_xrealloc_limbs (r->_mp_d, size); + else + r->_mp_d = gmp_xalloc_limbs (size); + r->_mp_alloc = size; + + if (GMP_ABS (r->_mp_size) > size) + r->_mp_size = 0; + + return r->_mp_d; +} + +/* Realloc for an mpz_t WHAT if it has less than NEEDED limbs. */ +#define MPZ_REALLOC(z,n) ((n) > (z)->_mp_alloc \ + ? mpz_realloc(z,n) \ + : (z)->_mp_d) + +/* MPZ assignment and basic conversions. */ +void +mpz_set_si (mpz_t r, signed long int x) +{ + if (x >= 0) + mpz_set_ui (r, x); + else /* (x < 0) */ + if (GMP_LIMB_BITS < GMP_ULONG_BITS) + { + mpz_set_ui (r, GMP_NEG_CAST (unsigned long int, x)); + mpz_neg (r, r); + } + else + { + r->_mp_size = -1; + MPZ_REALLOC (r, 1)[0] = GMP_NEG_CAST (unsigned long int, x); + } +} + +void +mpz_set_ui (mpz_t r, unsigned long int x) +{ + if (x > 0) + { + r->_mp_size = 1; + MPZ_REALLOC (r, 1)[0] = x; + if (GMP_LIMB_BITS < GMP_ULONG_BITS) + { + int LOCAL_GMP_LIMB_BITS = GMP_LIMB_BITS; + while (x >>= LOCAL_GMP_LIMB_BITS) + { + ++ r->_mp_size; + MPZ_REALLOC (r, r->_mp_size)[r->_mp_size - 1] = x; + } + } + } + else + r->_mp_size = 0; +} + +void +mpz_set (mpz_t r, const mpz_t x) +{ + /* Allow the NOP r == x */ + if (r != x) + { + mp_size_t n; + mp_ptr rp; + + n = GMP_ABS (x->_mp_size); + rp = MPZ_REALLOC (r, n); + + mpn_copyi (rp, x->_mp_d, n); + r->_mp_size = x->_mp_size; + } +} + +void +mpz_init_set_si (mpz_t r, signed long int x) +{ + mpz_init (r); + mpz_set_si (r, x); +} + +void +mpz_init_set_ui (mpz_t r, unsigned long int x) +{ + mpz_init (r); + mpz_set_ui (r, x); +} + +void +mpz_init_set (mpz_t r, const mpz_t x) +{ + mpz_init (r); + mpz_set (r, x); +} + +int +mpz_fits_slong_p (const mpz_t u) +{ + return (LONG_MAX + LONG_MIN == 0 || mpz_cmp_ui (u, LONG_MAX) <= 0) && + mpz_cmpabs_ui (u, GMP_NEG_CAST (unsigned long int, LONG_MIN)) <= 0; +} + +static int +mpn_absfits_ulong_p (mp_srcptr up, mp_size_t un) +{ + int ulongsize = GMP_ULONG_BITS / GMP_LIMB_BITS; + mp_limb_t ulongrem = 0; + + if (GMP_ULONG_BITS % GMP_LIMB_BITS != 0) + ulongrem = (mp_limb_t) (ULONG_MAX >> GMP_LIMB_BITS * ulongsize) + 1; + + return un <= ulongsize || (up[ulongsize] < ulongrem && un == ulongsize + 1); +} + +int +mpz_fits_ulong_p (const mpz_t u) +{ + mp_size_t us = u->_mp_size; + + return us >= 0 && mpn_absfits_ulong_p (u->_mp_d, us); +} + +long int +mpz_get_si (const mpz_t u) +{ + unsigned long r = mpz_get_ui (u); + unsigned long c = -LONG_MAX - LONG_MIN; + + if (u->_mp_size < 0) + /* This expression is necessary to properly handle -LONG_MIN */ + return -(long) c - (long) ((r - c) & LONG_MAX); + else + return (long) (r & LONG_MAX); +} + +unsigned long int +mpz_get_ui (const mpz_t u) +{ + if (GMP_LIMB_BITS < GMP_ULONG_BITS) + { + int LOCAL_GMP_LIMB_BITS = GMP_LIMB_BITS; + unsigned long r = 0; + mp_size_t n = GMP_ABS (u->_mp_size); + n = GMP_MIN (n, 1 + (mp_size_t) (GMP_ULONG_BITS - 1) / GMP_LIMB_BITS); + while (--n >= 0) + r = (r << LOCAL_GMP_LIMB_BITS) + u->_mp_d[n]; + return r; + } + + return u->_mp_size == 0 ? 0 : u->_mp_d[0]; +} + +size_t +mpz_size (const mpz_t u) +{ + return GMP_ABS (u->_mp_size); +} + +mp_limb_t +mpz_getlimbn (const mpz_t u, mp_size_t n) +{ + if (n >= 0 && n < GMP_ABS (u->_mp_size)) + return u->_mp_d[n]; + else + return 0; +} + +void +mpz_realloc2 (mpz_t x, mp_bitcnt_t n) +{ + mpz_realloc (x, 1 + (n - (n != 0)) / GMP_LIMB_BITS); +} + +mp_srcptr +mpz_limbs_read (mpz_srcptr x) +{ + return x->_mp_d; +} + +mp_ptr +mpz_limbs_modify (mpz_t x, mp_size_t n) +{ + assert (n > 0); + return MPZ_REALLOC (x, n); +} + +mp_ptr +mpz_limbs_write (mpz_t x, mp_size_t n) +{ + return mpz_limbs_modify (x, n); +} + +void +mpz_limbs_finish (mpz_t x, mp_size_t xs) +{ + mp_size_t xn; + xn = mpn_normalized_size (x->_mp_d, GMP_ABS (xs)); + x->_mp_size = xs < 0 ? -xn : xn; +} + +static mpz_srcptr +mpz_roinit_normal_n (mpz_t x, mp_srcptr xp, mp_size_t xs) +{ + x->_mp_alloc = 0; + x->_mp_d = (mp_ptr) xp; + x->_mp_size = xs; + return x; +} + +mpz_srcptr +mpz_roinit_n (mpz_t x, mp_srcptr xp, mp_size_t xs) +{ + mpz_roinit_normal_n (x, xp, xs); + mpz_limbs_finish (x, xs); + return x; +} + + +/* Conversions and comparison to double. */ +void +mpz_set_d (mpz_t r, double x) +{ + int sign; + mp_ptr rp; + mp_size_t rn, i; + double B; + double Bi; + mp_limb_t f; + + /* x != x is true when x is a NaN, and x == x * 0.5 is true when x is + zero or infinity. */ + if (x != x || x == x * 0.5) + { + r->_mp_size = 0; + return; + } + + sign = x < 0.0 ; + if (sign) + x = - x; + + if (x < 1.0) + { + r->_mp_size = 0; + return; + } + B = 4.0 * (double) (GMP_LIMB_HIGHBIT >> 1); + Bi = 1.0 / B; + for (rn = 1; x >= B; rn++) + x *= Bi; + + rp = MPZ_REALLOC (r, rn); + + f = (mp_limb_t) x; + x -= f; + assert (x < 1.0); + i = rn-1; + rp[i] = f; + while (--i >= 0) + { + x = B * x; + f = (mp_limb_t) x; + x -= f; + assert (x < 1.0); + rp[i] = f; + } + + r->_mp_size = sign ? - rn : rn; +} + +void +mpz_init_set_d (mpz_t r, double x) +{ + mpz_init (r); + mpz_set_d (r, x); +} + +double +mpz_get_d (const mpz_t u) +{ + int m; + mp_limb_t l; + mp_size_t un; + double x; + double B = 4.0 * (double) (GMP_LIMB_HIGHBIT >> 1); + + un = GMP_ABS (u->_mp_size); + + if (un == 0) + return 0.0; + + l = u->_mp_d[--un]; + gmp_clz (m, l); + m = m + GMP_DBL_MANT_BITS - GMP_LIMB_BITS; + if (m < 0) + l &= GMP_LIMB_MAX << -m; + + for (x = l; --un >= 0;) + { + x = B*x; + if (m > 0) { + l = u->_mp_d[un]; + m -= GMP_LIMB_BITS; + if (m < 0) + l &= GMP_LIMB_MAX << -m; + x += l; + } + } + + if (u->_mp_size < 0) + x = -x; + + return x; +} + +int +mpz_cmpabs_d (const mpz_t x, double d) +{ + mp_size_t xn; + double B, Bi; + mp_size_t i; + + xn = x->_mp_size; + d = GMP_ABS (d); + + if (xn != 0) + { + xn = GMP_ABS (xn); + + B = 4.0 * (double) (GMP_LIMB_HIGHBIT >> 1); + Bi = 1.0 / B; + + /* Scale d so it can be compared with the top limb. */ + for (i = 1; i < xn; i++) + d *= Bi; + + if (d >= B) + return -1; + + /* Compare floor(d) to top limb, subtract and cancel when equal. */ + for (i = xn; i-- > 0;) + { + mp_limb_t f, xl; + + f = (mp_limb_t) d; + xl = x->_mp_d[i]; + if (xl > f) + return 1; + else if (xl < f) + return -1; + d = B * (d - f); + } + } + return - (d > 0.0); +} + +int +mpz_cmp_d (const mpz_t x, double d) +{ + if (x->_mp_size < 0) + { + if (d >= 0.0) + return -1; + else + return -mpz_cmpabs_d (x, d); + } + else + { + if (d < 0.0) + return 1; + else + return mpz_cmpabs_d (x, d); + } +} + + +/* MPZ comparisons and the like. */ +int +mpz_sgn (const mpz_t u) +{ + return GMP_CMP (u->_mp_size, 0); +} + +int +mpz_cmp_si (const mpz_t u, long v) +{ + mp_size_t usize = u->_mp_size; + + if (v >= 0) + return mpz_cmp_ui (u, v); + else if (usize >= 0) + return 1; + else + return - mpz_cmpabs_ui (u, GMP_NEG_CAST (unsigned long int, v)); +} + +int +mpz_cmp_ui (const mpz_t u, unsigned long v) +{ + mp_size_t usize = u->_mp_size; + + if (usize < 0) + return -1; + else + return mpz_cmpabs_ui (u, v); +} + +int +mpz_cmp (const mpz_t a, const mpz_t b) +{ + mp_size_t asize = a->_mp_size; + mp_size_t bsize = b->_mp_size; + + if (asize != bsize) + return (asize < bsize) ? -1 : 1; + else if (asize >= 0) + return mpn_cmp (a->_mp_d, b->_mp_d, asize); + else + return mpn_cmp (b->_mp_d, a->_mp_d, -asize); +} + +int +mpz_cmpabs_ui (const mpz_t u, unsigned long v) +{ + mp_size_t un = GMP_ABS (u->_mp_size); + + if (! mpn_absfits_ulong_p (u->_mp_d, un)) + return 1; + else + { + unsigned long uu = mpz_get_ui (u); + return GMP_CMP(uu, v); + } +} + +int +mpz_cmpabs (const mpz_t u, const mpz_t v) +{ + return mpn_cmp4 (u->_mp_d, GMP_ABS (u->_mp_size), + v->_mp_d, GMP_ABS (v->_mp_size)); +} + +void +mpz_abs (mpz_t r, const mpz_t u) +{ + mpz_set (r, u); + r->_mp_size = GMP_ABS (r->_mp_size); +} + +void +mpz_neg (mpz_t r, const mpz_t u) +{ + mpz_set (r, u); + r->_mp_size = -r->_mp_size; +} + +void +mpz_swap (mpz_t u, mpz_t v) +{ + MP_SIZE_T_SWAP (u->_mp_size, v->_mp_size); + MP_SIZE_T_SWAP (u->_mp_alloc, v->_mp_alloc); + MP_PTR_SWAP (u->_mp_d, v->_mp_d); +} + + +/* MPZ addition and subtraction */ + + +void +mpz_add_ui (mpz_t r, const mpz_t a, unsigned long b) +{ + mpz_t bb; + mpz_init_set_ui (bb, b); + mpz_add (r, a, bb); + mpz_clear (bb); +} + +void +mpz_sub_ui (mpz_t r, const mpz_t a, unsigned long b) +{ + mpz_ui_sub (r, b, a); + mpz_neg (r, r); +} + +void +mpz_ui_sub (mpz_t r, unsigned long a, const mpz_t b) +{ + mpz_neg (r, b); + mpz_add_ui (r, r, a); +} + +static mp_size_t +mpz_abs_add (mpz_t r, const mpz_t a, const mpz_t b) +{ + mp_size_t an = GMP_ABS (a->_mp_size); + mp_size_t bn = GMP_ABS (b->_mp_size); + mp_ptr rp; + mp_limb_t cy; + + if (an < bn) + { + MPZ_SRCPTR_SWAP (a, b); + MP_SIZE_T_SWAP (an, bn); + } + + rp = MPZ_REALLOC (r, an + 1); + cy = mpn_add (rp, a->_mp_d, an, b->_mp_d, bn); + + rp[an] = cy; + + return an + cy; +} + +static mp_size_t +mpz_abs_sub (mpz_t r, const mpz_t a, const mpz_t b) +{ + mp_size_t an = GMP_ABS (a->_mp_size); + mp_size_t bn = GMP_ABS (b->_mp_size); + int cmp; + mp_ptr rp; + + cmp = mpn_cmp4 (a->_mp_d, an, b->_mp_d, bn); + if (cmp > 0) + { + rp = MPZ_REALLOC (r, an); + gmp_assert_nocarry (mpn_sub (rp, a->_mp_d, an, b->_mp_d, bn)); + return mpn_normalized_size (rp, an); + } + else if (cmp < 0) + { + rp = MPZ_REALLOC (r, bn); + gmp_assert_nocarry (mpn_sub (rp, b->_mp_d, bn, a->_mp_d, an)); + return -mpn_normalized_size (rp, bn); + } + else + return 0; +} + +void +mpz_add (mpz_t r, const mpz_t a, const mpz_t b) +{ + mp_size_t rn; + + if ( (a->_mp_size ^ b->_mp_size) >= 0) + rn = mpz_abs_add (r, a, b); + else + rn = mpz_abs_sub (r, a, b); + + r->_mp_size = a->_mp_size >= 0 ? rn : - rn; +} + +void +mpz_sub (mpz_t r, const mpz_t a, const mpz_t b) +{ + mp_size_t rn; + + if ( (a->_mp_size ^ b->_mp_size) >= 0) + rn = mpz_abs_sub (r, a, b); + else + rn = mpz_abs_add (r, a, b); + + r->_mp_size = a->_mp_size >= 0 ? rn : - rn; +} + + +/* MPZ multiplication */ +void +mpz_mul_si (mpz_t r, const mpz_t u, long int v) +{ + if (v < 0) + { + mpz_mul_ui (r, u, GMP_NEG_CAST (unsigned long int, v)); + mpz_neg (r, r); + } + else + mpz_mul_ui (r, u, v); +} + +void +mpz_mul_ui (mpz_t r, const mpz_t u, unsigned long int v) +{ + mpz_t vv; + mpz_init_set_ui (vv, v); + mpz_mul (r, u, vv); + mpz_clear (vv); + return; +} + +void +mpz_mul (mpz_t r, const mpz_t u, const mpz_t v) +{ + int sign; + mp_size_t un, vn, rn; + mpz_t t; + mp_ptr tp; + + un = u->_mp_size; + vn = v->_mp_size; + + if (un == 0 || vn == 0) + { + r->_mp_size = 0; + return; + } + + sign = (un ^ vn) < 0; + + un = GMP_ABS (un); + vn = GMP_ABS (vn); + + mpz_init2 (t, (un + vn) * GMP_LIMB_BITS); + + tp = t->_mp_d; + if (un >= vn) + mpn_mul (tp, u->_mp_d, un, v->_mp_d, vn); + else + mpn_mul (tp, v->_mp_d, vn, u->_mp_d, un); + + rn = un + vn; + rn -= tp[rn-1] == 0; + + t->_mp_size = sign ? - rn : rn; + mpz_swap (r, t); + mpz_clear (t); +} + +void +mpz_mul_2exp (mpz_t r, const mpz_t u, mp_bitcnt_t bits) +{ + mp_size_t un, rn; + mp_size_t limbs; + unsigned shift; + mp_ptr rp; + + un = GMP_ABS (u->_mp_size); + if (un == 0) + { + r->_mp_size = 0; + return; + } + + limbs = bits / GMP_LIMB_BITS; + shift = bits % GMP_LIMB_BITS; + + rn = un + limbs + (shift > 0); + rp = MPZ_REALLOC (r, rn); + if (shift > 0) + { + mp_limb_t cy = mpn_lshift (rp + limbs, u->_mp_d, un, shift); + rp[rn-1] = cy; + rn -= (cy == 0); + } + else + mpn_copyd (rp + limbs, u->_mp_d, un); + + mpn_zero (rp, limbs); + + r->_mp_size = (u->_mp_size < 0) ? - rn : rn; +} + +void +mpz_addmul_ui (mpz_t r, const mpz_t u, unsigned long int v) +{ + mpz_t t; + mpz_init_set_ui (t, v); + mpz_mul (t, u, t); + mpz_add (r, r, t); + mpz_clear (t); +} + +void +mpz_submul_ui (mpz_t r, const mpz_t u, unsigned long int v) +{ + mpz_t t; + mpz_init_set_ui (t, v); + mpz_mul (t, u, t); + mpz_sub (r, r, t); + mpz_clear (t); +} + +void +mpz_addmul (mpz_t r, const mpz_t u, const mpz_t v) +{ + mpz_t t; + mpz_init (t); + mpz_mul (t, u, v); + mpz_add (r, r, t); + mpz_clear (t); +} + +void +mpz_submul (mpz_t r, const mpz_t u, const mpz_t v) +{ + mpz_t t; + mpz_init (t); + mpz_mul (t, u, v); + mpz_sub (r, r, t); + mpz_clear (t); +} + + +/* MPZ division */ +enum mpz_div_round_mode { GMP_DIV_FLOOR, GMP_DIV_CEIL, GMP_DIV_TRUNC }; + +/* Allows q or r to be zero. Returns 1 iff remainder is non-zero. */ +static int +mpz_div_qr (mpz_t q, mpz_t r, + const mpz_t n, const mpz_t d, enum mpz_div_round_mode mode) +{ + mp_size_t ns, ds, nn, dn, qs; + ns = n->_mp_size; + ds = d->_mp_size; + + if (ds == 0) + gmp_die("mpz_div_qr: Divide by zero."); + + if (ns == 0) + { + if (q) + q->_mp_size = 0; + if (r) + r->_mp_size = 0; + return 0; + } + + nn = GMP_ABS (ns); + dn = GMP_ABS (ds); + + qs = ds ^ ns; + + if (nn < dn) + { + if (mode == GMP_DIV_CEIL && qs >= 0) + { + /* q = 1, r = n - d */ + if (r) + mpz_sub (r, n, d); + if (q) + mpz_set_ui (q, 1); + } + else if (mode == GMP_DIV_FLOOR && qs < 0) + { + /* q = -1, r = n + d */ + if (r) + mpz_add (r, n, d); + if (q) + mpz_set_si (q, -1); + } + else + { + /* q = 0, r = d */ + if (r) + mpz_set (r, n); + if (q) + q->_mp_size = 0; + } + return 1; + } + else + { + mp_ptr np, qp; + mp_size_t qn, rn; + mpz_t tq, tr; + + mpz_init_set (tr, n); + np = tr->_mp_d; + + qn = nn - dn + 1; + + if (q) + { + mpz_init2 (tq, qn * GMP_LIMB_BITS); + qp = tq->_mp_d; + } + else + qp = NULL; + + mpn_div_qr (qp, np, nn, d->_mp_d, dn); + + if (qp) + { + qn -= (qp[qn-1] == 0); + + tq->_mp_size = qs < 0 ? -qn : qn; + } + rn = mpn_normalized_size (np, dn); + tr->_mp_size = ns < 0 ? - rn : rn; + + if (mode == GMP_DIV_FLOOR && qs < 0 && rn != 0) + { + if (q) + mpz_sub_ui (tq, tq, 1); + if (r) + mpz_add (tr, tr, d); + } + else if (mode == GMP_DIV_CEIL && qs >= 0 && rn != 0) + { + if (q) + mpz_add_ui (tq, tq, 1); + if (r) + mpz_sub (tr, tr, d); + } + + if (q) + { + mpz_swap (tq, q); + mpz_clear (tq); + } + if (r) + mpz_swap (tr, r); + + mpz_clear (tr); + + return rn != 0; + } +} + +void +mpz_cdiv_qr (mpz_t q, mpz_t r, const mpz_t n, const mpz_t d) +{ + mpz_div_qr (q, r, n, d, GMP_DIV_CEIL); +} + +void +mpz_fdiv_qr (mpz_t q, mpz_t r, const mpz_t n, const mpz_t d) +{ + mpz_div_qr (q, r, n, d, GMP_DIV_FLOOR); +} + +void +mpz_tdiv_qr (mpz_t q, mpz_t r, const mpz_t n, const mpz_t d) +{ + mpz_div_qr (q, r, n, d, GMP_DIV_TRUNC); +} + +void +mpz_cdiv_q (mpz_t q, const mpz_t n, const mpz_t d) +{ + mpz_div_qr (q, NULL, n, d, GMP_DIV_CEIL); +} + +void +mpz_fdiv_q (mpz_t q, const mpz_t n, const mpz_t d) +{ + mpz_div_qr (q, NULL, n, d, GMP_DIV_FLOOR); +} + +void +mpz_tdiv_q (mpz_t q, const mpz_t n, const mpz_t d) +{ + mpz_div_qr (q, NULL, n, d, GMP_DIV_TRUNC); +} + +void +mpz_cdiv_r (mpz_t r, const mpz_t n, const mpz_t d) +{ + mpz_div_qr (NULL, r, n, d, GMP_DIV_CEIL); +} + +void +mpz_fdiv_r (mpz_t r, const mpz_t n, const mpz_t d) +{ + mpz_div_qr (NULL, r, n, d, GMP_DIV_FLOOR); +} + +void +mpz_tdiv_r (mpz_t r, const mpz_t n, const mpz_t d) +{ + mpz_div_qr (NULL, r, n, d, GMP_DIV_TRUNC); +} + +void +mpz_mod (mpz_t r, const mpz_t n, const mpz_t d) +{ + mpz_div_qr (NULL, r, n, d, d->_mp_size >= 0 ? GMP_DIV_FLOOR : GMP_DIV_CEIL); +} + +static void +mpz_div_q_2exp (mpz_t q, const mpz_t u, mp_bitcnt_t bit_index, + enum mpz_div_round_mode mode) +{ + mp_size_t un, qn; + mp_size_t limb_cnt; + mp_ptr qp; + int adjust; + + un = u->_mp_size; + if (un == 0) + { + q->_mp_size = 0; + return; + } + limb_cnt = bit_index / GMP_LIMB_BITS; + qn = GMP_ABS (un) - limb_cnt; + bit_index %= GMP_LIMB_BITS; + + if (mode == ((un > 0) ? GMP_DIV_CEIL : GMP_DIV_FLOOR)) /* un != 0 here. */ + /* Note: Below, the final indexing at limb_cnt is valid because at + that point we have qn > 0. */ + adjust = (qn <= 0 + || !mpn_zero_p (u->_mp_d, limb_cnt) + || (u->_mp_d[limb_cnt] + & (((mp_limb_t) 1 << bit_index) - 1))); + else + adjust = 0; + + if (qn <= 0) + qn = 0; + else + { + qp = MPZ_REALLOC (q, qn); + + if (bit_index != 0) + { + mpn_rshift (qp, u->_mp_d + limb_cnt, qn, bit_index); + qn -= qp[qn - 1] == 0; + } + else + { + mpn_copyi (qp, u->_mp_d + limb_cnt, qn); + } + } + + q->_mp_size = qn; + + if (adjust) + mpz_add_ui (q, q, 1); + if (un < 0) + mpz_neg (q, q); +} + +static void +mpz_div_r_2exp (mpz_t r, const mpz_t u, mp_bitcnt_t bit_index, + enum mpz_div_round_mode mode) +{ + mp_size_t us, un, rn; + mp_ptr rp; + mp_limb_t mask; + + us = u->_mp_size; + if (us == 0 || bit_index == 0) + { + r->_mp_size = 0; + return; + } + rn = (bit_index + GMP_LIMB_BITS - 1) / GMP_LIMB_BITS; + assert (rn > 0); + + rp = MPZ_REALLOC (r, rn); + un = GMP_ABS (us); + + mask = GMP_LIMB_MAX >> (rn * GMP_LIMB_BITS - bit_index); + + if (rn > un) + { + /* Quotient (with truncation) is zero, and remainder is + non-zero */ + if (mode == ((us > 0) ? GMP_DIV_CEIL : GMP_DIV_FLOOR)) /* us != 0 here. */ + { + /* Have to negate and sign extend. */ + mp_size_t i; + + gmp_assert_nocarry (! mpn_neg (rp, u->_mp_d, un)); + for (i = un; i < rn - 1; i++) + rp[i] = GMP_LIMB_MAX; + + rp[rn-1] = mask; + us = -us; + } + else + { + /* Just copy */ + if (r != u) + mpn_copyi (rp, u->_mp_d, un); + + rn = un; + } + } + else + { + if (r != u) + mpn_copyi (rp, u->_mp_d, rn - 1); + + rp[rn-1] = u->_mp_d[rn-1] & mask; + + if (mode == ((us > 0) ? GMP_DIV_CEIL : GMP_DIV_FLOOR)) /* us != 0 here. */ + { + /* If r != 0, compute 2^{bit_count} - r. */ + mpn_neg (rp, rp, rn); + + rp[rn-1] &= mask; + + /* us is not used for anything else, so we can modify it + here to indicate flipped sign. */ + us = -us; + } + } + rn = mpn_normalized_size (rp, rn); + r->_mp_size = us < 0 ? -rn : rn; +} + +void +mpz_cdiv_q_2exp (mpz_t r, const mpz_t u, mp_bitcnt_t cnt) +{ + mpz_div_q_2exp (r, u, cnt, GMP_DIV_CEIL); +} + +void +mpz_fdiv_q_2exp (mpz_t r, const mpz_t u, mp_bitcnt_t cnt) +{ + mpz_div_q_2exp (r, u, cnt, GMP_DIV_FLOOR); +} + +void +mpz_tdiv_q_2exp (mpz_t r, const mpz_t u, mp_bitcnt_t cnt) +{ + mpz_div_q_2exp (r, u, cnt, GMP_DIV_TRUNC); +} + +void +mpz_cdiv_r_2exp (mpz_t r, const mpz_t u, mp_bitcnt_t cnt) +{ + mpz_div_r_2exp (r, u, cnt, GMP_DIV_CEIL); +} + +void +mpz_fdiv_r_2exp (mpz_t r, const mpz_t u, mp_bitcnt_t cnt) +{ + mpz_div_r_2exp (r, u, cnt, GMP_DIV_FLOOR); +} + +void +mpz_tdiv_r_2exp (mpz_t r, const mpz_t u, mp_bitcnt_t cnt) +{ + mpz_div_r_2exp (r, u, cnt, GMP_DIV_TRUNC); +} + +void +mpz_divexact (mpz_t q, const mpz_t n, const mpz_t d) +{ + gmp_assert_nocarry (mpz_div_qr (q, NULL, n, d, GMP_DIV_TRUNC)); +} + +int +mpz_divisible_p (const mpz_t n, const mpz_t d) +{ + return mpz_div_qr (NULL, NULL, n, d, GMP_DIV_TRUNC) == 0; +} + +int +mpz_congruent_p (const mpz_t a, const mpz_t b, const mpz_t m) +{ + mpz_t t; + int res; + + /* a == b (mod 0) iff a == b */ + if (mpz_sgn (m) == 0) + return (mpz_cmp (a, b) == 0); + + mpz_init (t); + mpz_sub (t, a, b); + res = mpz_divisible_p (t, m); + mpz_clear (t); + + return res; +} + +static unsigned long +mpz_div_qr_ui (mpz_t q, mpz_t r, + const mpz_t n, unsigned long d, enum mpz_div_round_mode mode) +{ + unsigned long ret; + mpz_t rr, dd; + + mpz_init (rr); + mpz_init_set_ui (dd, d); + mpz_div_qr (q, rr, n, dd, mode); + mpz_clear (dd); + ret = mpz_get_ui (rr); + + if (r) + mpz_swap (r, rr); + mpz_clear (rr); + + return ret; +} + +unsigned long +mpz_cdiv_qr_ui (mpz_t q, mpz_t r, const mpz_t n, unsigned long d) +{ + return mpz_div_qr_ui (q, r, n, d, GMP_DIV_CEIL); +} + +unsigned long +mpz_fdiv_qr_ui (mpz_t q, mpz_t r, const mpz_t n, unsigned long d) +{ + return mpz_div_qr_ui (q, r, n, d, GMP_DIV_FLOOR); +} + +unsigned long +mpz_tdiv_qr_ui (mpz_t q, mpz_t r, const mpz_t n, unsigned long d) +{ + return mpz_div_qr_ui (q, r, n, d, GMP_DIV_TRUNC); +} + +unsigned long +mpz_cdiv_q_ui (mpz_t q, const mpz_t n, unsigned long d) +{ + return mpz_div_qr_ui (q, NULL, n, d, GMP_DIV_CEIL); +} + +unsigned long +mpz_fdiv_q_ui (mpz_t q, const mpz_t n, unsigned long d) +{ + return mpz_div_qr_ui (q, NULL, n, d, GMP_DIV_FLOOR); +} + +unsigned long +mpz_tdiv_q_ui (mpz_t q, const mpz_t n, unsigned long d) +{ + return mpz_div_qr_ui (q, NULL, n, d, GMP_DIV_TRUNC); +} + +unsigned long +mpz_cdiv_r_ui (mpz_t r, const mpz_t n, unsigned long d) +{ + return mpz_div_qr_ui (NULL, r, n, d, GMP_DIV_CEIL); +} +unsigned long +mpz_fdiv_r_ui (mpz_t r, const mpz_t n, unsigned long d) +{ + return mpz_div_qr_ui (NULL, r, n, d, GMP_DIV_FLOOR); +} +unsigned long +mpz_tdiv_r_ui (mpz_t r, const mpz_t n, unsigned long d) +{ + return mpz_div_qr_ui (NULL, r, n, d, GMP_DIV_TRUNC); +} + +unsigned long +mpz_cdiv_ui (const mpz_t n, unsigned long d) +{ + return mpz_div_qr_ui (NULL, NULL, n, d, GMP_DIV_CEIL); +} + +unsigned long +mpz_fdiv_ui (const mpz_t n, unsigned long d) +{ + return mpz_div_qr_ui (NULL, NULL, n, d, GMP_DIV_FLOOR); +} + +unsigned long +mpz_tdiv_ui (const mpz_t n, unsigned long d) +{ + return mpz_div_qr_ui (NULL, NULL, n, d, GMP_DIV_TRUNC); +} + +unsigned long +mpz_mod_ui (mpz_t r, const mpz_t n, unsigned long d) +{ + return mpz_div_qr_ui (NULL, r, n, d, GMP_DIV_FLOOR); +} + +void +mpz_divexact_ui (mpz_t q, const mpz_t n, unsigned long d) +{ + gmp_assert_nocarry (mpz_div_qr_ui (q, NULL, n, d, GMP_DIV_TRUNC)); +} + +int +mpz_divisible_ui_p (const mpz_t n, unsigned long d) +{ + return mpz_div_qr_ui (NULL, NULL, n, d, GMP_DIV_TRUNC) == 0; +} + + +/* GCD */ +static mp_limb_t +mpn_gcd_11 (mp_limb_t u, mp_limb_t v) +{ + unsigned shift; + + assert ( (u | v) > 0); + + if (u == 0) + return v; + else if (v == 0) + return u; + + gmp_ctz (shift, u | v); + + u >>= shift; + v >>= shift; + + if ( (u & 1) == 0) + MP_LIMB_T_SWAP (u, v); + + while ( (v & 1) == 0) + v >>= 1; + + while (u != v) + { + if (u > v) + { + u -= v; + do + u >>= 1; + while ( (u & 1) == 0); + } + else + { + v -= u; + do + v >>= 1; + while ( (v & 1) == 0); + } + } + return u << shift; +} + +unsigned long +mpz_gcd_ui (mpz_t g, const mpz_t u, unsigned long v) +{ + mpz_t t; + mpz_init_set_ui(t, v); + mpz_gcd (t, u, t); + if (v > 0) + v = mpz_get_ui (t); + + if (g) + mpz_swap (t, g); + + mpz_clear (t); + + return v; +} + +static mp_bitcnt_t +mpz_make_odd (mpz_t r) +{ + mp_bitcnt_t shift; + + assert (r->_mp_size > 0); + /* Count trailing zeros, equivalent to mpn_scan1, because we know that there is a 1 */ + shift = mpn_common_scan (r->_mp_d[0], 0, r->_mp_d, 0, 0); + mpz_tdiv_q_2exp (r, r, shift); + + return shift; +} + +void +mpz_gcd (mpz_t g, const mpz_t u, const mpz_t v) +{ + mpz_t tu, tv; + mp_bitcnt_t uz, vz, gz; + + if (u->_mp_size == 0) + { + mpz_abs (g, v); + return; + } + if (v->_mp_size == 0) + { + mpz_abs (g, u); + return; + } + + mpz_init (tu); + mpz_init (tv); + + mpz_abs (tu, u); + uz = mpz_make_odd (tu); + mpz_abs (tv, v); + vz = mpz_make_odd (tv); + gz = GMP_MIN (uz, vz); + + if (tu->_mp_size < tv->_mp_size) + mpz_swap (tu, tv); + + mpz_tdiv_r (tu, tu, tv); + if (tu->_mp_size == 0) + { + mpz_swap (g, tv); + } + else + for (;;) + { + int c; + + mpz_make_odd (tu); + c = mpz_cmp (tu, tv); + if (c == 0) + { + mpz_swap (g, tu); + break; + } + if (c < 0) + mpz_swap (tu, tv); + + if (tv->_mp_size == 1) + { + mp_limb_t vl = tv->_mp_d[0]; + mp_limb_t ul = mpz_tdiv_ui (tu, vl); + mpz_set_ui (g, mpn_gcd_11 (ul, vl)); + break; + } + mpz_sub (tu, tu, tv); + } + mpz_clear (tu); + mpz_clear (tv); + mpz_mul_2exp (g, g, gz); +} + +void +mpz_gcdext (mpz_t g, mpz_t s, mpz_t t, const mpz_t u, const mpz_t v) +{ + mpz_t tu, tv, s0, s1, t0, t1; + mp_bitcnt_t uz, vz, gz; + mp_bitcnt_t power; + + if (u->_mp_size == 0) + { + /* g = 0 u + sgn(v) v */ + signed long sign = mpz_sgn (v); + mpz_abs (g, v); + if (s) + s->_mp_size = 0; + if (t) + mpz_set_si (t, sign); + return; + } + + if (v->_mp_size == 0) + { + /* g = sgn(u) u + 0 v */ + signed long sign = mpz_sgn (u); + mpz_abs (g, u); + if (s) + mpz_set_si (s, sign); + if (t) + t->_mp_size = 0; + return; + } + + mpz_init (tu); + mpz_init (tv); + mpz_init (s0); + mpz_init (s1); + mpz_init (t0); + mpz_init (t1); + + mpz_abs (tu, u); + uz = mpz_make_odd (tu); + mpz_abs (tv, v); + vz = mpz_make_odd (tv); + gz = GMP_MIN (uz, vz); + + uz -= gz; + vz -= gz; + + /* Cofactors corresponding to odd gcd. gz handled later. */ + if (tu->_mp_size < tv->_mp_size) + { + mpz_swap (tu, tv); + MPZ_SRCPTR_SWAP (u, v); + MPZ_PTR_SWAP (s, t); + MP_BITCNT_T_SWAP (uz, vz); + } + + /* Maintain + * + * u = t0 tu + t1 tv + * v = s0 tu + s1 tv + * + * where u and v denote the inputs with common factors of two + * eliminated, and det (s0, t0; s1, t1) = 2^p. Then + * + * 2^p tu = s1 u - t1 v + * 2^p tv = -s0 u + t0 v + */ + + /* After initial division, tu = q tv + tu', we have + * + * u = 2^uz (tu' + q tv) + * v = 2^vz tv + * + * or + * + * t0 = 2^uz, t1 = 2^uz q + * s0 = 0, s1 = 2^vz + */ + + mpz_setbit (t0, uz); + mpz_tdiv_qr (t1, tu, tu, tv); + mpz_mul_2exp (t1, t1, uz); + + mpz_setbit (s1, vz); + power = uz + vz; + + if (tu->_mp_size > 0) + { + mp_bitcnt_t shift; + shift = mpz_make_odd (tu); + mpz_mul_2exp (t0, t0, shift); + mpz_mul_2exp (s0, s0, shift); + power += shift; + + for (;;) + { + int c; + c = mpz_cmp (tu, tv); + if (c == 0) + break; + + if (c < 0) + { + /* tv = tv' + tu + * + * u = t0 tu + t1 (tv' + tu) = (t0 + t1) tu + t1 tv' + * v = s0 tu + s1 (tv' + tu) = (s0 + s1) tu + s1 tv' */ + + mpz_sub (tv, tv, tu); + mpz_add (t0, t0, t1); + mpz_add (s0, s0, s1); + + shift = mpz_make_odd (tv); + mpz_mul_2exp (t1, t1, shift); + mpz_mul_2exp (s1, s1, shift); + } + else + { + mpz_sub (tu, tu, tv); + mpz_add (t1, t0, t1); + mpz_add (s1, s0, s1); + + shift = mpz_make_odd (tu); + mpz_mul_2exp (t0, t0, shift); + mpz_mul_2exp (s0, s0, shift); + } + power += shift; + } + } + + /* Now tv = odd part of gcd, and -s0 and t0 are corresponding + cofactors. */ + + mpz_mul_2exp (tv, tv, gz); + mpz_neg (s0, s0); + + /* 2^p g = s0 u + t0 v. Eliminate one factor of two at a time. To + adjust cofactors, we need u / g and v / g */ + + mpz_divexact (s1, v, tv); + mpz_abs (s1, s1); + mpz_divexact (t1, u, tv); + mpz_abs (t1, t1); + + while (power-- > 0) + { + /* s0 u + t0 v = (s0 - v/g) u - (t0 + u/g) v */ + if (mpz_odd_p (s0) || mpz_odd_p (t0)) + { + mpz_sub (s0, s0, s1); + mpz_add (t0, t0, t1); + } + assert (mpz_even_p (t0) && mpz_even_p (s0)); + mpz_tdiv_q_2exp (s0, s0, 1); + mpz_tdiv_q_2exp (t0, t0, 1); + } + + /* Arrange so that |s| < |u| / 2g */ + mpz_add (s1, s0, s1); + if (mpz_cmpabs (s0, s1) > 0) + { + mpz_swap (s0, s1); + mpz_sub (t0, t0, t1); + } + if (u->_mp_size < 0) + mpz_neg (s0, s0); + if (v->_mp_size < 0) + mpz_neg (t0, t0); + + mpz_swap (g, tv); + if (s) + mpz_swap (s, s0); + if (t) + mpz_swap (t, t0); + + mpz_clear (tu); + mpz_clear (tv); + mpz_clear (s0); + mpz_clear (s1); + mpz_clear (t0); + mpz_clear (t1); +} + +void +mpz_lcm (mpz_t r, const mpz_t u, const mpz_t v) +{ + mpz_t g; + + if (u->_mp_size == 0 || v->_mp_size == 0) + { + r->_mp_size = 0; + return; + } + + mpz_init (g); + + mpz_gcd (g, u, v); + mpz_divexact (g, u, g); + mpz_mul (r, g, v); + + mpz_clear (g); + mpz_abs (r, r); +} + +void +mpz_lcm_ui (mpz_t r, const mpz_t u, unsigned long v) +{ + if (v == 0 || u->_mp_size == 0) + { + r->_mp_size = 0; + return; + } + + v /= mpz_gcd_ui (NULL, u, v); + mpz_mul_ui (r, u, v); + + mpz_abs (r, r); +} + +int +mpz_invert (mpz_t r, const mpz_t u, const mpz_t m) +{ + mpz_t g, tr; + int invertible; + + if (u->_mp_size == 0 || mpz_cmpabs_ui (m, 1) <= 0) + return 0; + + mpz_init (g); + mpz_init (tr); + + mpz_gcdext (g, tr, NULL, u, m); + invertible = (mpz_cmp_ui (g, 1) == 0); + + if (invertible) + { + if (tr->_mp_size < 0) + { + if (m->_mp_size >= 0) + mpz_add (tr, tr, m); + else + mpz_sub (tr, tr, m); + } + mpz_swap (r, tr); + } + + mpz_clear (g); + mpz_clear (tr); + return invertible; +} + + +/* Higher level operations (sqrt, pow and root) */ + +void +mpz_pow_ui (mpz_t r, const mpz_t b, unsigned long e) +{ + unsigned long bit; + mpz_t tr; + mpz_init_set_ui (tr, 1); + + bit = GMP_ULONG_HIGHBIT; + do + { + mpz_mul (tr, tr, tr); + if (e & bit) + mpz_mul (tr, tr, b); + bit >>= 1; + } + while (bit > 0); + + mpz_swap (r, tr); + mpz_clear (tr); +} + +void +mpz_ui_pow_ui (mpz_t r, unsigned long blimb, unsigned long e) +{ + mpz_t b; + + mpz_init_set_ui (b, blimb); + mpz_pow_ui (r, b, e); + mpz_clear (b); +} + +void +mpz_powm (mpz_t r, const mpz_t b, const mpz_t e, const mpz_t m) +{ + mpz_t tr; + mpz_t base; + mp_size_t en, mn; + mp_srcptr mp; + struct gmp_div_inverse minv; + unsigned shift; + mp_ptr tp = NULL; + + en = GMP_ABS (e->_mp_size); + mn = GMP_ABS (m->_mp_size); + if (mn == 0) + gmp_die ("mpz_powm: Zero modulo."); + + if (en == 0) + { + mpz_set_ui (r, 1); + return; + } + + mp = m->_mp_d; + mpn_div_qr_invert (&minv, mp, mn); + shift = minv.shift; + + if (shift > 0) + { + /* To avoid shifts, we do all our reductions, except the final + one, using a *normalized* m. */ + minv.shift = 0; + + tp = gmp_xalloc_limbs (mn); + gmp_assert_nocarry (mpn_lshift (tp, mp, mn, shift)); + mp = tp; + } + + mpz_init (base); + + if (e->_mp_size < 0) + { + if (!mpz_invert (base, b, m)) + gmp_die ("mpz_powm: Negative exponent and non-invertible base."); + } + else + { + mp_size_t bn; + mpz_abs (base, b); + + bn = base->_mp_size; + if (bn >= mn) + { + mpn_div_qr_preinv (NULL, base->_mp_d, base->_mp_size, mp, mn, &minv); + bn = mn; + } + + /* We have reduced the absolute value. Now take care of the + sign. Note that we get zero represented non-canonically as + m. */ + if (b->_mp_size < 0) + { + mp_ptr bp = MPZ_REALLOC (base, mn); + gmp_assert_nocarry (mpn_sub (bp, mp, mn, bp, bn)); + bn = mn; + } + base->_mp_size = mpn_normalized_size (base->_mp_d, bn); + } + mpz_init_set_ui (tr, 1); + + while (--en >= 0) + { + mp_limb_t w = e->_mp_d[en]; + mp_limb_t bit; + + bit = GMP_LIMB_HIGHBIT; + do + { + mpz_mul (tr, tr, tr); + if (w & bit) + mpz_mul (tr, tr, base); + if (tr->_mp_size > mn) + { + mpn_div_qr_preinv (NULL, tr->_mp_d, tr->_mp_size, mp, mn, &minv); + tr->_mp_size = mpn_normalized_size (tr->_mp_d, mn); + } + bit >>= 1; + } + while (bit > 0); + } + + /* Final reduction */ + if (tr->_mp_size >= mn) + { + minv.shift = shift; + mpn_div_qr_preinv (NULL, tr->_mp_d, tr->_mp_size, mp, mn, &minv); + tr->_mp_size = mpn_normalized_size (tr->_mp_d, mn); + } + if (tp) + gmp_free (tp); + + mpz_swap (r, tr); + mpz_clear (tr); + mpz_clear (base); +} + +void +mpz_powm_ui (mpz_t r, const mpz_t b, unsigned long elimb, const mpz_t m) +{ + mpz_t e; + + mpz_init_set_ui (e, elimb); + mpz_powm (r, b, e, m); + mpz_clear (e); +} + +/* x=trunc(y^(1/z)), r=y-x^z */ +void +mpz_rootrem (mpz_t x, mpz_t r, const mpz_t y, unsigned long z) +{ + int sgn; + mpz_t t, u; + + sgn = y->_mp_size < 0; + if ((~z & sgn) != 0) + gmp_die ("mpz_rootrem: Negative argument, with even root."); + if (z == 0) + gmp_die ("mpz_rootrem: Zeroth root."); + + if (mpz_cmpabs_ui (y, 1) <= 0) { + if (x) + mpz_set (x, y); + if (r) + r->_mp_size = 0; + return; + } + + mpz_init (u); + mpz_init (t); + mpz_setbit (t, mpz_sizeinbase (y, 2) / z + 1); + + if (z == 2) /* simplify sqrt loop: z-1 == 1 */ + do { + mpz_swap (u, t); /* u = x */ + mpz_tdiv_q (t, y, u); /* t = y/x */ + mpz_add (t, t, u); /* t = y/x + x */ + mpz_tdiv_q_2exp (t, t, 1); /* x'= (y/x + x)/2 */ + } while (mpz_cmpabs (t, u) < 0); /* |x'| < |x| */ + else /* z != 2 */ { + mpz_t v; + + mpz_init (v); + if (sgn) + mpz_neg (t, t); + + do { + mpz_swap (u, t); /* u = x */ + mpz_pow_ui (t, u, z - 1); /* t = x^(z-1) */ + mpz_tdiv_q (t, y, t); /* t = y/x^(z-1) */ + mpz_mul_ui (v, u, z - 1); /* v = x*(z-1) */ + mpz_add (t, t, v); /* t = y/x^(z-1) + x*(z-1) */ + mpz_tdiv_q_ui (t, t, z); /* x'=(y/x^(z-1) + x*(z-1))/z */ + } while (mpz_cmpabs (t, u) < 0); /* |x'| < |x| */ + + mpz_clear (v); + } + + if (r) { + mpz_pow_ui (t, u, z); + mpz_sub (r, y, t); + } + if (x) + mpz_swap (x, u); + mpz_clear (u); + mpz_clear (t); +} + +int +mpz_root (mpz_t x, const mpz_t y, unsigned long z) +{ + int res; + mpz_t r; + + mpz_init (r); + mpz_rootrem (x, r, y, z); + res = r->_mp_size == 0; + mpz_clear (r); + + return res; +} + +/* Compute s = floor(sqrt(u)) and r = u - s^2. Allows r == NULL */ +void +mpz_sqrtrem (mpz_t s, mpz_t r, const mpz_t u) +{ + mpz_rootrem (s, r, u, 2); +} + +void +mpz_sqrt (mpz_t s, const mpz_t u) +{ + mpz_rootrem (s, NULL, u, 2); +} + +int +mpz_perfect_square_p (const mpz_t u) +{ + if (u->_mp_size <= 0) + return (u->_mp_size == 0); + else + return mpz_root (NULL, u, 2); +} + +int +mpn_perfect_square_p (mp_srcptr p, mp_size_t n) +{ + mpz_t t; + + assert (n > 0); + assert (p [n-1] != 0); + return mpz_root (NULL, mpz_roinit_normal_n (t, p, n), 2); +} + +mp_size_t +mpn_sqrtrem (mp_ptr sp, mp_ptr rp, mp_srcptr p, mp_size_t n) +{ + mpz_t s, r, u; + mp_size_t res; + + assert (n > 0); + assert (p [n-1] != 0); + + mpz_init (r); + mpz_init (s); + mpz_rootrem (s, r, mpz_roinit_normal_n (u, p, n), 2); + + assert (s->_mp_size == (n+1)/2); + mpn_copyd (sp, s->_mp_d, s->_mp_size); + mpz_clear (s); + res = r->_mp_size; + if (rp) + mpn_copyd (rp, r->_mp_d, res); + mpz_clear (r); + return res; +} + +/* Combinatorics */ + +void +mpz_mfac_uiui (mpz_t x, unsigned long n, unsigned long m) +{ + mpz_set_ui (x, n + (n == 0)); + if (m + 1 < 2) return; + while (n > m + 1) + mpz_mul_ui (x, x, n -= m); +} + +void +mpz_2fac_ui (mpz_t x, unsigned long n) +{ + mpz_mfac_uiui (x, n, 2); +} + +void +mpz_fac_ui (mpz_t x, unsigned long n) +{ + mpz_mfac_uiui (x, n, 1); +} + +void +mpz_bin_uiui (mpz_t r, unsigned long n, unsigned long k) +{ + mpz_t t; + + mpz_set_ui (r, k <= n); + + if (k > (n >> 1)) + k = (k <= n) ? n - k : 0; + + mpz_init (t); + mpz_fac_ui (t, k); + + for (; k > 0; --k) + mpz_mul_ui (r, r, n--); + + mpz_divexact (r, r, t); + mpz_clear (t); +} + + +/* Primality testing */ + +/* Computes Kronecker (a/b) with odd b, a!=0 and GCD(a,b) = 1 */ +/* Adapted from JACOBI_BASE_METHOD==4 in mpn/generic/jacbase.c */ +static int +gmp_jacobi_coprime (mp_limb_t a, mp_limb_t b) +{ + int c, bit = 0; + + assert (b & 1); + assert (a != 0); + /* assert (mpn_gcd_11 (a, b) == 1); */ + + /* Below, we represent a and b shifted right so that the least + significant one bit is implicit. */ + b >>= 1; + + gmp_ctz(c, a); + a >>= 1; + + do + { + a >>= c; + /* (2/b) = -1 if b = 3 or 5 mod 8 */ + bit ^= c & (b ^ (b >> 1)); + if (a < b) + { + bit ^= a & b; + a = b - a; + b -= a; + } + else + { + a -= b; + assert (a != 0); + } + + gmp_ctz(c, a); + ++c; + } + while (b > 0); + + return bit & 1 ? -1 : 1; +} + +static void +gmp_lucas_step_k_2k (mpz_t V, mpz_t Qk, const mpz_t n) +{ + mpz_mod (Qk, Qk, n); + /* V_{2k} <- V_k ^ 2 - 2Q^k */ + mpz_mul (V, V, V); + mpz_submul_ui (V, Qk, 2); + mpz_tdiv_r (V, V, n); + /* Q^{2k} = (Q^k)^2 */ + mpz_mul (Qk, Qk, Qk); +} + +/* Computes V_k, Q^k (mod n) for the Lucas' sequence */ +/* with P=1, Q=Q; k = (n>>b0)|1. */ +/* Requires an odd n > 4; b0 > 0; -2*Q must not overflow a long */ +/* Returns (U_k == 0) and sets V=V_k and Qk=Q^k. */ +static int +gmp_lucas_mod (mpz_t V, mpz_t Qk, long Q, + mp_bitcnt_t b0, const mpz_t n) +{ + mp_bitcnt_t bs; + mpz_t U; + int res; + + assert (b0 > 0); + assert (Q <= - (LONG_MIN / 2)); + assert (Q >= - (LONG_MAX / 2)); + assert (mpz_cmp_ui (n, 4) > 0); + assert (mpz_odd_p (n)); + + mpz_init_set_ui (U, 1); /* U1 = 1 */ + mpz_set_ui (V, 1); /* V1 = 1 */ + mpz_set_si (Qk, Q); + + for (bs = mpz_sizeinbase (n, 2) - 1; --bs >= b0;) + { + /* U_{2k} <- U_k * V_k */ + mpz_mul (U, U, V); + /* V_{2k} <- V_k ^ 2 - 2Q^k */ + /* Q^{2k} = (Q^k)^2 */ + gmp_lucas_step_k_2k (V, Qk, n); + + /* A step k->k+1 is performed if the bit in $n$ is 1 */ + /* mpz_tstbit(n,bs) or the bit is 0 in $n$ but */ + /* should be 1 in $n+1$ (bs == b0) */ + if (b0 == bs || mpz_tstbit (n, bs)) + { + /* Q^{k+1} <- Q^k * Q */ + mpz_mul_si (Qk, Qk, Q); + /* U_{k+1} <- (U_k + V_k) / 2 */ + mpz_swap (U, V); /* Keep in V the old value of U_k */ + mpz_add (U, U, V); + /* We have to compute U/2, so we need an even value, */ + /* equivalent (mod n) */ + if (mpz_odd_p (U)) + mpz_add (U, U, n); + mpz_tdiv_q_2exp (U, U, 1); + /* V_{k+1} <-(D*U_k + V_k) / 2 = + U_{k+1} + (D-1)/2*U_k = U_{k+1} - 2Q*U_k */ + mpz_mul_si (V, V, -2*Q); + mpz_add (V, U, V); + mpz_tdiv_r (V, V, n); + } + mpz_tdiv_r (U, U, n); + } + + res = U->_mp_size == 0; + mpz_clear (U); + return res; +} + +/* Performs strong Lucas' test on x, with parameters suggested */ +/* for the BPSW test. Qk is only passed to recycle a variable. */ +/* Requires GCD (x,6) = 1.*/ +static int +gmp_stronglucas (const mpz_t x, mpz_t Qk) +{ + mp_bitcnt_t b0; + mpz_t V, n; + mp_limb_t maxD, D; /* The absolute value is stored. */ + long Q; + mp_limb_t tl; + + /* Test on the absolute value. */ + mpz_roinit_normal_n (n, x->_mp_d, GMP_ABS (x->_mp_size)); + + assert (mpz_odd_p (n)); + /* assert (mpz_gcd_ui (NULL, n, 6) == 1); */ + if (mpz_root (Qk, n, 2)) + return 0; /* A square is composite. */ + + /* Check Ds up to square root (in case, n is prime) + or avoid overflows */ + maxD = (Qk->_mp_size == 1) ? Qk->_mp_d [0] - 1 : GMP_LIMB_MAX; + + D = 3; + /* Search a D such that (D/n) = -1 in the sequence 5,-7,9,-11,.. */ + /* For those Ds we have (D/n) = (n/|D|) */ + do + { + if (D >= maxD) + return 1 + (D != GMP_LIMB_MAX); /* (1 + ! ~ D) */ + D += 2; + tl = mpz_tdiv_ui (n, D); + if (tl == 0) + return 0; + } + while (gmp_jacobi_coprime (tl, D) == 1); + + mpz_init (V); + + /* n-(D/n) = n+1 = d*2^{b0}, with d = (n>>b0) | 1 */ + b0 = mpz_scan0 (n, 0); + + /* D= P^2 - 4Q; P = 1; Q = (1-D)/4 */ + Q = (D & 2) ? (long) (D >> 2) + 1 : -(long) (D >> 2); + + if (! gmp_lucas_mod (V, Qk, Q, b0, n)) /* If Ud != 0 */ + while (V->_mp_size != 0 && --b0 != 0) /* while Vk != 0 */ + /* V <- V ^ 2 - 2Q^k */ + /* Q^{2k} = (Q^k)^2 */ + gmp_lucas_step_k_2k (V, Qk, n); + + mpz_clear (V); + return (b0 != 0); +} + +static int +gmp_millerrabin (const mpz_t n, const mpz_t nm1, mpz_t y, + const mpz_t q, mp_bitcnt_t k) +{ + assert (k > 0); + + /* Caller must initialize y to the base. */ + mpz_powm (y, y, q, n); + + if (mpz_cmp_ui (y, 1) == 0 || mpz_cmp (y, nm1) == 0) + return 1; + + while (--k > 0) + { + mpz_powm_ui (y, y, 2, n); + if (mpz_cmp (y, nm1) == 0) + return 1; + /* y == 1 means that the previous y was a non-trivial square root + of 1 (mod n). y == 0 means that n is a power of the base. + In either case, n is not prime. */ + if (mpz_cmp_ui (y, 1) <= 0) + return 0; + } + return 0; +} + +/* This product is 0xc0cfd797, and fits in 32 bits. */ +#define GMP_PRIME_PRODUCT \ + (3UL*5UL*7UL*11UL*13UL*17UL*19UL*23UL*29UL) + +/* Bit (p+1)/2 is set, for each odd prime <= 61 */ +#define GMP_PRIME_MASK 0xc96996dcUL + +int +mpz_probab_prime_p (const mpz_t n, int reps) +{ + mpz_t nm1; + mpz_t q; + mpz_t y; + mp_bitcnt_t k; + int is_prime; + int j; + + /* Note that we use the absolute value of n only, for compatibility + with the real GMP. */ + if (mpz_even_p (n)) + return (mpz_cmpabs_ui (n, 2) == 0) ? 2 : 0; + + /* Above test excludes n == 0 */ + assert (n->_mp_size != 0); + + if (mpz_cmpabs_ui (n, 64) < 0) + return (GMP_PRIME_MASK >> (n->_mp_d[0] >> 1)) & 2; + + if (mpz_gcd_ui (NULL, n, GMP_PRIME_PRODUCT) != 1) + return 0; + + /* All prime factors are >= 31. */ + if (mpz_cmpabs_ui (n, 31*31) < 0) + return 2; + + mpz_init (nm1); + mpz_init (q); + + /* Find q and k, where q is odd and n = 1 + 2**k * q. */ + mpz_abs (nm1, n); + nm1->_mp_d[0] -= 1; + k = mpz_scan1 (nm1, 0); + mpz_tdiv_q_2exp (q, nm1, k); + + /* BPSW test */ + mpz_init_set_ui (y, 2); + is_prime = gmp_millerrabin (n, nm1, y, q, k) && gmp_stronglucas (n, y); + reps -= 24; /* skip the first 24 repetitions */ + + /* Use Miller-Rabin, with a deterministic sequence of bases, a[j] = + j^2 + j + 41 using Euler's polynomial. We potentially stop early, + if a[j] >= n - 1. Since n >= 31*31, this can happen only if reps > + 30 (a[30] == 971 > 31*31 == 961). */ + + for (j = 0; is_prime & (j < reps); j++) + { + mpz_set_ui (y, (unsigned long) j*j+j+41); + if (mpz_cmp (y, nm1) >= 0) + { + /* Don't try any further bases. This "early" break does not affect + the result for any reasonable reps value (<=5000 was tested) */ + assert (j >= 30); + break; + } + is_prime = gmp_millerrabin (n, nm1, y, q, k); + } + mpz_clear (nm1); + mpz_clear (q); + mpz_clear (y); + + return is_prime; +} + + +/* Logical operations and bit manipulation. */ + +/* Numbers are treated as if represented in two's complement (and + infinitely sign extended). For a negative values we get the two's + complement from -x = ~x + 1, where ~ is bitwise complement. + Negation transforms + + xxxx10...0 + + into + + yyyy10...0 + + where yyyy is the bitwise complement of xxxx. So least significant + bits, up to and including the first one bit, are unchanged, and + the more significant bits are all complemented. + + To change a bit from zero to one in a negative number, subtract the + corresponding power of two from the absolute value. This can never + underflow. To change a bit from one to zero, add the corresponding + power of two, and this might overflow. E.g., if x = -001111, the + two's complement is 110001. Clearing the least significant bit, we + get two's complement 110000, and -010000. */ + +int +mpz_tstbit (const mpz_t d, mp_bitcnt_t bit_index) +{ + mp_size_t limb_index; + unsigned shift; + mp_size_t ds; + mp_size_t dn; + mp_limb_t w; + int bit; + + ds = d->_mp_size; + dn = GMP_ABS (ds); + limb_index = bit_index / GMP_LIMB_BITS; + if (limb_index >= dn) + return ds < 0; + + shift = bit_index % GMP_LIMB_BITS; + w = d->_mp_d[limb_index]; + bit = (w >> shift) & 1; + + if (ds < 0) + { + /* d < 0. Check if any of the bits below is set: If so, our bit + must be complemented. */ + if (shift > 0 && (mp_limb_t) (w << (GMP_LIMB_BITS - shift)) > 0) + return bit ^ 1; + while (--limb_index >= 0) + if (d->_mp_d[limb_index] > 0) + return bit ^ 1; + } + return bit; +} + +static void +mpz_abs_add_bit (mpz_t d, mp_bitcnt_t bit_index) +{ + mp_size_t dn, limb_index; + mp_limb_t bit; + mp_ptr dp; + + dn = GMP_ABS (d->_mp_size); + + limb_index = bit_index / GMP_LIMB_BITS; + bit = (mp_limb_t) 1 << (bit_index % GMP_LIMB_BITS); + + if (limb_index >= dn) + { + mp_size_t i; + /* The bit should be set outside of the end of the number. + We have to increase the size of the number. */ + dp = MPZ_REALLOC (d, limb_index + 1); + + dp[limb_index] = bit; + for (i = dn; i < limb_index; i++) + dp[i] = 0; + dn = limb_index + 1; + } + else + { + mp_limb_t cy; + + dp = d->_mp_d; + + cy = mpn_add_1 (dp + limb_index, dp + limb_index, dn - limb_index, bit); + if (cy > 0) + { + dp = MPZ_REALLOC (d, dn + 1); + dp[dn++] = cy; + } + } + + d->_mp_size = (d->_mp_size < 0) ? - dn : dn; +} + +static void +mpz_abs_sub_bit (mpz_t d, mp_bitcnt_t bit_index) +{ + mp_size_t dn, limb_index; + mp_ptr dp; + mp_limb_t bit; + + dn = GMP_ABS (d->_mp_size); + dp = d->_mp_d; + + limb_index = bit_index / GMP_LIMB_BITS; + bit = (mp_limb_t) 1 << (bit_index % GMP_LIMB_BITS); + + assert (limb_index < dn); + + gmp_assert_nocarry (mpn_sub_1 (dp + limb_index, dp + limb_index, + dn - limb_index, bit)); + dn = mpn_normalized_size (dp, dn); + d->_mp_size = (d->_mp_size < 0) ? - dn : dn; +} + +void +mpz_setbit (mpz_t d, mp_bitcnt_t bit_index) +{ + if (!mpz_tstbit (d, bit_index)) + { + if (d->_mp_size >= 0) + mpz_abs_add_bit (d, bit_index); + else + mpz_abs_sub_bit (d, bit_index); + } +} + +void +mpz_clrbit (mpz_t d, mp_bitcnt_t bit_index) +{ + if (mpz_tstbit (d, bit_index)) + { + if (d->_mp_size >= 0) + mpz_abs_sub_bit (d, bit_index); + else + mpz_abs_add_bit (d, bit_index); + } +} + +void +mpz_combit (mpz_t d, mp_bitcnt_t bit_index) +{ + if (mpz_tstbit (d, bit_index) ^ (d->_mp_size < 0)) + mpz_abs_sub_bit (d, bit_index); + else + mpz_abs_add_bit (d, bit_index); +} + +void +mpz_com (mpz_t r, const mpz_t u) +{ + mpz_add_ui (r, u, 1); + mpz_neg (r, r); +} + +void +mpz_and (mpz_t r, const mpz_t u, const mpz_t v) +{ + mp_size_t un, vn, rn, i; + mp_ptr up, vp, rp; + + mp_limb_t ux, vx, rx; + mp_limb_t uc, vc, rc; + mp_limb_t ul, vl, rl; + + un = GMP_ABS (u->_mp_size); + vn = GMP_ABS (v->_mp_size); + if (un < vn) + { + MPZ_SRCPTR_SWAP (u, v); + MP_SIZE_T_SWAP (un, vn); + } + if (vn == 0) + { + r->_mp_size = 0; + return; + } + + uc = u->_mp_size < 0; + vc = v->_mp_size < 0; + rc = uc & vc; + + ux = -uc; + vx = -vc; + rx = -rc; + + /* If the smaller input is positive, higher limbs don't matter. */ + rn = vx ? un : vn; + + rp = MPZ_REALLOC (r, rn + (mp_size_t) rc); + + up = u->_mp_d; + vp = v->_mp_d; + + i = 0; + do + { + ul = (up[i] ^ ux) + uc; + uc = ul < uc; + + vl = (vp[i] ^ vx) + vc; + vc = vl < vc; + + rl = ( (ul & vl) ^ rx) + rc; + rc = rl < rc; + rp[i] = rl; + } + while (++i < vn); + assert (vc == 0); + + for (; i < rn; i++) + { + ul = (up[i] ^ ux) + uc; + uc = ul < uc; + + rl = ( (ul & vx) ^ rx) + rc; + rc = rl < rc; + rp[i] = rl; + } + if (rc) + rp[rn++] = rc; + else + rn = mpn_normalized_size (rp, rn); + + r->_mp_size = rx ? -rn : rn; +} + +void +mpz_ior (mpz_t r, const mpz_t u, const mpz_t v) +{ + mp_size_t un, vn, rn, i; + mp_ptr up, vp, rp; + + mp_limb_t ux, vx, rx; + mp_limb_t uc, vc, rc; + mp_limb_t ul, vl, rl; + + un = GMP_ABS (u->_mp_size); + vn = GMP_ABS (v->_mp_size); + if (un < vn) + { + MPZ_SRCPTR_SWAP (u, v); + MP_SIZE_T_SWAP (un, vn); + } + if (vn == 0) + { + mpz_set (r, u); + return; + } + + uc = u->_mp_size < 0; + vc = v->_mp_size < 0; + rc = uc | vc; + + ux = -uc; + vx = -vc; + rx = -rc; + + /* If the smaller input is negative, by sign extension higher limbs + don't matter. */ + rn = vx ? vn : un; + + rp = MPZ_REALLOC (r, rn + (mp_size_t) rc); + + up = u->_mp_d; + vp = v->_mp_d; + + i = 0; + do + { + ul = (up[i] ^ ux) + uc; + uc = ul < uc; + + vl = (vp[i] ^ vx) + vc; + vc = vl < vc; + + rl = ( (ul | vl) ^ rx) + rc; + rc = rl < rc; + rp[i] = rl; + } + while (++i < vn); + assert (vc == 0); + + for (; i < rn; i++) + { + ul = (up[i] ^ ux) + uc; + uc = ul < uc; + + rl = ( (ul | vx) ^ rx) + rc; + rc = rl < rc; + rp[i] = rl; + } + if (rc) + rp[rn++] = rc; + else + rn = mpn_normalized_size (rp, rn); + + r->_mp_size = rx ? -rn : rn; +} + +void +mpz_xor (mpz_t r, const mpz_t u, const mpz_t v) +{ + mp_size_t un, vn, i; + mp_ptr up, vp, rp; + + mp_limb_t ux, vx, rx; + mp_limb_t uc, vc, rc; + mp_limb_t ul, vl, rl; + + un = GMP_ABS (u->_mp_size); + vn = GMP_ABS (v->_mp_size); + if (un < vn) + { + MPZ_SRCPTR_SWAP (u, v); + MP_SIZE_T_SWAP (un, vn); + } + if (vn == 0) + { + mpz_set (r, u); + return; + } + + uc = u->_mp_size < 0; + vc = v->_mp_size < 0; + rc = uc ^ vc; + + ux = -uc; + vx = -vc; + rx = -rc; + + rp = MPZ_REALLOC (r, un + (mp_size_t) rc); + + up = u->_mp_d; + vp = v->_mp_d; + + i = 0; + do + { + ul = (up[i] ^ ux) + uc; + uc = ul < uc; + + vl = (vp[i] ^ vx) + vc; + vc = vl < vc; + + rl = (ul ^ vl ^ rx) + rc; + rc = rl < rc; + rp[i] = rl; + } + while (++i < vn); + assert (vc == 0); + + for (; i < un; i++) + { + ul = (up[i] ^ ux) + uc; + uc = ul < uc; + + rl = (ul ^ ux) + rc; + rc = rl < rc; + rp[i] = rl; + } + if (rc) + rp[un++] = rc; + else + un = mpn_normalized_size (rp, un); + + r->_mp_size = rx ? -un : un; +} + +static unsigned +gmp_popcount_limb (mp_limb_t x) +{ + unsigned c; + + /* Do 16 bits at a time, to avoid limb-sized constants. */ + int LOCAL_SHIFT_BITS = 16; + for (c = 0; x > 0;) + { + unsigned w = x - ((x >> 1) & 0x5555); + w = ((w >> 2) & 0x3333) + (w & 0x3333); + w = (w >> 4) + w; + w = ((w >> 8) & 0x000f) + (w & 0x000f); + c += w; + if (GMP_LIMB_BITS > LOCAL_SHIFT_BITS) + x >>= LOCAL_SHIFT_BITS; + else + x = 0; + } + return c; +} + +mp_bitcnt_t +mpn_popcount (mp_srcptr p, mp_size_t n) +{ + mp_size_t i; + mp_bitcnt_t c; + + for (c = 0, i = 0; i < n; i++) + c += gmp_popcount_limb (p[i]); + + return c; +} + +mp_bitcnt_t +mpz_popcount (const mpz_t u) +{ + mp_size_t un; + + un = u->_mp_size; + + if (un < 0) + return ~(mp_bitcnt_t) 0; + + return mpn_popcount (u->_mp_d, un); +} + +mp_bitcnt_t +mpz_hamdist (const mpz_t u, const mpz_t v) +{ + mp_size_t un, vn, i; + mp_limb_t uc, vc, ul, vl, comp; + mp_srcptr up, vp; + mp_bitcnt_t c; + + un = u->_mp_size; + vn = v->_mp_size; + + if ( (un ^ vn) < 0) + return ~(mp_bitcnt_t) 0; + + comp = - (uc = vc = (un < 0)); + if (uc) + { + assert (vn < 0); + un = -un; + vn = -vn; + } + + up = u->_mp_d; + vp = v->_mp_d; + + if (un < vn) + MPN_SRCPTR_SWAP (up, un, vp, vn); + + for (i = 0, c = 0; i < vn; i++) + { + ul = (up[i] ^ comp) + uc; + uc = ul < uc; + + vl = (vp[i] ^ comp) + vc; + vc = vl < vc; + + c += gmp_popcount_limb (ul ^ vl); + } + assert (vc == 0); + + for (; i < un; i++) + { + ul = (up[i] ^ comp) + uc; + uc = ul < uc; + + c += gmp_popcount_limb (ul ^ comp); + } + + return c; +} + +mp_bitcnt_t +mpz_scan1 (const mpz_t u, mp_bitcnt_t starting_bit) +{ + mp_ptr up; + mp_size_t us, un, i; + mp_limb_t limb, ux; + + us = u->_mp_size; + un = GMP_ABS (us); + i = starting_bit / GMP_LIMB_BITS; + + /* Past the end there's no 1 bits for u>=0, or an immediate 1 bit + for u<0. Notice this test picks up any u==0 too. */ + if (i >= un) + return (us >= 0 ? ~(mp_bitcnt_t) 0 : starting_bit); + + up = u->_mp_d; + ux = 0; + limb = up[i]; + + if (starting_bit != 0) + { + if (us < 0) + { + ux = mpn_zero_p (up, i); + limb = ~ limb + ux; + ux = - (mp_limb_t) (limb >= ux); + } + + /* Mask to 0 all bits before starting_bit, thus ignoring them. */ + limb &= GMP_LIMB_MAX << (starting_bit % GMP_LIMB_BITS); + } + + return mpn_common_scan (limb, i, up, un, ux); +} + +mp_bitcnt_t +mpz_scan0 (const mpz_t u, mp_bitcnt_t starting_bit) +{ + mp_ptr up; + mp_size_t us, un, i; + mp_limb_t limb, ux; + + us = u->_mp_size; + ux = - (mp_limb_t) (us >= 0); + un = GMP_ABS (us); + i = starting_bit / GMP_LIMB_BITS; + + /* When past end, there's an immediate 0 bit for u>=0, or no 0 bits for + u<0. Notice this test picks up all cases of u==0 too. */ + if (i >= un) + return (ux ? starting_bit : ~(mp_bitcnt_t) 0); + + up = u->_mp_d; + limb = up[i] ^ ux; + + if (ux == 0) + limb -= mpn_zero_p (up, i); /* limb = ~(~limb + zero_p) */ + + /* Mask all bits before starting_bit, thus ignoring them. */ + limb &= GMP_LIMB_MAX << (starting_bit % GMP_LIMB_BITS); + + return mpn_common_scan (limb, i, up, un, ux); +} + + +/* MPZ base conversion. */ + +size_t +mpz_sizeinbase (const mpz_t u, int base) +{ + mp_size_t un; + mp_srcptr up; + mp_ptr tp; + mp_bitcnt_t bits; + struct gmp_div_inverse bi; + size_t ndigits; + + assert (base >= 2); + assert (base <= 62); + + un = GMP_ABS (u->_mp_size); + if (un == 0) + return 1; + + up = u->_mp_d; + + bits = (un - 1) * GMP_LIMB_BITS + mpn_limb_size_in_base_2 (up[un-1]); + switch (base) + { + case 2: + return bits; + case 4: + return (bits + 1) / 2; + case 8: + return (bits + 2) / 3; + case 16: + return (bits + 3) / 4; + case 32: + return (bits + 4) / 5; + /* FIXME: Do something more clever for the common case of base + 10. */ + } + + tp = gmp_xalloc_limbs (un); + mpn_copyi (tp, up, un); + mpn_div_qr_1_invert (&bi, base); + + ndigits = 0; + do + { + ndigits++; + mpn_div_qr_1_preinv (tp, tp, un, &bi); + un -= (tp[un-1] == 0); + } + while (un > 0); + + gmp_free (tp); + return ndigits; +} + +char * +mpz_get_str (char *sp, int base, const mpz_t u) +{ + unsigned bits; + const char *digits; + mp_size_t un; + size_t i, sn; + + digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + if (base > 1) + { + if (base <= 36) + digits = "0123456789abcdefghijklmnopqrstuvwxyz"; + else if (base > 62) + return NULL; + } + else if (base >= -1) + base = 10; + else + { + base = -base; + if (base > 36) + return NULL; + } + + sn = 1 + mpz_sizeinbase (u, base); + if (!sp) + sp = (char *) gmp_xalloc (1 + sn); + + un = GMP_ABS (u->_mp_size); + + if (un == 0) + { + sp[0] = '0'; + sp[1] = '\0'; + return sp; + } + + i = 0; + + if (u->_mp_size < 0) + sp[i++] = '-'; + + bits = mpn_base_power_of_two_p (base); + + if (bits) + /* Not modified in this case. */ + sn = i + mpn_get_str_bits ((unsigned char *) sp + i, bits, u->_mp_d, un); + else + { + struct mpn_base_info info; + mp_ptr tp; + + mpn_get_base_info (&info, base); + tp = gmp_xalloc_limbs (un); + mpn_copyi (tp, u->_mp_d, un); + + sn = i + mpn_get_str_other ((unsigned char *) sp + i, base, &info, tp, un); + gmp_free (tp); + } + + for (; i < sn; i++) + sp[i] = digits[(unsigned char) sp[i]]; + + sp[sn] = '\0'; + return sp; +} + +int +mpz_set_str (mpz_t r, const char *sp, int base) +{ + unsigned bits, value_of_a; + mp_size_t rn, alloc; + mp_ptr rp; + size_t dn; + int sign; + unsigned char *dp; + + assert (base == 0 || (base >= 2 && base <= 62)); + + while (isspace( (unsigned char) *sp)) + sp++; + + sign = (*sp == '-'); + sp += sign; + + if (base == 0) + { + if (sp[0] == '0') + { + if (sp[1] == 'x' || sp[1] == 'X') + { + base = 16; + sp += 2; + } + else if (sp[1] == 'b' || sp[1] == 'B') + { + base = 2; + sp += 2; + } + else + base = 8; + } + else + base = 10; + } + + if (!*sp) + { + r->_mp_size = 0; + return -1; + } + dp = (unsigned char *) gmp_xalloc (strlen (sp)); + + value_of_a = (base > 36) ? 36 : 10; + for (dn = 0; *sp; sp++) + { + unsigned digit; + + if (isspace ((unsigned char) *sp)) + continue; + else if (*sp >= '0' && *sp <= '9') + digit = *sp - '0'; + else if (*sp >= 'a' && *sp <= 'z') + digit = *sp - 'a' + value_of_a; + else if (*sp >= 'A' && *sp <= 'Z') + digit = *sp - 'A' + 10; + else + digit = base; /* fail */ + + if (digit >= (unsigned) base) + { + gmp_free (dp); + r->_mp_size = 0; + return -1; + } + + dp[dn++] = digit; + } + + if (!dn) + { + gmp_free (dp); + r->_mp_size = 0; + return -1; + } + bits = mpn_base_power_of_two_p (base); + + if (bits > 0) + { + alloc = (dn * bits + GMP_LIMB_BITS - 1) / GMP_LIMB_BITS; + rp = MPZ_REALLOC (r, alloc); + rn = mpn_set_str_bits (rp, dp, dn, bits); + } + else + { + struct mpn_base_info info; + mpn_get_base_info (&info, base); + alloc = (dn + info.exp - 1) / info.exp; + rp = MPZ_REALLOC (r, alloc); + rn = mpn_set_str_other (rp, dp, dn, base, &info); + /* Normalization, needed for all-zero input. */ + assert (rn > 0); + rn -= rp[rn-1] == 0; + } + assert (rn <= alloc); + gmp_free (dp); + + r->_mp_size = sign ? - rn : rn; + + return 0; +} + +int +mpz_init_set_str (mpz_t r, const char *sp, int base) +{ + mpz_init (r); + return mpz_set_str (r, sp, base); +} + +size_t +mpz_out_str (FILE *stream, int base, const mpz_t x) +{ + char *str; + size_t len; + + str = mpz_get_str (NULL, base, x); + if (!str) + return 0; + len = strlen (str); + len = fwrite (str, 1, len, stream); + gmp_free (str); + return len; +} + + +static int +gmp_detect_endian (void) +{ + static const int i = 2; + const unsigned char *p = (const unsigned char *) &i; + return 1 - *p; +} + +/* Import and export. Does not support nails. */ +void +mpz_import (mpz_t r, size_t count, int order, size_t size, int endian, + size_t nails, const void *src) +{ + const unsigned char *p; + ptrdiff_t word_step; + mp_ptr rp; + mp_size_t rn; + + /* The current (partial) limb. */ + mp_limb_t limb; + /* The number of bytes already copied to this limb (starting from + the low end). */ + size_t bytes; + /* The index where the limb should be stored, when completed. */ + mp_size_t i; + + if (nails != 0) + gmp_die ("mpz_import: Nails not supported."); + + assert (order == 1 || order == -1); + assert (endian >= -1 && endian <= 1); + + if (endian == 0) + endian = gmp_detect_endian (); + + p = (unsigned char *) src; + + word_step = (order != endian) ? 2 * size : 0; + + /* Process bytes from the least significant end, so point p at the + least significant word. */ + if (order == 1) + { + p += size * (count - 1); + word_step = - word_step; + } + + /* And at least significant byte of that word. */ + if (endian == 1) + p += (size - 1); + + rn = (size * count + sizeof(mp_limb_t) - 1) / sizeof(mp_limb_t); + rp = MPZ_REALLOC (r, rn); + + for (limb = 0, bytes = 0, i = 0; count > 0; count--, p += word_step) + { + size_t j; + for (j = 0; j < size; j++, p -= (ptrdiff_t) endian) + { + limb |= (mp_limb_t) *p << (bytes++ * CHAR_BIT); + if (bytes == sizeof(mp_limb_t)) + { + rp[i++] = limb; + bytes = 0; + limb = 0; + } + } + } + assert (i + (bytes > 0) == rn); + if (limb != 0) + rp[i++] = limb; + else + i = mpn_normalized_size (rp, i); + + r->_mp_size = i; +} + +void * +mpz_export (void *r, size_t *countp, int order, size_t size, int endian, + size_t nails, const mpz_t u) +{ + size_t count; + mp_size_t un; + + if (nails != 0) + gmp_die ("mpz_import: Nails not supported."); + + assert (order == 1 || order == -1); + assert (endian >= -1 && endian <= 1); + assert (size > 0 || u->_mp_size == 0); + + un = u->_mp_size; + count = 0; + if (un != 0) + { + size_t k; + unsigned char *p; + ptrdiff_t word_step; + /* The current (partial) limb. */ + mp_limb_t limb; + /* The number of bytes left to do in this limb. */ + size_t bytes; + /* The index where the limb was read. */ + mp_size_t i; + + un = GMP_ABS (un); + + /* Count bytes in top limb. */ + limb = u->_mp_d[un-1]; + assert (limb != 0); + + k = (GMP_LIMB_BITS <= CHAR_BIT); + if (!k) + { + do { + int LOCAL_CHAR_BIT = CHAR_BIT; + k++; limb >>= LOCAL_CHAR_BIT; + } while (limb != 0); + } + /* else limb = 0; */ + + count = (k + (un-1) * sizeof (mp_limb_t) + size - 1) / size; + + if (!r) + r = gmp_xalloc (count * size); + + if (endian == 0) + endian = gmp_detect_endian (); + + p = (unsigned char *) r; + + word_step = (order != endian) ? 2 * size : 0; + + /* Process bytes from the least significant end, so point p at the + least significant word. */ + if (order == 1) + { + p += size * (count - 1); + word_step = - word_step; + } + + /* And at least significant byte of that word. */ + if (endian == 1) + p += (size - 1); + + for (bytes = 0, i = 0, k = 0; k < count; k++, p += word_step) + { + size_t j; + for (j = 0; j < size; ++j, p -= (ptrdiff_t) endian) + { + if (sizeof (mp_limb_t) == 1) + { + if (i < un) + *p = u->_mp_d[i++]; + else + *p = 0; + } + else + { + int LOCAL_CHAR_BIT = CHAR_BIT; + if (bytes == 0) + { + if (i < un) + limb = u->_mp_d[i++]; + bytes = sizeof (mp_limb_t); + } + *p = limb; + limb >>= LOCAL_CHAR_BIT; + bytes--; + } + } + } + assert (i == un); + assert (k == count); + } + + if (countp) + *countp = count; + + return r; +} diff --git a/src/rigraph/vendor/mini-gmp/mini-gmp.h b/src/rigraph/vendor/mini-gmp/mini-gmp.h new file mode 100644 index 0000000..d575f7d --- /dev/null +++ b/src/rigraph/vendor/mini-gmp/mini-gmp.h @@ -0,0 +1,305 @@ +/* mini-gmp, a minimalistic implementation of a GNU GMP subset. + +Copyright 2011-2015, 2017, 2019-2020 Free Software Foundation, Inc. + +This file is part of the GNU MP Library. + +The GNU MP Library is free software; you can redistribute it and/or modify +it under the terms of either: + + * the GNU Lesser General Public License as published by the Free + Software Foundation; either version 3 of the License, or (at your + option) any later version. + +or + + * the GNU General Public License as published by the Free Software + Foundation; either version 2 of the License, or (at your option) any + later version. + +or both in parallel, as here. + +The GNU MP Library is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received copies of the GNU General Public License and the +GNU Lesser General Public License along with the GNU MP Library. If not, +see https://www.gnu.org/licenses/. */ + +/* About mini-gmp: This is a minimal implementation of a subset of the + GMP interface. It is intended for inclusion into applications which + have modest bignums needs, as a fallback when the real GMP library + is not installed. + + This file defines the public interface. */ + +#ifndef __MINI_GMP_H__ +#define __MINI_GMP_H__ + +/* For size_t */ +#include + +#if defined (__cplusplus) +extern "C" { +#endif + +void mp_set_memory_functions (void *(*) (size_t), + void *(*) (void *, size_t, size_t), + void (*) (void *, size_t)); + +void mp_get_memory_functions (void *(**) (size_t), + void *(**) (void *, size_t, size_t), + void (**) (void *, size_t)); + +#ifndef MINI_GMP_LIMB_TYPE +#define MINI_GMP_LIMB_TYPE long +#endif + +typedef unsigned MINI_GMP_LIMB_TYPE mp_limb_t; +typedef long mp_size_t; +typedef unsigned long mp_bitcnt_t; + +typedef mp_limb_t *mp_ptr; +typedef const mp_limb_t *mp_srcptr; + +typedef struct +{ + int _mp_alloc; /* Number of *limbs* allocated and pointed + to by the _mp_d field. */ + int _mp_size; /* abs(_mp_size) is the number of limbs the + last field points to. If _mp_size is + negative this is a negative number. */ + mp_limb_t *_mp_d; /* Pointer to the limbs. */ +} __mpz_struct; + +typedef __mpz_struct mpz_t[1]; + +typedef __mpz_struct *mpz_ptr; +typedef const __mpz_struct *mpz_srcptr; + +extern const int mp_bits_per_limb; + +void mpn_copyi (mp_ptr, mp_srcptr, mp_size_t); +void mpn_copyd (mp_ptr, mp_srcptr, mp_size_t); +void mpn_zero (mp_ptr, mp_size_t); + +int mpn_cmp (mp_srcptr, mp_srcptr, mp_size_t); +int mpn_zero_p (mp_srcptr, mp_size_t); + +mp_limb_t mpn_add_1 (mp_ptr, mp_srcptr, mp_size_t, mp_limb_t); +mp_limb_t mpn_add_n (mp_ptr, mp_srcptr, mp_srcptr, mp_size_t); +mp_limb_t mpn_add (mp_ptr, mp_srcptr, mp_size_t, mp_srcptr, mp_size_t); + +mp_limb_t mpn_sub_1 (mp_ptr, mp_srcptr, mp_size_t, mp_limb_t); +mp_limb_t mpn_sub_n (mp_ptr, mp_srcptr, mp_srcptr, mp_size_t); +mp_limb_t mpn_sub (mp_ptr, mp_srcptr, mp_size_t, mp_srcptr, mp_size_t); + +mp_limb_t mpn_mul_1 (mp_ptr, mp_srcptr, mp_size_t, mp_limb_t); +mp_limb_t mpn_addmul_1 (mp_ptr, mp_srcptr, mp_size_t, mp_limb_t); +mp_limb_t mpn_submul_1 (mp_ptr, mp_srcptr, mp_size_t, mp_limb_t); + +mp_limb_t mpn_mul (mp_ptr, mp_srcptr, mp_size_t, mp_srcptr, mp_size_t); +void mpn_mul_n (mp_ptr, mp_srcptr, mp_srcptr, mp_size_t); +void mpn_sqr (mp_ptr, mp_srcptr, mp_size_t); +int mpn_perfect_square_p (mp_srcptr, mp_size_t); +mp_size_t mpn_sqrtrem (mp_ptr, mp_ptr, mp_srcptr, mp_size_t); + +mp_limb_t mpn_lshift (mp_ptr, mp_srcptr, mp_size_t, unsigned int); +mp_limb_t mpn_rshift (mp_ptr, mp_srcptr, mp_size_t, unsigned int); + +mp_bitcnt_t mpn_scan0 (mp_srcptr, mp_bitcnt_t); +mp_bitcnt_t mpn_scan1 (mp_srcptr, mp_bitcnt_t); + +void mpn_com (mp_ptr, mp_srcptr, mp_size_t); +mp_limb_t mpn_neg (mp_ptr, mp_srcptr, mp_size_t); + +mp_bitcnt_t mpn_popcount (mp_srcptr, mp_size_t); + +mp_limb_t mpn_invert_3by2 (mp_limb_t, mp_limb_t); +#define mpn_invert_limb(x) mpn_invert_3by2 ((x), 0) + +size_t mpn_get_str (unsigned char *, int, mp_ptr, mp_size_t); +mp_size_t mpn_set_str (mp_ptr, const unsigned char *, size_t, int); + +void mpz_init (mpz_t); +void mpz_init2 (mpz_t, mp_bitcnt_t); +void mpz_clear (mpz_t); + +#define mpz_odd_p(z) (((z)->_mp_size != 0) & (int) (z)->_mp_d[0]) +#define mpz_even_p(z) (! mpz_odd_p (z)) + +int mpz_sgn (const mpz_t); +int mpz_cmp_si (const mpz_t, long); +int mpz_cmp_ui (const mpz_t, unsigned long); +int mpz_cmp (const mpz_t, const mpz_t); +int mpz_cmpabs_ui (const mpz_t, unsigned long); +int mpz_cmpabs (const mpz_t, const mpz_t); +int mpz_cmp_d (const mpz_t, double); +int mpz_cmpabs_d (const mpz_t, double); + +void mpz_abs (mpz_t, const mpz_t); +void mpz_neg (mpz_t, const mpz_t); +void mpz_swap (mpz_t, mpz_t); + +void mpz_add_ui (mpz_t, const mpz_t, unsigned long); +void mpz_add (mpz_t, const mpz_t, const mpz_t); +void mpz_sub_ui (mpz_t, const mpz_t, unsigned long); +void mpz_ui_sub (mpz_t, unsigned long, const mpz_t); +void mpz_sub (mpz_t, const mpz_t, const mpz_t); + +void mpz_mul_si (mpz_t, const mpz_t, long int); +void mpz_mul_ui (mpz_t, const mpz_t, unsigned long int); +void mpz_mul (mpz_t, const mpz_t, const mpz_t); +void mpz_mul_2exp (mpz_t, const mpz_t, mp_bitcnt_t); +void mpz_addmul_ui (mpz_t, const mpz_t, unsigned long int); +void mpz_addmul (mpz_t, const mpz_t, const mpz_t); +void mpz_submul_ui (mpz_t, const mpz_t, unsigned long int); +void mpz_submul (mpz_t, const mpz_t, const mpz_t); + +void mpz_cdiv_qr (mpz_t, mpz_t, const mpz_t, const mpz_t); +void mpz_fdiv_qr (mpz_t, mpz_t, const mpz_t, const mpz_t); +void mpz_tdiv_qr (mpz_t, mpz_t, const mpz_t, const mpz_t); +void mpz_cdiv_q (mpz_t, const mpz_t, const mpz_t); +void mpz_fdiv_q (mpz_t, const mpz_t, const mpz_t); +void mpz_tdiv_q (mpz_t, const mpz_t, const mpz_t); +void mpz_cdiv_r (mpz_t, const mpz_t, const mpz_t); +void mpz_fdiv_r (mpz_t, const mpz_t, const mpz_t); +void mpz_tdiv_r (mpz_t, const mpz_t, const mpz_t); + +void mpz_cdiv_q_2exp (mpz_t, const mpz_t, mp_bitcnt_t); +void mpz_fdiv_q_2exp (mpz_t, const mpz_t, mp_bitcnt_t); +void mpz_tdiv_q_2exp (mpz_t, const mpz_t, mp_bitcnt_t); +void mpz_cdiv_r_2exp (mpz_t, const mpz_t, mp_bitcnt_t); +void mpz_fdiv_r_2exp (mpz_t, const mpz_t, mp_bitcnt_t); +void mpz_tdiv_r_2exp (mpz_t, const mpz_t, mp_bitcnt_t); + +void mpz_mod (mpz_t, const mpz_t, const mpz_t); + +void mpz_divexact (mpz_t, const mpz_t, const mpz_t); + +int mpz_divisible_p (const mpz_t, const mpz_t); +int mpz_congruent_p (const mpz_t, const mpz_t, const mpz_t); + +unsigned long mpz_cdiv_qr_ui (mpz_t, mpz_t, const mpz_t, unsigned long); +unsigned long mpz_fdiv_qr_ui (mpz_t, mpz_t, const mpz_t, unsigned long); +unsigned long mpz_tdiv_qr_ui (mpz_t, mpz_t, const mpz_t, unsigned long); +unsigned long mpz_cdiv_q_ui (mpz_t, const mpz_t, unsigned long); +unsigned long mpz_fdiv_q_ui (mpz_t, const mpz_t, unsigned long); +unsigned long mpz_tdiv_q_ui (mpz_t, const mpz_t, unsigned long); +unsigned long mpz_cdiv_r_ui (mpz_t, const mpz_t, unsigned long); +unsigned long mpz_fdiv_r_ui (mpz_t, const mpz_t, unsigned long); +unsigned long mpz_tdiv_r_ui (mpz_t, const mpz_t, unsigned long); +unsigned long mpz_cdiv_ui (const mpz_t, unsigned long); +unsigned long mpz_fdiv_ui (const mpz_t, unsigned long); +unsigned long mpz_tdiv_ui (const mpz_t, unsigned long); + +unsigned long mpz_mod_ui (mpz_t, const mpz_t, unsigned long); + +void mpz_divexact_ui (mpz_t, const mpz_t, unsigned long); + +int mpz_divisible_ui_p (const mpz_t, unsigned long); + +unsigned long mpz_gcd_ui (mpz_t, const mpz_t, unsigned long); +void mpz_gcd (mpz_t, const mpz_t, const mpz_t); +void mpz_gcdext (mpz_t, mpz_t, mpz_t, const mpz_t, const mpz_t); +void mpz_lcm_ui (mpz_t, const mpz_t, unsigned long); +void mpz_lcm (mpz_t, const mpz_t, const mpz_t); +int mpz_invert (mpz_t, const mpz_t, const mpz_t); + +void mpz_sqrtrem (mpz_t, mpz_t, const mpz_t); +void mpz_sqrt (mpz_t, const mpz_t); +int mpz_perfect_square_p (const mpz_t); + +void mpz_pow_ui (mpz_t, const mpz_t, unsigned long); +void mpz_ui_pow_ui (mpz_t, unsigned long, unsigned long); +void mpz_powm (mpz_t, const mpz_t, const mpz_t, const mpz_t); +void mpz_powm_ui (mpz_t, const mpz_t, unsigned long, const mpz_t); + +void mpz_rootrem (mpz_t, mpz_t, const mpz_t, unsigned long); +int mpz_root (mpz_t, const mpz_t, unsigned long); + +void mpz_fac_ui (mpz_t, unsigned long); +void mpz_2fac_ui (mpz_t, unsigned long); +void mpz_mfac_uiui (mpz_t, unsigned long, unsigned long); +void mpz_bin_uiui (mpz_t, unsigned long, unsigned long); + +int mpz_probab_prime_p (const mpz_t, int); + +int mpz_tstbit (const mpz_t, mp_bitcnt_t); +void mpz_setbit (mpz_t, mp_bitcnt_t); +void mpz_clrbit (mpz_t, mp_bitcnt_t); +void mpz_combit (mpz_t, mp_bitcnt_t); + +void mpz_com (mpz_t, const mpz_t); +void mpz_and (mpz_t, const mpz_t, const mpz_t); +void mpz_ior (mpz_t, const mpz_t, const mpz_t); +void mpz_xor (mpz_t, const mpz_t, const mpz_t); + +mp_bitcnt_t mpz_popcount (const mpz_t); +mp_bitcnt_t mpz_hamdist (const mpz_t, const mpz_t); +mp_bitcnt_t mpz_scan0 (const mpz_t, mp_bitcnt_t); +mp_bitcnt_t mpz_scan1 (const mpz_t, mp_bitcnt_t); + +int mpz_fits_slong_p (const mpz_t); +int mpz_fits_ulong_p (const mpz_t); +long int mpz_get_si (const mpz_t); +unsigned long int mpz_get_ui (const mpz_t); +double mpz_get_d (const mpz_t); +size_t mpz_size (const mpz_t); +mp_limb_t mpz_getlimbn (const mpz_t, mp_size_t); + +void mpz_realloc2 (mpz_t, mp_bitcnt_t); +mp_srcptr mpz_limbs_read (mpz_srcptr); +mp_ptr mpz_limbs_modify (mpz_t, mp_size_t); +mp_ptr mpz_limbs_write (mpz_t, mp_size_t); +void mpz_limbs_finish (mpz_t, mp_size_t); +mpz_srcptr mpz_roinit_n (mpz_t, mp_srcptr, mp_size_t); + +#define MPZ_ROINIT_N(xp, xs) {{0, (xs),(xp) }} + +void mpz_set_si (mpz_t, signed long int); +void mpz_set_ui (mpz_t, unsigned long int); +void mpz_set (mpz_t, const mpz_t); +void mpz_set_d (mpz_t, double); + +void mpz_init_set_si (mpz_t, signed long int); +void mpz_init_set_ui (mpz_t, unsigned long int); +void mpz_init_set (mpz_t, const mpz_t); +void mpz_init_set_d (mpz_t, double); + +size_t mpz_sizeinbase (const mpz_t, int); +char *mpz_get_str (char *, int, const mpz_t); +int mpz_set_str (mpz_t, const char *, int); +int mpz_init_set_str (mpz_t, const char *, int); + +/* This long list taken from gmp.h. */ +/* For reference, "defined(EOF)" cannot be used here. In g++ 2.95.4, + defines EOF but not FILE. */ +#if defined (FILE) \ + || defined (H_STDIO) \ + || defined (_H_STDIO) /* AIX */ \ + || defined (_STDIO_H) /* glibc, Sun, SCO */ \ + || defined (_STDIO_H_) /* BSD, OSF */ \ + || defined (__STDIO_H) /* Borland */ \ + || defined (__STDIO_H__) /* IRIX */ \ + || defined (_STDIO_INCLUDED) /* HPUX */ \ + || defined (__dj_include_stdio_h_) /* DJGPP */ \ + || defined (_FILE_DEFINED) /* Microsoft */ \ + || defined (__STDIO__) /* Apple MPW MrC */ \ + || defined (_MSL_STDIO_H) /* Metrowerks */ \ + || defined (_STDIO_H_INCLUDED) /* QNX4 */ \ + || defined (_ISO_STDIO_ISO_H) /* Sun C++ */ \ + || defined (__STDIO_LOADED) /* VMS */ \ + || defined (__DEFINED_FILE) /* musl */ +size_t mpz_out_str (FILE *, int, const mpz_t); +#endif + +void mpz_import (mpz_t, size_t, int, size_t, int, size_t, const void *); +void *mpz_export (void *, size_t *, int, size_t, int, size_t, const mpz_t); + +#if defined (__cplusplus) +} +#endif +#endif /* __MINI_GMP_H__ */ diff --git a/src/rigraph/vendor/plfit/arithmetic_ansi.h b/src/rigraph/vendor/plfit/arithmetic_ansi.h new file mode 100644 index 0000000..c58c98a --- /dev/null +++ b/src/rigraph/vendor/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 +#include + +#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/src/rigraph/vendor/plfit/arithmetic_sse_double.h b/src/rigraph/vendor/plfit/arithmetic_sse_double.h new file mode 100644 index 0000000..a94d89d --- /dev/null +++ b/src/rigraph/vendor/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 + +#if !defined(__APPLE__) +#include +#endif + +#include + +#if 1400 <= _MSC_VER +#include +#endif/*1400 <= _MSC_VER*/ + +#if HAVE_EMMINTRIN_H +#include +#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/src/rigraph/vendor/plfit/arithmetic_sse_float.h b/src/rigraph/vendor/plfit/arithmetic_sse_float.h new file mode 100644 index 0000000..b88f0e2 --- /dev/null +++ b/src/rigraph/vendor/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 + +#if !defined(__APPLE__) +#include +#endif + +#include + +#if 1400 <= _MSC_VER +#include +#endif/*_MSC_VER*/ + +#if HAVE_XMMINTRIN_H +#include +#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/src/rigraph/vendor/plfit/gss.c b/src/rigraph/vendor/plfit/gss.c new file mode 100644 index 0000000..f503484 --- /dev/null +++ b/src/rigraph/vendor/plfit/gss.c @@ -0,0 +1,153 @@ +/* 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 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 +#include +#include +#include "plfit_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/src/rigraph/vendor/plfit/gss.h b/src/rigraph/vendor/plfit/gss.h new file mode 100644 index 0000000..b96b213 --- /dev/null +++ b/src/rigraph/vendor/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 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public 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/src/rigraph/vendor/plfit/hzeta.c b/src/rigraph/vendor/plfit/hzeta.c new file mode 100644 index 0000000..eefd70c --- /dev/null +++ b/src/rigraph/vendor/plfit/hzeta.c @@ -0,0 +1,651 @@ +/* vim:set ts=4 sw=2 sts=2 et: */ + +/* This file was imported from a private scientific library + * based on GSL coined Home Scientific Libray (HSL) by its author + * Jerome Benoit; this very material is itself inspired from the + * material written by G. Jungan and distributed by GSL. + * Ultimately, some modifications were done in order to render the + * imported material independent from the rest of GSL. + */ + +/* `hsl/specfunc/hzeta.c' C source file +// HSL - Home Scientific Library +// Copyright (C) 2017-2018 Jerome Benoit +// +// HSL is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public 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 material in this file is mainly inspired by the material written by +// G. Jungan and distributed under GPLv2 by the GNU Scientific Library (GSL) +// ( https://www.gnu.org/software/gsl/ [specfunc/zeta.c]), itself inspired by +// the material written by Moshier and distributed in the Cephes Mathematical +// Library ( http://www.moshier.net/ [zeta.c]). +// +// More specifically, hsl_sf_hzeta_e is a slightly modifed clone of +// gsl_sf_hzeta_e as found in GSL 2.4; the remaining is `inspired by'. +// [Sooner or later a _Working_Note_ may be deposited at ResearchGate +// ( https://www.researchgate.net/profile/Jerome_Benoit )] +*/ + +/* Author: Jerome G. Benoit < jgmbenoit _at_ rezozer _dot_ net > */ + +#ifdef _MSC_VER +#define _USE_MATH_DEFINES +#endif + +#include +#include +#include "hzeta.h" +#include "plfit_error.h" +#include "platform.h" /* because of NAN */ + +/* imported 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 + +/* imported from gsl_math.h */ + +#ifndef M_LOG2E +#define M_LOG2E 1.44269504088896340735992468100 /* log_2 (e) */ +#endif + +/* imported from gsl_sf_result.h */ + +struct gsl_sf_result_struct { + double val; + double err; +}; +typedef struct gsl_sf_result_struct gsl_sf_result; + +/* imported and adapted from hsl/specfunc/specfunc_def.h */ + +#define HSL_SF_EVAL_RESULT(FnE) \ + gsl_sf_result result; \ + FnE ; \ + return (result.val); + +#define HSL_SF_EVAL_TUPLE_RESULT(FnET) \ + gsl_sf_result result0; \ + gsl_sf_result result1; \ + FnET ; \ + *tuple1=result1.val; \ + *tuple0=result0.val; \ + return (result0.val); + +/* */ + + +#define HSL_SF_HZETA_EULERMACLAURIN_SERIES_SHIFT 10 +#define HSL_SF_HZETA_EULERMACLAURIN_SERIES_ORDER 32 + +#define HSL_SF_LNHZETA_EULERMACLAURIN_SERIES_SHIFT_MAX 256 + +// B_{2j}/(2j) +static +double hsl_sf_hzeta_eulermaclaurin_series_coeffs[HSL_SF_HZETA_EULERMACLAURIN_SERIES_ORDER+1]={ + +1.0, + +1.0/12.0, + -1.0/720.0, + +1.0/30240.0, + -1.0/1209600.0, + +1.0/47900160.0, + -691.0/1307674368000.0, + +1.0/74724249600.0, + -3.38968029632258286683019539125e-13, + +8.58606205627784456413590545043e-15, + -2.17486869855806187304151642387e-16, + +5.50900282836022951520265260890e-18, + -1.39544646858125233407076862641e-19, + +3.53470703962946747169322997780e-21, + -8.95351742703754685040261131811e-23, + +2.26795245233768306031095073887e-24, + -5.74479066887220244526388198761e-26, + +1.45517247561486490186626486727e-27, + -3.68599494066531017818178247991e-29, + +9.33673425709504467203255515279e-31, + -2.36502241570062993455963519637e-32, + +5.99067176248213430465991239682e-34, + -1.51745488446829026171081313586e-35, + +3.84375812545418823222944529099e-37, + -9.73635307264669103526762127925e-39, + +2.46624704420068095710640028029e-40, + -6.24707674182074369314875679472e-42, + +1.58240302446449142975108170683e-43, + -4.00827368594893596853001219052e-45, + +1.01530758555695563116307139454e-46, + -2.57180415824187174992481940976e-48, + +6.51445603523381493155843485864e-50, + -1.65013099068965245550609878048e-51 + }; // hsl_sf_hzeta_eulermaclaurin_series_coeffs + +// 4\zeta(2j)/(2\pi)^(2j) +static +double hsl_sf_hzeta_eulermaclaurin_series_majorantratios[HSL_SF_HZETA_EULERMACLAURIN_SERIES_ORDER+1]={ + -2.0, + +1.0/6.0, + +1.0/360.0, + +1.0/15120.0, + +1.0/604800.0, + +1.0/23950080.0, + +691.0/653837184000.0, + +1.0/37362124800.0, + +3617.0/5335311421440000.0, + +1.71721241125556891282718109009e-14, + +4.34973739711612374608303284773e-16, + +1.10180056567204590304053052178e-17, + +2.79089293716250466814153725281e-19, + +7.06941407925893494338645995561e-21, + +1.79070348540750937008052226362e-22, + +4.53590490467536612062190147774e-24, + +1.14895813377444048905277639752e-25, + +2.91034495122972980373252973454e-27, + +7.37198988133062035636356495982e-29, + +1.86734685141900893440651103056e-30, + +4.73004483140125986911927039274e-32, + +1.19813435249642686093198247936e-33, + +3.03490976893658052342162627173e-35, + +7.68751625090837646445889058198e-37, + +1.94727061452933820705352425585e-38, + +4.93249408840136191421280056051e-40, + +1.24941534836414873862975135893e-41, + +3.16480604892898285950216341362e-43, + +8.01654737189787193706002438098e-45, + +2.03061517111391126232614278906e-46, + +5.14360831648374349984963881946e-48, + +1.30289120704676298631168697172e-49, + +3.30026198137930491101219756091e-51 + }; // hsl_sf_hzeta_eulermaclaurin_series_majorantratios + + +extern +int hsl_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; // max_bits=\lceil{s}\rceil with \zeta(s,2)=\zeta(s)-1=GSL_DBL_EPSILON + const double ln_term0=-s*log(q); + if (ln_term0 < GSL_LOG_DBL_MIN+1.0) { + PLFIT_ERROR("underflow", PLFIT_UNDRFLOW); + } + else if (GSL_LOG_DBL_MAX-1.0 < ln_term0) { + PLFIT_ERROR("overflow", PLFIT_OVERFLOW); + } +#if 1 + else if (((max_bits < s) && (q < 1.0)) || ((0.5*max_bits < s) && (q < 0.25))) { + result->val=pow(q,-s); + result->err=2.0*GSL_DBL_EPSILON*fabs(result->val); + return (PLFIT_SUCCESS); + } + else if ((0.5*max_bits < s) && (q < 1.0)) { + const double a0=pow(q,-s); + const double p1=pow(q/(1.0+q),s); + const double p2=pow(q/(2.0+q),s); + const double ans=a0*(1.0+p1+p2); + result->val=ans; + result->err=GSL_DBL_EPSILON*(2.0+0.5*s)*fabs(result->val); + return (PLFIT_SUCCESS); + } +#endif + else { // Euler-Maclaurin summation formula + const double qshift=HSL_SF_HZETA_EULERMACLAURIN_SERIES_SHIFT+q; + const double inv_qshift=1.0/qshift; + const double sqr_inv_qshift=inv_qshift*inv_qshift; + const double inv_sm1=1.0/(s-1.0); + const double pmax=pow(qshift,-s); + double terms[HSL_SF_HZETA_EULERMACLAURIN_SERIES_SHIFT+HSL_SF_HZETA_EULERMACLAURIN_SERIES_ORDER+1]={NAN}; + double delta=NAN; + double tscp=s; + double scp=tscp; + double pcp=pmax*inv_qshift; + double ratio=scp*pcp; + size_t n=0; + size_t j=0; + double ans=0.0; + double mjr=NAN; + + for(j=0;jval=+ans; + result->err=2.0*((HSL_SF_HZETA_EULERMACLAURIN_SERIES_SHIFT+1.0)*GSL_DBL_EPSILON*fabs(ans)+mjr); + return (PLFIT_SUCCESS); + } + } + +} + +extern +double hsl_sf_hzeta(const double s, const double q) { + HSL_SF_EVAL_RESULT(hsl_sf_hzeta_e(s,q,&result)); } + +extern +int hsl_sf_hzeta_deriv_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 ln_hz_term0=-s*log(q); + if (ln_hz_term0 < GSL_LOG_DBL_MIN+1.0) { + PLFIT_ERROR("underflow", PLFIT_UNDRFLOW); + } + else if (GSL_LOG_DBL_MAX-1.0 < ln_hz_term0) { + PLFIT_ERROR("overflow", PLFIT_OVERFLOW); + } + else { // Euler-Maclaurin summation formula + const double qshift=HSL_SF_HZETA_EULERMACLAURIN_SERIES_SHIFT+q; + const double inv_qshift=1.0/qshift; + const double sqr_inv_qshift=inv_qshift*inv_qshift; + const double inv_sm1=1.0/(s-1.0); + const double pmax=pow(qshift,-s); + const double lmax=log(qshift); + double terms[HSL_SF_HZETA_EULERMACLAURIN_SERIES_SHIFT+HSL_SF_HZETA_EULERMACLAURIN_SERIES_ORDER+1]={NAN}; + double delta=NAN; + double tscp=s; + double scp=tscp; + double pcp=pmax*inv_qshift; + double lcp=lmax-1.0/s; + double ratio=scp*pcp*lcp; + double qs=NAN; + size_t n=0; + size_t j=0; + double ans=0.0; + double mjr=NAN; + + for(j=0,qs=q;jval=-ans; + result->err=2.0*((HSL_SF_HZETA_EULERMACLAURIN_SERIES_SHIFT+1.0)*GSL_DBL_EPSILON*fabs(ans)+mjr); + return (PLFIT_SUCCESS); + } + } + +} + +extern +double hsl_sf_hzeta_deriv(const double s, const double q) { + HSL_SF_EVAL_RESULT(hsl_sf_hzeta_deriv_e(s,q,&result)); } + +extern +int hsl_sf_hzeta_deriv2_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 ln_hz_term0=-s*log(q); + if (ln_hz_term0 < GSL_LOG_DBL_MIN+1.0) { + PLFIT_ERROR("underflow", PLFIT_UNDRFLOW); + } + else if (GSL_LOG_DBL_MAX-1.0 < ln_hz_term0) { + PLFIT_ERROR("overflow", PLFIT_OVERFLOW); + } + else { // Euler-Maclaurin summation formula + const double qshift=HSL_SF_HZETA_EULERMACLAURIN_SERIES_SHIFT+q; + const double inv_qshift=1.0/qshift; + const double sqr_inv_qshift=inv_qshift*inv_qshift; + const double inv_sm1=1.0/(s-1.0); + const double pmax=pow(qshift,-s); + const double lmax=log(qshift); + const double lmax_p_inv_sm1=lmax+inv_sm1; + const double sqr_inv_sm1=inv_sm1*inv_sm1; + const double sqr_lmax=lmax*lmax; + const double sqr_lmax_p_inv_sm1=lmax_p_inv_sm1*lmax_p_inv_sm1; + double terms[HSL_SF_HZETA_EULERMACLAURIN_SERIES_SHIFT+HSL_SF_HZETA_EULERMACLAURIN_SERIES_ORDER+1]={NAN}; + double delta=NAN; + double tscp=s; + double slcp=NAN; + double plcp=NAN; + double scp=tscp; + double pcp=pmax*inv_qshift; + double lcp=1.0/s-lmax; + double sqr_lcp=lmax*(lmax-2.0/s); + double ratio=scp*pcp*sqr_lcp; + double qs=NAN; + double lqs=NAN; + size_t n=0; + size_t j=0; + double ans=0.0; + double mjr=NAN; + + for(j=0,qs=q;jval=+ans; + result->err=2.0*((HSL_SF_HZETA_EULERMACLAURIN_SERIES_SHIFT+1.0)*GSL_DBL_EPSILON*fabs(ans)+mjr); + return (PLFIT_SUCCESS); + } + } + +} + +extern +double hsl_sf_hzeta_deriv2(const double s, const double q) { + HSL_SF_EVAL_RESULT(hsl_sf_hzeta_deriv2_e(s,q,&result)); } + +static inline +double hsl_sf_hZeta0_zed(const double s, const double q) { +#if 1 + const long double ld_q=(long double)(q); + const long double ld_s=(long double)(s); + const long double ld_log1prq=log1pl(1.0L/ld_q); + const long double ld_epsilon=expm1l(-ld_s*ld_log1prq); + const long double ld_z=ld_s+(ld_q+0.5L*ld_s+0.5L)*ld_epsilon; + const double z=(double)(ld_z); +#else + double z=s+(q+0.5*s+0.5)*expm1(-s*log1p(1.0/q)); +#endif + return (z); } + +// Z_{0}(s,a) = a^s \left(\frac{1}{2}+\frac{a}{s-1}\right)^{-1} \zeta(s,a) - 1 +// Z_{0}(s,a) = O\left(\frac{(s-1)s}{6a^{2}}\right) +static +int hsl_sf_hZeta0(const double s, const double q, double * value, double * abserror) { + const double criterion=ceil(10.0*s-q); + const size_t shift=(criterion<0.0)?0: + (criterionval=log1p(ln_hZeta0_value); + result->err=(2.0*GSL_DBL_EPSILON*ln_hz_coeff+hZeta0_abserror)/(1.0+ln_hZeta0_value); + } + + if (result_deriv) { + const double ld_hz_coeff2=1.0+inv_sm1*M_LOG2E; + const double ld_hz_coeff1=1.0+inv_qsm1*ld_hz_coeff2; + double hZeta1_value=NAN; + double hZeta1_abserror=NAN; + hsl_sf_hZeta1(s,2.0,M_LN2,&hZeta1_value,&hZeta1_abserror,NULL); + hZeta0_value*=hz_coeff1; + hZeta0_value+=hz_coeff0; + hZeta1_value+=1.0; + hZeta1_value*=-M_LN2*ld_hz_coeff1; + result_deriv->val=hZeta1_value/hZeta0_value; + result_deriv->err=2.0*GSL_DBL_EPSILON*fabs(result_deriv->val)+(hZeta0_abserror+hZeta1_abserror); + } + } + else { + const double ln_q=log(q); + double hZeta0_value=NAN; + double hZeta0_abserror=NAN; + hsl_sf_hZeta0(s,q,&hZeta0_value,&hZeta0_abserror); + if (result) { + const double ln_hz_term0=-s*ln_q; + const double ln_hz_term1=log(0.5+q/(s-1.0)); + result->val=ln_hz_term0+ln_hz_term1+log1p(hZeta0_value); + result->err=2.0*GSL_DBL_EPSILON*(fabs(ln_hz_term0)+fabs(ln_hz_term1))+hZeta0_abserror/(1.0+hZeta0_value); + } + if (result_deriv) { + double hZeta1_value=NAN; + double hZeta1_abserror=NAN; + double ld_hz_coeff1=NAN; + hsl_sf_hZeta1(s,q,ln_q,&hZeta1_value,&hZeta1_abserror,&ld_hz_coeff1); + result_deriv->val=-ln_q*ld_hz_coeff1*(1.0+hZeta1_value)/(1.0+hZeta0_value); + result_deriv->err=2.0*GSL_DBL_EPSILON*fabs(result_deriv->val)+(hZeta0_abserror+hZeta1_abserror); + } + } + + return (PLFIT_SUCCESS); } + +extern +double hsl_sf_lnhzeta_deriv_tuple(const double s, const double q, double * tuple0, double * tuple1) { + HSL_SF_EVAL_TUPLE_RESULT(hsl_sf_lnhzeta_deriv_tuple_e(s,q,&result0,&result1)); } + +extern +int hsl_sf_lnhzeta_e(const double s, const double q, gsl_sf_result * result) { + return (hsl_sf_lnhzeta_deriv_tuple_e(s,q,result,NULL)); } + +extern +double hsl_sf_lnhzeta(const double s, const double q) { + HSL_SF_EVAL_RESULT(hsl_sf_lnhzeta_e(s,q,&result)); } + +extern +int hsl_sf_lnhzeta_deriv_e(const double s, const double q, gsl_sf_result * result) { + return (hsl_sf_lnhzeta_deriv_tuple_e(s,q,NULL,result)); } + +extern +double hsl_sf_lnhzeta_deriv(const double s, const double q) { + HSL_SF_EVAL_RESULT(hsl_sf_lnhzeta_deriv_e(s,q,&result)); } + +// +// End of file `hsl/specfunc/hzeta.c'. diff --git a/src/rigraph/vendor/plfit/hzeta.h b/src/rigraph/vendor/plfit/hzeta.h new file mode 100644 index 0000000..31d646e --- /dev/null +++ b/src/rigraph/vendor/plfit/hzeta.h @@ -0,0 +1,96 @@ +/* This file was imported from a private scientific library + * based on GSL coined Home Scientific Libray (HSL) by its author + * Jerome Benoit; this very material is itself inspired from the + * material written by G. Jungan and distributed by GSL. + * Ultimately, some modifications were done in order to render the + * imported material independent from the rest of GSL. + */ + +/* `hsl/hsl_sf_zeta.h' C header file +// HSL - Home Scientific Library +// Copyright (C) 2005-2018 Jerome Benoit +// +// HSL is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +/* For futher details, see its source conterpart src/hzeta.c */ + +/* Author: Jerome G. Benoit < jgmbenoit _at_ rezozer _dot_ net > */ + +#ifndef __HZETA_H__ +#define __HZETA_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 hsl_sf_hzeta(const double s, const double q); + +/* First Derivative of Hurwitz Zeta Function + * zeta'(s,q) = - Sum[ Ln(k+q)/(k+q)^(s), {k,0,Infinity} ] + * + * s > 1.0, q > 0.0 + */ +double hsl_sf_hzeta_deriv(const double s, const double q); + +/* Second Derivative of Hurwitz Zeta Function + * zeta''(s,q) = + Sum[ Ln(k+q)^2/(k+q)^(s), {k,0,Infinity} ] + * + * s > 1.0, q > 0.0 + */ +double hsl_sf_hzeta_deriv2(const double s, const double q); + +/* Logarithm of Hurwitz Zeta Function + * lnzeta(s,q) = ln(zeta(s,q)) + * + * s > 1.0, q > 0.0 (and q >> 1) + */ +double hsl_sf_lnhzeta(const double s, const double q); + +/* Logarithmic Derivative of Hurwitz Zeta Function + * lnzeta'(s,q) = zeta'(s,q)/zeta(s,q) + * + * s > 1.0, q > 0.0 (and q >> 1) + */ +double hsl_sf_lnhzeta_deriv(const double s, const double q); + +/* Logarithm and Logarithmic Derivative of Hurwitz Zeta Function: + * nonredundant computation version: + * - lnzeta(s,q) and lnzeta'(s,q) are stored in *deriv0 and *deriv1, respectively; + * - the return value and the value stored in *deriv0 are the same; + * - deriv0 and deriv1 must be effective pointers, that is, not the NULL pointer. + * + * s > 1.0, q > 0.0 (and q >> 1) + */ +double hsl_sf_lnhzeta_deriv_tuple(const double s, const double q, double * deriv0, double * deriv1); + + +__END_DECLS + +#endif // __HZETA_H__ diff --git a/src/rigraph/vendor/plfit/kolmogorov.c b/src/rigraph/vendor/plfit/kolmogorov.c new file mode 100644 index 0000000..432878f --- /dev/null +++ b/src/rigraph/vendor/plfit/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 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 +#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((double) 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/src/rigraph/vendor/plfit/kolmogorov.h b/src/rigraph/vendor/plfit/kolmogorov.h new file mode 100644 index 0000000..358e697 --- /dev/null +++ b/src/rigraph/vendor/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 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 + +__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/src/rigraph/vendor/plfit/lbfgs.c b/src/rigraph/vendor/plfit/lbfgs.c new file mode 100644 index 0000000..3fcd2c3 --- /dev/null +++ b/src/rigraph/vendor/plfit/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. + Mathematics of Computation, Vol. 35, No. 151, pp. 773--782, 1980. + - Dong C. Liu and Jorge Nocedal. + On the limited memory BFGS method for large scale optimization. + Mathematical Programming 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. + Numerical Methods for Unconstrained Optimization and Nonlinear + Equations, Englewood Cliffs, 1983. + - 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. + +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 Proceedings of the 24th International Conference on Machine + Learning (ICML 2007), 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 +#endif + +#include +#include +#include + +#include "lbfgs.h" +#include "platform.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, ¶m); + } else { + ls = linesearch(n, x, &fx, g, d, &step, xp, pg, w, &cd, ¶m); + 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) { + ret = cd.proc_progress(cd.instance, x, g, fx, xnorm, gnorm, step, cd.n, k, ls); + if (ret) { + 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); + } + } +} + + + +/** + * 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/src/rigraph/vendor/plfit/lbfgs.h b/src/rigraph/vendor/plfit/lbfgs.h new file mode 100644 index 0000000..f26ae87 --- /dev/null +++ b/src/rigraph/vendor/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, + +
+    minimize F(x), x = (x1, x2, ..., xN),
+
+ +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: +- Optimization with L1-norm (Orthant-Wise Limited-memory Quasi-Newton + (OWL-QN) method): + 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). +- Clean C code: + 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. +- Callback interface: + 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. +- Thread safe: + The library is thread-safe, which is the secondary gain from the callback + interface. +- Cross platform. The source code can be compiled on Microsoft Visual + Studio 2005, GNU C Compiler (gcc), etc. +- Configurable precision: A user can choose single-precision (float) + or double-precision (double) accuracy by changing ::LBFGS_FLOAT macro. +- SSE/SSE2 optimization: + 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: +- CRFsuite: A fast implementation of Conditional Random Fields (CRFs) +- Classias: A collection of machine-learning algorithms for classification +- mlegp: an R package for maximum likelihood estimates for Gaussian processes +- imaging2: the imaging2 class library +- Algorithm::LBFGS - Perl extension for L-BFGS +- YAP-LBFGS (an interface to call libLBFGS from YAP Prolog) + +@section download Download + +- Source code + +libLBFGS is distributed under the term of the +MIT license. + +@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, the OW-LQN method must be used with the + backtracking algorithm (::LBFGS_LINESEARCH_BACKTRACKING), 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. + Mathematics of Computation, Vol. 35, No. 151, pp. 773--782, 1980. + - Dong C. Liu and Jorge Nocedal. + On the limited memory BFGS method for large scale optimization. + Mathematical Programming 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. + Numerical Methods for Unconstrained Optimization and Nonlinear + Equations, Englewood Cliffs, 1983. + - 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. + +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 Proceedings of the 24th International Conference on Machine + Learning (ICML 2007), 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 + +- L-BFGS by Jorge Nocedal. +- Orthant-Wise Limited-memory Quasi-Newton Optimizer for L1-regularized Objectives by Galen Andrew. +- C port (via f2c) by Taku Kudo. +- C#/C++/Delphi/VisualBasic6 port in ALGLIB. +- Computational Crystallography Toolbox includes + scitbx::lbfgs. +*/ + +#endif/*__LBFGS_H__*/ diff --git a/src/rigraph/vendor/plfit/mt.c b/src/rigraph/vendor/plfit/mt.c new file mode 100644 index 0000000..615c6bc --- /dev/null +++ b/src/rigraph/vendor/plfit/mt.c @@ -0,0 +1,93 @@ +/* mt.c + * + * Mersenne Twister random number generator, based on the implementation of + * Michael Brundage (which has been placed in the public domain). + * + * Author: Tamas Nepusz (original by Michael Brundage) + * + * See the following URL for the original implementation: + * http://www.qbrundage.com/michaelb/pubs/essays/random_number_generation.html + * + * This file has been placed in the public domain. + */ + +#include + +#include "igraph_random.h" + +#include "plfit_mt.h" + +static uint16_t get_random_uint16() { + return RNG_INT31() & 0xFFFF; +} + +void plfit_mt_init(plfit_mt_rng_t* rng) { + plfit_mt_init_from_rng(rng, 0); +} + +void plfit_mt_init_from_rng(plfit_mt_rng_t* rng, plfit_mt_rng_t* seeder) { + int i; + + if (seeder == 0) { + for (i = 0; i < PLFIT_MT_LEN; i++) { + /* RAND_MAX is guaranteed to be at least 32767, so we can use two + * calls to rand() to produce a random 32-bit number */ + rng->mt_buffer[i] = (get_random_uint16() << 16) + get_random_uint16(); + } + } else { + for (i = 0; i < PLFIT_MT_LEN; i++) { + rng->mt_buffer[i] = plfit_mt_random(seeder); + } + } + + rng->mt_index = 0; +} + +#define MT_IA 397 +#define MT_IB (PLFIT_MT_LEN - MT_IA) +#define UPPER_MASK 0x80000000 +#define LOWER_MASK 0x7FFFFFFF +#define MATRIX_A 0x9908B0DF +#define TWIST(b,i,j) ((b)[i] & UPPER_MASK) | ((b)[j] & LOWER_MASK) +#define MAGIC(s) (((s)&1)*MATRIX_A) + +uint32_t plfit_mt_random(plfit_mt_rng_t* rng) { + uint32_t * b = rng->mt_buffer; + int idx = rng->mt_index; + uint32_t s; + int i; + + if (idx == PLFIT_MT_LEN * sizeof(uint32_t)) { + idx = 0; + i = 0; + for (; i < MT_IB; i++) { + s = TWIST(b, i, i+1); + b[i] = b[i + MT_IA] ^ (s >> 1) ^ MAGIC(s); + } + for (; i < PLFIT_MT_LEN-1; i++) { + s = TWIST(b, i, i+1); + b[i] = b[i - MT_IB] ^ (s >> 1) ^ MAGIC(s); + } + + s = TWIST(b, PLFIT_MT_LEN-1, 0); + b[PLFIT_MT_LEN-1] = b[MT_IA-1] ^ (s >> 1) ^ MAGIC(s); + } + + rng->mt_index = idx + sizeof(uint32_t); + return *(uint32_t *)((unsigned char *)b + idx); + /* + Matsumoto and Nishimura additionally confound the bits returned to the caller + but this doesn't increase the randomness, and slows down the generator by + as much as 25%. So I omit these operations here. + + r ^= (r >> 11); + r ^= (r << 7) & 0x9D2C5680; + r ^= (r << 15) & 0xEFC60000; + r ^= (r >> 18); + */ +} + + +double plfit_mt_uniform_01(plfit_mt_rng_t* rng) { + return ((double)plfit_mt_random(rng)) / PLFIT_MT_RAND_MAX; +} diff --git a/src/rigraph/vendor/plfit/options.c b/src/rigraph/vendor/plfit/options.c new file mode 100644 index 0000000..1665560 --- /dev/null +++ b/src/rigraph/vendor/plfit/options.c @@ -0,0 +1,52 @@ +/* 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 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 "plfit_error.h" +#include "plfit.h" + +const plfit_continuous_options_t plfit_continuous_default_options = { + /* .finite_size_correction = */ 0, + /* .xmin_method = */ PLFIT_DEFAULT_CONTINUOUS_METHOD, + /* .p_value_method = */ PLFIT_DEFAULT_P_VALUE_METHOD, + /* .p_value_precision = */ 0.01, + /* .rng = */ 0 +}; + +const plfit_discrete_options_t plfit_discrete_default_options = { + /* .finite_size_correction = */ 0, + /* .alpha_method = */ PLFIT_DEFAULT_DISCRETE_METHOD, + /* .alpha = */ { + /* .min = */ 1.01, + /* .max = */ 5, + /* .step = */ 0.01 + }, + /* .p_value_method = */ PLFIT_DEFAULT_P_VALUE_METHOD, + /* .p_value_precision = */ 0.01, + /* .rng = */ 0 +}; + +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/src/rigraph/vendor/plfit/platform.c b/src/rigraph/vendor/plfit/platform.c new file mode 100644 index 0000000..e9560e4 --- /dev/null +++ b/src/rigraph/vendor/plfit/platform.c @@ -0,0 +1,36 @@ +/* platform.c + * + * Copyright (C) 2014 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 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 "platform.h" + +#ifdef _MSC_VER + +inline double _plfit_fmin(double a, double b) { + return (a < b) ? a : b; +} + +inline double _plfit_round(double x) { + return floor(x+0.5); +} + +#endif + +/* Dummy function to prevent a warning when compiling with Clang - the file + * would contain no symbols */ +void _plfit_i_unused() {} diff --git a/src/rigraph/vendor/plfit/platform.h b/src/rigraph/vendor/plfit/platform.h new file mode 100644 index 0000000..6512955 --- /dev/null +++ b/src/rigraph/vendor/plfit/platform.h @@ -0,0 +1,69 @@ +/* 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 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 + +__BEGIN_DECLS + +#ifdef _MSC_VER +#include + +#define snprintf _snprintf +#define inline __inline + +#ifndef isfinite +# define isfinite(x) _finite(x) +#endif + +extern double _plfit_fmin(double a, double b); +extern double _plfit_round(double x); + +#define fmin _plfit_fmin +#define round _plfit_round + +#endif /* _MSC_VER */ + +#ifndef isnan +# define isnan(x) ((x) != (x)) +#endif + +#ifndef INFINITY +# define INFINITY (1.0/0.0) +#endif + +#ifndef NAN +# define NAN ((double)0.0 / (double)DBL_MIN) +#endif + +__END_DECLS + +#endif /* __PLATFORM_H__ */ diff --git a/src/rigraph/vendor/plfit/plfit.c b/src/rigraph/vendor/plfit/plfit.c new file mode 100644 index 0000000..266a9b0 --- /dev/null +++ b/src/rigraph/vendor/plfit/plfit.c @@ -0,0 +1,1342 @@ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* 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 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 +#include +#include +#include +#include +#include +#include "plfit_error.h" +#include "gss.h" +#include "lbfgs.h" +#include "platform.h" +#include "plfit.h" +#include "kolmogorov.h" +#include "plfit_sampling.h" +#include "hzeta.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 plfit_i_resample_continuous(double* xs_head, size_t num_smaller, + size_t n, double alpha, double xmin, size_t num_samples, plfit_mt_rng_t* rng, + double* result); +static int plfit_i_resample_discrete(double* xs_head, size_t num_smaller, + size_t n, double alpha, double xmin, size_t num_samples, plfit_mt_rng_t* rng, + double* result); + +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); +} + +static int plfit_i_copy_and_sort(double* xs, size_t n, double** result) { + *result = (double*)malloc(sizeof(double) * n); + if (*result == 0) { + PLFIT_ERROR("cannot create sorted copy of input data", PLFIT_ENOMEM); + } + + memcpy(*result, xs, sizeof(double) * n); + qsort(*result, n, sizeof(double), double_comparator); + + return PLFIT_SUCCESS; +} + +/** + * Given an unsorted array of doubles, counts how many elements there are that + * are smaller than a given value. + * + * \param begin pointer to the beginning of the array + * \param end pointer to the first element after the end of the array + * \param xmin the threshold value + * + * \return the nubmer of elements in the array that are smaller than the given + * value. + */ +static size_t count_smaller(double* begin, double* end, double xmin) { + double* p; + size_t counter = 0; + + for (p = begin; p < end; p++) { + if (*p < xmin) { + counter++; + } + } + + return counter; +} + +/** + * Given an unsorted array of doubles, return another array that contains the + * elements that are smaller than a given value + * + * \param begin pointer to the beginning of the array + * \param end pointer to the first element after the end of the array + * \param xmin the threshold value + * \param result_length if not \c NULL, the number of unique elements in the + * given array is returned here + * + * \return pointer to the head of the new array or 0 if there is not enough + * memory + */ +static double* extract_smaller(double* begin, double* end, double xmin, + size_t* result_length) { + size_t counter = count_smaller(begin, end, xmin); + double *p, *result; + + result = calloc(counter, sizeof(double)); + if (result == 0) + return 0; + + for (p = result; begin < end; begin++) { + if (*begin < xmin) { + *p = *begin; + p++; + } + } + + if (result_length) { + *result_length = counter; + } + + return result; +} + +/** + * 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. It is left unchanged if + * the function returns with an error. + * + * \return pointer to the head of the new array or 0 if there is not enough + * memory + */ +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; + if (result_length != 0) { + *result_length = 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 **********/ + +static 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; +} + +static 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; +} + +static 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; +} + +static 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; +} + +static int plfit_i_calculate_p_value_continuous(double* xs, size_t n, + const plfit_continuous_options_t *options, plfit_bool_t xmin_fixed, + plfit_result_t *result) { + long int num_trials; + long int successes = 0; + double *xs_head; + size_t num_smaller; + plfit_continuous_options_t options_no_p_value = *options; + int retval = PLFIT_SUCCESS; + + if (options->p_value_method == PLFIT_P_VALUE_SKIP) { + result->p = NAN; + return PLFIT_SUCCESS; + } + + if (options->p_value_method == PLFIT_P_VALUE_APPROXIMATE) { + num_smaller = count_smaller(xs, xs + n, result->xmin); + result->p = plfit_ks_test_one_sample_p(result->D, n - num_smaller); + return PLFIT_SUCCESS; + } + + options_no_p_value.p_value_method = PLFIT_P_VALUE_SKIP; + num_trials = (long int)(0.25 / options->p_value_precision / options->p_value_precision); + if (num_trials <= 0) { + PLFIT_ERROR("invalid p-value precision", PLFIT_EINVAL); + } + + /* Extract the head of xs that contains elements smaller than xmin */ + xs_head = extract_smaller(xs, xs+n, result->xmin, &num_smaller); + if (xs_head == 0) + PLFIT_ERROR("cannot calculate exact p-value", PLFIT_ENOMEM); + +#ifdef _OPENMP +#pragma omp parallel +#endif + { + /* Parallel section starts here. If we are compiling using OpenMP, each + * thread will use its own RNG that is seeded from the master RNG. If + * we are compiling without OpenMP, there is only one thread and it uses + * the master RNG. This section must be critical to ensure that only one + * thread is using the master RNG at the same time. */ +#ifdef _OPENMP + plfit_mt_rng_t private_rng; +#endif + plfit_mt_rng_t *p_rng; + double *ys; + long int i; + plfit_result_t result_synthetic; + +#ifdef _OPENMP +#pragma omp critical + { + p_rng = &private_rng; + plfit_mt_init_from_rng(p_rng, options->rng); + } +#else + p_rng = options->rng; +#endif + + /* Allocate memory to sample into */ + ys = calloc(n, sizeof(double)); + if (ys == 0) { + retval = PLFIT_ENOMEM; + } else { + /* The main for loop starts here. */ +#ifdef _OPENMP +#pragma omp for reduction(+:successes) +#endif + for (i = 0; i < num_trials; i++) { + plfit_i_resample_continuous(xs_head, num_smaller, n, result->alpha, + result->xmin, n, p_rng, ys); + if (xmin_fixed) { + plfit_estimate_alpha_continuous(ys, n, result->xmin, + &options_no_p_value, &result_synthetic); + } else { + plfit_continuous(ys, n, &options_no_p_value, &result_synthetic); + } + if (result_synthetic.D > result->D) + successes++; + } + free(ys); + } + + /* End of parallelized part */ + } + + free(xs_head); + + if (retval == PLFIT_SUCCESS) { + result->p = successes / ((double)num_trials); + } else { + PLFIT_ERROR("cannot calculate exact p-value", retval); + } + + return retval; +} + +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_sorted(double* xs, size_t n, double xmin, + const plfit_continuous_options_t* options, plfit_result_t *result) { + double *begin, *end; + + if (!options) + options = &plfit_continuous_default_options; + + begin = xs; + end = xs + n; + while (begin < end && *begin < xmin) + begin++; + + PLFIT_CHECK(plfit_i_estimate_alpha_continuous_sorted(begin, end-begin, + xmin, &result->alpha)); + PLFIT_CHECK(plfit_i_ks_test_continuous(begin, end, result->alpha, + xmin, &result->D)); + + if (options->finite_size_correction) + plfit_i_perform_finite_size_correction(result, end-begin); + result->xmin = xmin; + + PLFIT_CHECK(plfit_log_likelihood_continuous(begin, end-begin, result->alpha, + result->xmin, &result->L)); + PLFIT_CHECK(plfit_i_calculate_p_value_continuous(xs, n, options, 1, result)); + + 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; + + PLFIT_CHECK(plfit_i_copy_and_sort(xs, n, &xs_copy)); + PLFIT_CHECK(plfit_estimate_alpha_continuous_sorted(xs_copy, n, xmin, + options, result)); + free(xs_copy); + + 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 **probes; /**< Pointers to the elements of the array that will be probed */ + size_t num_probes; /**< Number of probes */ + plfit_result_t last; /**< Result of the last evaluation */ +} plfit_continuous_xmin_opt_data_t; + +static 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->probes[(long int)x]; + + data->last.xmin = *begin; + +#ifdef PLFIT_DEBUG + printf("Trying with probes[%ld] = %.4f\n", (long int)x, *begin); +#endif + + plfit_i_estimate_alpha_continuous_sorted(begin, 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; +} + +static 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; +} + +static int plfit_i_continuous_xmin_opt_linear_scan( + plfit_continuous_xmin_opt_data_t* opt_data, plfit_result_t* best_result, + size_t* best_n) { + /* this must be signed because OpenMP with Windows MSVC needs signed for + * loop index variables. ssize_t will not work because that is a POSIX + * extension */ + ptrdiff_t i = 0; /* initialize to work around incorrect warning issued by Clang 9.0 */ + plfit_result_t global_best_result; + size_t global_best_n; + + /* Prepare some variables */ + global_best_n = 0; + global_best_result.D = DBL_MAX; + global_best_result.xmin = 0; + global_best_result.alpha = 0; + + /* Due to the OpenMP parallelization, we do things as follows. Each + * OpenMP thread will search for the best D-score on its own and store + * the result in a private local_best_result variable. The end of the + * parallel block contains a critical section that threads will enter + * one by one and compare their private local_best_result with a + * global_best that is shared among the threads. + */ +#ifdef _OPENMP +#pragma omp parallel shared(global_best_result, global_best_n) private(i) firstprivate(opt_data) +#endif + { + /* These variables are private since they are declared within the + * parallel block */ + plfit_result_t local_best_result; + plfit_continuous_xmin_opt_data_t local_opt_data = *opt_data; + size_t local_best_n; + + /* Initialize the local_best_result and local_best_n variables */ + local_best_n = 0; + local_best_result.D = DBL_MAX; + local_best_result.xmin = 0; + local_best_result.alpha = 0; + local_best_result.p = NAN; + local_best_result.L = NAN; + + /* The range of the for loop below is divided among the threads. + * nowait means that there will be no implicit barrier at the end + * of the loop so threads that get there earlier can enter the + * critical section without waiting for the others */ +#ifdef _OPENMP +#pragma omp for nowait schedule(dynamic,10) +#endif + for (i = 0; i < local_opt_data.num_probes-1; i++) { + plfit_i_continuous_xmin_opt_evaluate(&local_opt_data, i); + if (local_opt_data.last.D < local_best_result.D) { +#ifdef PLFIT_DEBUG + printf("Found new local best at %g with D=%g\n", + local_opt_data.last.xmin, local_opt_data.last.D); +#endif + local_best_result = local_opt_data.last; + local_best_n = local_opt_data.end - local_opt_data.probes[i] + 1; + } + } + + /* Critical section that finds the global best result from the + * local ones collected by each thread */ +#ifdef _OPENMP +#pragma omp critical +#endif + if (local_best_result.D < global_best_result.D) { + global_best_result = local_best_result; + global_best_n = local_best_n; +#ifdef PLFIT_DEBUG + printf("Found new global best at %g with D=%g\n", global_best_result.xmin, + global_best_result.D); +#endif + } + } + + *best_result = global_best_result; + *best_n = global_best_n; + +#ifdef PLFIT_DEBUG + printf("Returning global best: %g\n", best_result->xmin); +#endif + + return PLFIT_SUCCESS; +} + +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 = { + /* alpha = */ NAN, + /* xmin = */ NAN, + /* L = */ NAN, + /* D = */ NAN, + /* p = */ NAN + }; + + int success; + size_t i, best_n, num_uniques = 0; + double x, *px, **uniques; + + DATA_POINTS_CHECK; + + /* Sane defaults */ + best_n = n; + if (!options) + options = &plfit_continuous_default_options; + + /* Make a copy of xs and sort it */ + PLFIT_CHECK(plfit_i_copy_and_sort(xs, n, &opt_data.begin)); + 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. */ + uniques = unique_element_pointers(opt_data.begin, opt_data.end, &num_uniques); + if (uniques == 0) + PLFIT_ERROR("cannot fit continuous power-law", PLFIT_ENOMEM); + + /* We will now determine the best xmin that yields the lowest D-score. The + * 'success' variable will denote whether the search procedure we tried was + * successful. If it is false after having exhausted all options, we fall + * back to a linear search. */ + success = 0; + switch (options->xmin_method) { + case PLFIT_GSS_OR_LINEAR: + /* Try golden section search first. */ + if (num_uniques > 5) { + opt_data.probes = uniques; + opt_data.num_probes = num_uniques; + gss_parameter_init(&gss_param); + success = (gss(0, opt_data.num_probes-5, &x, 0, + plfit_i_continuous_xmin_opt_evaluate, + plfit_i_continuous_xmin_opt_progress, &opt_data, &gss_param) == 0); + if (success) { + px = opt_data.probes[(int)x]; + best_n = opt_data.end-px+1; + best_result = opt_data.last; + } + } + break; + + case PLFIT_STRATIFIED_SAMPLING: + if (num_uniques >= 50) { + /* Try stratified sampling to narrow down the interval where the minimum + * is likely to reside. We check 10% of the unique items, distributed + * evenly, find the one with the lowest D-score, and then check the + * area around it more thoroughly. */ + const size_t subdivision_length = 10; + size_t num_strata = num_uniques / subdivision_length; + double **strata = calloc(num_strata, sizeof(double*)); + int error_code; + + for (i = 0; i < num_strata; i++) { + strata[i] = uniques[i * subdivision_length]; + } + + opt_data.probes = strata; + opt_data.num_probes = num_strata; + error_code = plfit_i_continuous_xmin_opt_linear_scan(&opt_data, &best_result, &best_n); + if (error_code != PLFIT_SUCCESS) { + free(strata); + return error_code; + } + + opt_data.num_probes = 0; + for (i = 0; i < num_strata; i++) { + if (*strata[i] == best_result.xmin) { + /* Okay, scan more thoroughly from strata[i-1] to strata[i+1], + * which is from uniques[(i-1)*subdivision_length] to + * uniques[(i+1)*subdivision_length */ + opt_data.probes = uniques + (i > 0 ? (i-1)*subdivision_length : 0); + opt_data.num_probes = 0; + if (i != 0) + opt_data.num_probes += subdivision_length; + if (i != num_strata-1) + opt_data.num_probes += subdivision_length; + break; + } + } + + free(strata); + if (opt_data.num_probes > 0) { + /* Do a strict linear scan in the subrange determined above */ + PLFIT_CHECK( + plfit_i_continuous_xmin_opt_linear_scan( + &opt_data, &best_result, &best_n + ) + ); + success = 1; + } else { + /* This should not happen, but we handle it anyway */ + success = 0; + } + } + break; + + default: + /* Just use the linear search */ + break; + } + + if (!success) { + /* More advanced search methods failed or were skipped; try linear search */ + opt_data.probes = uniques; + opt_data.num_probes = num_uniques; + PLFIT_CHECK(plfit_i_continuous_xmin_opt_linear_scan(&opt_data, &best_result, &best_n)); + success = 1; + } + + /* Get rid of the uniques array, we don't need it any more */ + free(uniques); + + /* Sort out the result */ + *result = best_result; + if (options->finite_size_correction) + plfit_i_perform_finite_size_correction(result, best_n); + + PLFIT_CHECK(plfit_log_likelihood_continuous(opt_data.begin + n - best_n, best_n, + result->alpha, result->xmin, &result->L)); + PLFIT_CHECK(plfit_i_calculate_p_value_continuous(opt_data.begin, n, options, 0, result)); + + /* 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; + +static double plfit_i_logsum_discrete(double* begin, double* end, double xmin) { + double logsum = 0.0; + for (; begin != end; begin++) + logsum += log(*begin); + return logsum; +} + +static 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; +} + +static 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 */ + double lnhzeta_x=NAN; + double lnhzeta_deriv_x=NAN; + + 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; + result = x[0] * data->logsum + data->m * hsl_sf_lnhzeta(x[0], data->xmin); + } else { + hsl_sf_lnhzeta_deriv_tuple(x[0], data->xmin, &lnhzeta_x, &lnhzeta_deriv_x); + g[0] = data->logsum + data->m * lnhzeta_deriv_x; + result = x[0] * data->logsum + data->m * lnhzeta_x; + } + +#ifdef PLFIT_DEBUG + printf(" - Gradient: %.4f\n", g[0]); + printf(" - Result: %.4f\n", result); +#endif + + return result; +} + +static 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; +} + +static 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 * hsl_sf_lnhzeta(curr_alpha, xmin); + if (L > L_max) { + L_max = L; + best_alpha = curr_alpha; + } + } + + *alpha = best_alpha; + + return PLFIT_SUCCESS; +} + +static 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(¶m); + 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, ¶m); + + if (ret < 0 && + ret != LBFGSERR_ROUNDING_ERROR && + ret != LBFGSERR_MAXIMUMLINESEARCH && + ret != LBFGSERR_MINIMUMSTEP && + 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; +} + +static 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); + } +} + +static 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, lnhzeta, x; + int m = 0; + + n = xs_end - xs; + lnhzeta = hsl_sf_lnhzeta(alpha, xmin); + + while (xs < xs_end) { + double d; + + x = *xs; + + /* Re the next line: this used to be the following: + * + * fabs( 1 - hzeta(alpha, x) / hzeta(alpha, xmin) - m / n) + * + * However, using the Hurwitz zeta directly sometimes yields + * underflows (see Github pull request #17 and related issues). + * hzeta(alpha, x) / hzeta(alpha, xmin) can be replaced with + * exp(lnhzeta(alpha, x) - lnhzeta(alpha, xmin)), but then + * we have 1 - exp(something), which is better to calculate + * with a dedicated expm1() function. + */ + d = fabs( expm1( hsl_sf_lnhzeta(alpha, x) - lnhzeta ) + m / n); + + if (d > result) + result = d; + + do { + xs++; m++; + } while (xs < xs_end && *xs == x); + } + + *D = result; + + return PLFIT_SUCCESS; +} + +static int plfit_i_calculate_p_value_discrete(double* xs, size_t n, + const plfit_discrete_options_t* options, plfit_bool_t xmin_fixed, + plfit_result_t *result) { + long int num_trials; + long int successes = 0; + double *xs_head; + size_t num_smaller; + plfit_discrete_options_t options_no_p_value = *options; + int retval = PLFIT_SUCCESS; + + if (options->p_value_method == PLFIT_P_VALUE_SKIP) { + /* skipping p-value calculation */ + result->p = NAN; + return PLFIT_SUCCESS; + } + + if (options->p_value_method == PLFIT_P_VALUE_APPROXIMATE) { + /* p-value approximation; most likely an upper bound */ + num_smaller = count_smaller(xs, xs + n, result->xmin); + result->p = plfit_ks_test_one_sample_p(result->D, n - num_smaller); + return PLFIT_SUCCESS; + } + + options_no_p_value.p_value_method = PLFIT_P_VALUE_SKIP; + num_trials = (long int)(0.25 / options->p_value_precision / options->p_value_precision); + if (num_trials <= 0) { + PLFIT_ERROR("invalid p-value precision", PLFIT_EINVAL); + } + + /* Extract the head of xs that contains elements smaller than xmin */ + xs_head = extract_smaller(xs, xs+n, result->xmin, &num_smaller); + if (xs_head == 0) + PLFIT_ERROR("cannot calculate exact p-value", PLFIT_ENOMEM); + +#ifdef _OPENMP +#pragma omp parallel +#endif + { + /* Parallel section starts here. If we are compiling using OpenMP, each + * thread will use its own RNG that is seeded from the master RNG. If + * we are compiling without OpenMP, there is only one thread and it uses + * the master RNG. This section must be critical to ensure that only one + * thread is using the master RNG at the same time. */ +#ifdef _OPENMP + plfit_mt_rng_t private_rng; +#endif + plfit_mt_rng_t *p_rng; + double *ys; + long int i; + plfit_result_t result_synthetic; + +#ifdef _OPENMP +#pragma omp critical + { + p_rng = &private_rng; + plfit_mt_init_from_rng(p_rng, options->rng); + } +#else + p_rng = options->rng; +#endif + + /* Allocate memory to sample into */ + ys = calloc(n, sizeof(double)); + if (ys == 0) { + retval = PLFIT_ENOMEM; + } else { + /* The main for loop starts here. */ +#ifdef _OPENMP +#pragma omp for reduction(+:successes) +#endif + for (i = 0; i < num_trials; i++) { + plfit_i_resample_discrete(xs_head, num_smaller, n, result->alpha, + result->xmin, n, p_rng, ys); + if (xmin_fixed) { + plfit_estimate_alpha_discrete(ys, n, result->xmin, + &options_no_p_value, &result_synthetic); + } else { + plfit_discrete(ys, n, &options_no_p_value, &result_synthetic); + } + if (result_synthetic.D > result->D) + successes++; + } + + free(ys); + } + + /* End of parallelized part */ + } + + free(xs_head); + + if (retval == PLFIT_SUCCESS) { + result->p = successes / ((double)num_trials); + } else { + PLFIT_ERROR("cannot calculate exact p-value", retval); + } + + return retval; +} + +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 * hsl_sf_lnhzeta(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, *begin, *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); + } + } + + PLFIT_CHECK(plfit_i_copy_and_sort(xs, n, &xs_copy)); + + begin = xs_copy; end = xs_copy + n; + while (begin < end && *begin < xmin) + begin++; + + PLFIT_CHECK(plfit_i_estimate_alpha_discrete(begin, end-begin, xmin, &result->alpha, + options, /* sorted = */ 1)); + PLFIT_CHECK(plfit_i_ks_test_discrete(begin, end, result->alpha, xmin, &result->D)); + + result->xmin = xmin; + if (options->finite_size_correction) + plfit_i_perform_finite_size_correction(result, end-begin); + + PLFIT_CHECK(plfit_log_likelihood_discrete(begin, end-begin, result->alpha, + result->xmin, &result->L)); + PLFIT_CHECK(plfit_i_calculate_p_value_discrete(xs, n, options, 1, result)); + + 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; + int 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); + } + } + + PLFIT_CHECK(plfit_i_copy_and_sort(xs, n, &xs_copy)); + + best_result.D = DBL_MAX; + best_result.xmin = 1; + best_result.alpha = 1; + best_n = 0; + + /* Skip initial values from xs_copy until we get to a positive element or + * until we reach the end of the array */ + px = xs_copy; end = px + n; end_xmin = end - 1; + while (px < end && *px < 1) { + px++; + } + + /* Make sure there are at least three distinct values if possible */ + m = px - xs_copy; + prev_x = *end_xmin; + while (end_xmin > px && *end_xmin == prev_x) { + end_xmin--; + } + prev_x = *end_xmin; + while (end_xmin > px && *end_xmin == prev_x) { + end_xmin--; + } + + prev_x = 0; + end_xmin++; + while (px < end_xmin) { + while (px < end_xmin && *px == prev_x) { + px++; m++; + } + + PLFIT_CHECK( + plfit_i_estimate_alpha_discrete( + px, n-m, *px, &curr_alpha, options, /* sorted = */ 1 + ) + ); + PLFIT_CHECK( + 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); + + PLFIT_CHECK(plfit_log_likelihood_discrete(xs_copy+(n-best_n), best_n, + result->alpha, result->xmin, &result->L)); + PLFIT_CHECK(plfit_i_calculate_p_value_discrete(xs_copy, n, options, 0, result)); + + free(xs_copy); + + return PLFIT_SUCCESS; +} + +/***** resampling routines to generate synthetic replicates ****/ + +static int plfit_i_resample_continuous(double* xs_head, size_t num_smaller, + size_t n, double alpha, double xmin, size_t num_samples, plfit_mt_rng_t* rng, + double* result) +{ + size_t num_orig_samples, i; + + /* Calculate how many samples have to be drawn from xs_head */ + num_orig_samples = (size_t) plfit_rbinom(num_samples, num_smaller / (double)n, rng); + + /* Draw the samples from xs_head */ + for (i = 0; i < num_orig_samples; i++, result++) { + *result = xs_head[(size_t)plfit_runif(0, num_smaller, rng)]; + } + + /* Draw the remaining samples from the fitted distribution */ + PLFIT_CHECK(plfit_rpareto_array(xmin, alpha-1, num_samples-num_orig_samples, rng, + result)); + + return PLFIT_SUCCESS; +} + +int plfit_resample_continuous(double* xs, size_t n, double alpha, double xmin, + size_t num_samples, plfit_mt_rng_t* rng, double* result) { + double *xs_head; + size_t num_smaller = 0; + int retval; + + /* Extract the head of xs that contains elements smaller than xmin */ + xs_head = extract_smaller(xs, xs+n, xmin, &num_smaller); + if (xs_head == 0) + PLFIT_ERROR("cannot resample continuous dataset", PLFIT_ENOMEM); + + retval = plfit_i_resample_continuous(xs_head, num_smaller, n, alpha, xmin, + num_samples, rng, result); + + /* Free xs_head; we don't need it any more */ + free(xs_head); + + return retval; +} + +static int plfit_i_resample_discrete(double* xs_head, size_t num_smaller, size_t n, + double alpha, double xmin, size_t num_samples, plfit_mt_rng_t* rng, + double* result) +{ + size_t num_orig_samples, i; + + /* Calculate how many samples have to be drawn from xs_head */ + num_orig_samples = (size_t) plfit_rbinom(num_samples, num_smaller / (double)n, rng); + + /* Draw the samples from xs_head */ + for (i = 0; i < num_orig_samples; i++, result++) { + *result = xs_head[(size_t)plfit_runif(0, num_smaller, rng)]; + } + + /* Draw the remaining samples from the fitted distribution */ + PLFIT_CHECK(plfit_rzeta_array((long int)xmin, alpha, + num_samples-num_orig_samples, rng, result)); + + return PLFIT_SUCCESS; +} + +int plfit_resample_discrete(double* xs, size_t n, double alpha, double xmin, + size_t num_samples, plfit_mt_rng_t* rng, double* result) { + double *xs_head; + size_t num_smaller = 0; + int retval; + + /* Extract the head of xs that contains elements smaller than xmin */ + xs_head = extract_smaller(xs, xs+n, xmin, &num_smaller); + if (xs_head == 0) + PLFIT_ERROR("cannot resample discrete dataset", PLFIT_ENOMEM); + + retval = plfit_i_resample_discrete(xs_head, num_smaller, n, alpha, xmin, + num_samples, rng, result); + + /* Free xs_head; we don't need it any more */ + free(xs_head); + + return retval; +} + +/******** calculating the p-value of a fitted model only *******/ + +int plfit_calculate_p_value_continuous(double* xs, size_t n, + const plfit_continuous_options_t* options, plfit_bool_t xmin_fixed, + plfit_result_t *result) { + double* xs_copy; + + PLFIT_CHECK(plfit_i_copy_and_sort(xs, n, &xs_copy)); + PLFIT_CHECK(plfit_i_calculate_p_value_continuous(xs_copy, n, options, + xmin_fixed, result)); + free(xs_copy); + + return PLFIT_SUCCESS; +} + +int plfit_calculate_p_value_discrete(double* xs, size_t n, + const plfit_discrete_options_t* options, plfit_bool_t xmin_fixed, + plfit_result_t *result) { + double* xs_copy; + + PLFIT_CHECK(plfit_i_copy_and_sort(xs, n, &xs_copy)); + PLFIT_CHECK(plfit_i_calculate_p_value_discrete(xs_copy, n, options, + xmin_fixed, result)); + free(xs_copy); + + return PLFIT_SUCCESS; +} diff --git a/src/rigraph/vendor/plfit/plfit.h b/src/rigraph/vendor/plfit/plfit.h new file mode 100644 index 0000000..afc0348 --- /dev/null +++ b/src/rigraph/vendor/plfit/plfit.h @@ -0,0 +1,140 @@ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* 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 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 +#include "plfit_mt.h" +#include "plfit_version.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 + +typedef unsigned short int plfit_bool_t; + +typedef enum { + PLFIT_LINEAR_ONLY, + PLFIT_STRATIFIED_SAMPLING, + PLFIT_GSS_OR_LINEAR, + PLFIT_DEFAULT_CONTINUOUS_METHOD = PLFIT_STRATIFIED_SAMPLING +} 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 enum { + PLFIT_P_VALUE_SKIP, + PLFIT_P_VALUE_APPROXIMATE, + PLFIT_P_VALUE_EXACT, + PLFIT_DEFAULT_P_VALUE_METHOD = PLFIT_P_VALUE_EXACT +} plfit_p_value_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_p_value_method_t p_value_method; + double p_value_precision; + plfit_mt_rng_t* rng; +} 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_p_value_method_t p_value_method; + double p_value_precision; + plfit_mt_rng_t* rng; +} 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_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); + +/***** resampling routines to generate synthetic replicates ****/ + +int plfit_resample_continuous(double* xs, size_t n, double alpha, double xmin, + size_t num_samples, plfit_mt_rng_t* rng, double* result); +int plfit_resample_discrete(double* xs, size_t n, double alpha, double xmin, + size_t num_samples, plfit_mt_rng_t* rng, double* result); + +/******** calculating the p-value of a fitted model only *******/ + +int plfit_calculate_p_value_continuous(double* xs, size_t n, + const plfit_continuous_options_t* options, plfit_bool_t xmin_fixed, + plfit_result_t *result); +int plfit_calculate_p_value_discrete(double* xs, size_t n, + const plfit_discrete_options_t* options, plfit_bool_t xmin_fixed, + plfit_result_t *result); + +/************* calculating descriptive statistics **************/ + +int plfit_moments(double* data, size_t n, double* mean, double* variance, + double* skewness, double* kurtosis); + +__END_DECLS + +#endif /* __PLFIT_H__ */ diff --git a/src/rigraph/vendor/plfit/plfit_error.c b/src/rigraph/vendor/plfit/plfit_error.c new file mode 100644 index 0000000..d93d1c0 --- /dev/null +++ b/src/rigraph/vendor/plfit/plfit_error.c @@ -0,0 +1,66 @@ +/* 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 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 +#include +#include "plfit_error.h" +#include "platform.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_printignore; +#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_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/src/rigraph/vendor/plfit/plfit_error.h b/src/rigraph/vendor/plfit/plfit_error.h new file mode 100644 index 0000000..e542997 --- /dev/null +++ b/src/rigraph/vendor/plfit/plfit_error.h @@ -0,0 +1,86 @@ +/* plfit_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 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public 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/src/rigraph/vendor/plfit/plfit_mt.h b/src/rigraph/vendor/plfit/plfit_mt.h new file mode 100644 index 0000000..c7ed0dc --- /dev/null +++ b/src/rigraph/vendor/plfit/plfit_mt.h @@ -0,0 +1,101 @@ +/* plfit_mt.h + * + * Mersenne Twister random number generator, based on the implementation of + * Michael Brundage (which has been placed in the public domain). + * + * Author: Tamas Nepusz (original by Michael Brundage) + * + * See the following URL for the original implementation: + * http://www.qbrundage.com/michaelb/pubs/essays/random_number_generation.html + * + * This file has been placed in the public domain. + */ + +#ifndef __PLFIT_MT_H__ +#define __PLFIT_MT_H__ + +/* VS 2010, i.e. _MSC_VER == 1600, already has stdint.h */ +#if defined(_MSC_VER) && _MSC_VER < 1600 +# define uint32_t unsigned __int32 +#else +# include +#endif + +#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_MT_LEN 624 + +/** + * \def PLFIT_MT_RAND_MAX + * + * The maximum random number that \c plfit_mt_random() can generate. + */ +#define PLFIT_MT_RAND_MAX 0xFFFFFFFF + +/** + * Struct that stores the internal state of a Mersenne Twister random number + * generator. + */ +typedef struct { + int mt_index; + uint32_t mt_buffer[PLFIT_MT_LEN]; +} plfit_mt_rng_t; + +/** + * \brief Initializes a Mersenne Twister random number generator. + * + * The random number generator is seeded with random 32-bit numbers obtained + * from the \em built-in random number generator using consecutive calls to + * \c rand(). + * + * \param rng the random number generator to initialize + */ +void plfit_mt_init(plfit_mt_rng_t* rng); + +/** + * \brief Initializes a Mersenne Twister random number generator, seeding it + * from another one. + * + * The random number generator is seeded with random 32-bit numbers obtained + * from another, initialized Mersenne Twister random number generator. + * + * \param rng the random number generator to initialize + * \param seeder the random number generator that will seed the one being + * initialized. When null, the random number generator will + * be initialized from the built-in RNG as if \ref plfit_mt_init() + * was called. + */ +void plfit_mt_init_from_rng(plfit_mt_rng_t* rng, plfit_mt_rng_t* seeder); + +/** + * \brief Returns the next 32-bit random number from the given Mersenne Twister + * random number generator. + * + * \param rng the random number generator to use + * \return the next 32-bit random number from the generator + */ +uint32_t plfit_mt_random(plfit_mt_rng_t* rng); + +/** + * \brief Returns a uniformly distributed double from the interval [0;1) + * based on the next value of the given Mersenne Twister random number + * generator. + * + * \param rng the random number generator to use + * \return a uniformly distributed random number from the interval [0;1) + */ +double plfit_mt_uniform_01(plfit_mt_rng_t* rng); + +__END_DECLS + +#endif diff --git a/src/rigraph/vendor/plfit/plfit_sampling.h b/src/rigraph/vendor/plfit/plfit_sampling.h new file mode 100644 index 0000000..f63407c --- /dev/null +++ b/src/rigraph/vendor/plfit/plfit_sampling.h @@ -0,0 +1,177 @@ +/* plfit_sampling.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 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 __SAMPLING_H__ +#define __SAMPLING_H__ + +#include +#include "plfit_mt.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 + +/** + * Draws a sample from a binomial distribution with the given count and + * probability values. + * + * This function is borrowed from R; see the corresponding license in + * \c rbinom.c. The return value is always an integer. + * + * The function is \em not thread-safe. + * + * \param n the number of trials + * \param p the success probability of each trial + * \param rng the Mersenne Twister random number generator to use + * \return the value drawn from the given binomial distribution. + */ +double plfit_rbinom(double n, double p, plfit_mt_rng_t* rng); + +/** + * Draws a sample from a Pareto distribution with the given minimum value and + * power-law exponent. + * + * \param xmin the minimum value of the distribution. Must be positive. + * \param alpha the exponent. Must be positive + * \param rng the Mersenne Twister random number generator to use + * + * \return the sample or NaN if one of the parameters is invalid + */ +extern double plfit_rpareto(double xmin, double alpha, plfit_mt_rng_t* rng); + +/** + * Draws a given number of samples from a Pareto distribution with the given + * minimum value and power-law exponent. + * + * \param xmin the minimum value of the distribution. Must be positive. + * \param alpha the exponent. Must be positive + * \param n the number of samples to draw + * \param rng the Mersenne Twister random number generator to use + * \param result the array where the result should be written. It must + * have enough space to store n items + * + * \return \c PLFIT_EINVAL if one of the parameters is invalid, zero otherwise + */ +int plfit_rpareto_array(double xmin, double alpha, size_t n, plfit_mt_rng_t* rng, + double* result); + +/** + * Draws a sample from a zeta distribution with the given minimum value and + * power-law exponent. + * + * \param xmin the minimum value of the distribution. Must be positive. + * \param alpha the exponent. Must be positive + * \param rng the Mersenne Twister random number generator to use + * + * \return the sample or NaN if one of the parameters is invalid + */ +extern double plfit_rzeta(long int xmin, double alpha, plfit_mt_rng_t* rng); + +/** + * Draws a given number of samples from a zeta distribution with the given + * minimum value and power-law exponent. + * + * \param xmin the minimum value of the distribution. Must be positive. + * \param alpha the exponent. Must be positive + * \param n the number of samples to draw + * \param rng the Mersenne Twister random number generator to use + * \param result the array where the result should be written. It must + * have enough space to store n items + * + * \return \c PLFIT_EINVAL if one of the parameters is invalid, zero otherwise + */ +int plfit_rzeta_array(long int xmin, double alpha, size_t n, plfit_mt_rng_t* rng, + double* result); + +/** + * Draws a sample from a uniform distribution with the given lower and + * upper bounds. + * + * The lower bound is inclusive, the uppoer bound is not. + * + * \param lo the lower bound + * \param hi the upper bound + * \param rng the Mersenne Twister random number generator to use + * \return the value drawn from the given uniform distribution. + */ +extern double plfit_runif(double lo, double hi, plfit_mt_rng_t* rng); + +/** + * Draws a sample from a uniform distribution over the [0; 1) interval. + * + * The interval is closed from the left and open from the right. + * + * \param rng the Mersenne Twister random number generator to use + * \return the value drawn from the given uniform distribution. + */ +extern double plfit_runif_01(plfit_mt_rng_t* rng); + +/** + * Random sampler using Walker's alias method. + */ +typedef struct { + long int num_bins; /**< Number of bins */ + long int* indexes; /**< Index of the "other" element in each bin */ + double* probs; /**< Probability of drawing the "own" element from a bin */ +} plfit_walker_alias_sampler_t; + +/** + * \brief Initializes the sampler with item probabilities. + * + * \param sampler the sampler to initialize + * \param ps pointer to an array containing a value proportional to the + * sampling probability of each item in the set being sampled. + * \param n the number of items in the array + * \return error code + */ +int plfit_walker_alias_sampler_init(plfit_walker_alias_sampler_t* sampler, + double* ps, size_t n); + +/** + * \brief Destroys an initialized sampler and frees the allocated memory. + * + * \param sampler the sampler to destroy + */ +void plfit_walker_alias_sampler_destroy(plfit_walker_alias_sampler_t* sampler); + +/** + * \brief Draws a given number of samples from the sampler and writes them + * to a given array. + * + * \param sampler the sampler to use + * \param xs pointer to an array where the sampled items should be + * written + * \param n the number of samples to draw + * \param rng the Mersenne Twister random number generator to use + * \return error code + */ +int plfit_walker_alias_sampler_sample(const plfit_walker_alias_sampler_t* sampler, + long int* xs, size_t n, plfit_mt_rng_t* rng); + +__END_DECLS + +#endif diff --git a/src/rigraph/vendor/plfit/plfit_version.h b/src/rigraph/vendor/plfit/plfit_version.h new file mode 100644 index 0000000..1212dd1 --- /dev/null +++ b/src/rigraph/vendor/plfit/plfit_version.h @@ -0,0 +1,28 @@ +/* plfit_version.h + * + * Copyright (C) 2021 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 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public 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_VERSION_H +#define PLFIT_VERSION_H + +#define PLFIT_VERSION_MAJOR 0 +#define PLFIT_VERSION_MINOR 9 +#define PLFIT_VERSION_PATCH 3 +#define PLFIT_VERSION_STRING "0.9.3" + +#endif diff --git a/src/rigraph/vendor/plfit/rbinom.c b/src/rigraph/vendor/plfit/rbinom.c new file mode 100644 index 0000000..771f972 --- /dev/null +++ b/src/rigraph/vendor/plfit/rbinom.c @@ -0,0 +1,208 @@ +/* + * Mathlib : A C Library of Special Functions + * Copyright (C) 1998 Ross Ihaka + * Copyright (C) 2000-2002 The R Core Team + * Copyright (C) 2007 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 + * + * #include + * double rbinom(double nin, double pp) + * + * DESCRIPTION + * + * Random variates from the binomial distribution. + * + * REFERENCE + * + * Kachitvichyanukul, V. and Schmeiser, B. W. (1988). + * Binomial random variate generation. + * Communications of the ACM 31, 216-222. + * (Algorithm BTPEC). + */ + +/* + * Modifications for this file were performed by Tamas Nepusz to make it fit + * better with plfit. The license of the original file applies to the + * modifications as well. + */ + +#include +#include +#include +#include "plfit_sampling.h" +#include "platform.h" + +#define repeat for(;;) + +double plfit_rbinom(double nin, double pp, plfit_mt_rng_t* rng) +{ + /* FIXME: These should become THREAD_specific globals : */ + + static double c, fm, npq, p1, p2, p3, p4, qn; + static double xl, xll, xlr, xm, xr; + + static double psave = -1.0; + static int nsave = -1; + static 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 (!isfinite(nin)) return NAN; + r = floor(nin + 0.5); + if (r != nin) return NAN; + if (!isfinite(pp) || + /* n=0, p=0, p=1 are not errors */ + r < 0 || pp < 0. || pp > 1.) return NAN; + + if (r == 0 || pp == 0.) return 0; + if (pp == 1.) return r; + + n = (int) r; + + 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 = (int) 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 = plfit_runif_01(rng) * p4; + v = plfit_runif_01(rng); + /* triangular region */ + if (u <= p1) { + ix = (int)(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 = (int) x; + } else { + if (u > p3) { /* right tail */ + ix = (int)(xr - log(v) / xlr); + if (ix > n) + continue; + v = v * (u - p3) * xlr; + } else {/* left tail */ + ix = (int)(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 = plfit_runif_01(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; +} diff --git a/src/rigraph/vendor/plfit/sampling.c b/src/rigraph/vendor/plfit/sampling.c new file mode 100644 index 0000000..4becf13 --- /dev/null +++ b/src/rigraph/vendor/plfit/sampling.c @@ -0,0 +1,312 @@ +/* sampling.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 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 +#include + +#include "igraph_random.h" + +#include "plfit_error.h" +#include "plfit_sampling.h" +#include "platform.h" + +inline double plfit_runif(double lo, double hi, plfit_mt_rng_t* rng) { + if (rng == 0) { + return RNG_UNIF(lo, hi); + } + return lo + plfit_mt_uniform_01(rng) * (hi-lo); +} + +inline double plfit_runif_01(plfit_mt_rng_t* rng) { + if (rng == 0) { + return RNG_UNIF01(); + } + return plfit_mt_uniform_01(rng); +} + +inline double plfit_rpareto(double xmin, double alpha, plfit_mt_rng_t* rng) { + if (alpha <= 0 || xmin <= 0) + return NAN; + + /* 1-u is used in the base here because we want to avoid the case of + * sampling zero */ + return pow(1-plfit_runif_01(rng), -1.0 / alpha) * xmin; +} + +int plfit_rpareto_array(double xmin, double alpha, size_t n, plfit_mt_rng_t* rng, + double* result) { + double gamma; + + if (alpha <= 0 || xmin <= 0) + return PLFIT_EINVAL; + + if (result == 0 || n == 0) + return PLFIT_SUCCESS; + + gamma = -1.0 / alpha; + while (n > 0) { + /* 1-u is used in the base here because we want to avoid the case of + * sampling zero */ + *result = pow(1-plfit_runif_01(rng), gamma) * xmin; + result++; n--; + } + + return PLFIT_SUCCESS; +} + +inline double plfit_rzeta(long int xmin, double alpha, plfit_mt_rng_t* rng) { + double u, v, t; + long int x; + double alpha_minus_1 = alpha-1; + double minus_1_over_alpha_minus_1 = -1.0 / (alpha-1); + double b; + double one_over_b_minus_1; + + if (alpha <= 0 || xmin < 1) + return NAN; + + xmin = (long int) round(xmin); + + /* Rejection sampling for the win. We use Y=floor(U^{-1/alpha} * xmin) as the + * envelope distribution, similarly to Chapter X.6 of Luc Devroye's book + * (where xmin is assumed to be 1): http://luc.devroye.org/chapter_ten.pdf + * + * Some notes that should help me recover what I was doing: + * + * p_i = 1/zeta(alpha, xmin) * i^-alpha + * q_i = (xmin/i)^{alpha-1} - (xmin/(i+1))^{alpha-1} + * = (i/xmin)^{1-alpha} - ((i+1)/xmin)^{1-alpha} + * = [i^{1-alpha} - (i+1)^{1-alpha}] / xmin^{1-alpha} + * + * p_i / q_i attains its maximum at xmin=i, so the rejection constant is: + * + * c = p_xmin / q_xmin + * + * We have to accept the sample if V <= (p_i / q_i) * (q_xmin / p_xmin) = + * (i/xmin)^-alpha * [xmin^{1-alpha} - (xmin+1)^{1-alpha}] / [i^{1-alpha} - (i+1)^{1-alpha}] = + * [xmin - xmin^alpha / (xmin+1)^{alpha-1}] / [i - i^alpha / (i+1)^{alpha-1}] = + * xmin/i * [1-(xmin/(xmin+1))^{alpha-1}]/[1-(i/(i+1))^{alpha-1}] + * + * In other words (and substituting i with X, which is the same), + * + * V * (X/xmin) <= [1 - (1+1/xmin)^{1-alpha}] / [1 - (1+1/i)^{1-alpha}] + * + * Let b := (1+1/xmin)^{alpha-1} and let T := (1+1/i)^{alpha-1}. Then: + * + * V * (X/xmin) <= [(b-1)/b] / [(T-1)/T] + * V * (X/xmin) * (T-1) / (b-1) <= T / b + * + * which is the same as in Devroye's book, except for the X/xmin term, and + * the definition of b. + */ + b = pow(1 + 1.0/xmin, alpha_minus_1); + one_over_b_minus_1 = 1.0/(b-1); + do { + do { + u = plfit_runif_01(rng); + v = plfit_runif_01(rng); + /* 1-u is used in the base here because we want to avoid the case of + * having zero in x */ + x = (long int) floor(pow(1-u, minus_1_over_alpha_minus_1) * xmin); + } while (x < xmin); + t = pow((x+1.0)/x, alpha_minus_1); + } while (v*x*(t-1)*one_over_b_minus_1*b > t*xmin); + + return x; +} + +int plfit_rzeta_array(long int xmin, double alpha, size_t n, plfit_mt_rng_t* rng, + double* result) { + double u, v, t; + long int x; + double alpha_minus_1 = alpha-1; + double minus_1_over_alpha_minus_1 = -1.0 / (alpha-1); + double b, one_over_b_minus_1; + + if (alpha <= 0 || xmin < 1) + return PLFIT_EINVAL; + + if (result == 0 || n == 0) + return PLFIT_SUCCESS; + + /* See the comments in plfit_rzeta for an explanation of the algorithm + * below. */ + xmin = (long int) round(xmin); + b = pow(1 + 1.0/xmin, alpha_minus_1); + one_over_b_minus_1 = 1.0/(b-1); + + while (n > 0) { + do { + do { + u = plfit_runif_01(rng); + v = plfit_runif_01(rng); + /* 1-u is used in the base here because we want to avoid the case of + * having zero in x */ + x = (long int) floor(pow(1-u, minus_1_over_alpha_minus_1) * xmin); + } while (x < xmin); /* handles overflow as well */ + t = pow((x+1.0)/x, alpha_minus_1); + } while (v*x*(t-1)*one_over_b_minus_1*b > t*xmin); + *result = x; + if (x < 0) return PLFIT_EINVAL; + result++; n--; + } + + return PLFIT_SUCCESS; +} + +int plfit_walker_alias_sampler_init(plfit_walker_alias_sampler_t* sampler, + double* ps, size_t n) { + double *p, *p2, *ps_end; + double sum; + long int *short_sticks, *long_sticks; + long int num_short_sticks, num_long_sticks; + long int i; + + if (n > LONG_MAX) { + return PLFIT_EINVAL; + } + + sampler->num_bins = (long int) n; + + ps_end = ps + n; + + /* Initialize indexes and probs */ + sampler->indexes = (long int*)calloc(n, sizeof(long int)); + if (sampler->indexes == 0) { + return PLFIT_ENOMEM; + } + sampler->probs = (double*)calloc(n, sizeof(double)); + if (sampler->probs == 0) { + free(sampler->indexes); + return PLFIT_ENOMEM; + } + + /* Normalize the probability vector; count how many short and long sticks + * are there initially */ + for (sum = 0.0, p = ps; p != ps_end; p++) { + sum += *p; + } + sum = n / sum; + + num_short_sticks = num_long_sticks = 0; + for (p = ps, p2 = sampler->probs; p != ps_end; p++, p2++) { + *p2 = *p * sum; + if (*p2 < 1) { + num_short_sticks++; + } else if (*p2 > 1) { + num_long_sticks++; + } + } + + /* Allocate space for short & long stick indexes */ + long_sticks = (long int*)calloc(num_long_sticks, sizeof(long int)); + if (long_sticks == 0) { + free(sampler->probs); + free(sampler->indexes); + return PLFIT_ENOMEM; + } + short_sticks = (long int*)calloc(num_long_sticks, sizeof(long int)); + if (short_sticks == 0) { + free(sampler->probs); + free(sampler->indexes); + free(long_sticks); + return PLFIT_ENOMEM; + } + + /* Initialize short_sticks and long_sticks */ + num_short_sticks = num_long_sticks = 0; + for (i = 0, p = sampler->probs; i < n; i++, p++) { + if (*p < 1) { + short_sticks[num_short_sticks++] = i; + } else if (*p > 1) { + long_sticks[num_long_sticks++] = i; + } + } + + /* Prepare the index table */ + while (num_short_sticks && num_long_sticks) { + long int short_index, long_index; + short_index = short_sticks[--num_short_sticks]; + long_index = long_sticks[num_long_sticks-1]; + sampler->indexes[short_index] = long_index; + sampler->probs[long_index] = /* numerical stability */ + (sampler->probs[long_index] + sampler->probs[short_index]) - 1; + if (sampler->probs[long_index] < 1) { + short_sticks[num_short_sticks++] = long_index; + num_long_sticks--; + } + } + + /* Fix numerical stability issues */ + while (num_long_sticks) { + i = long_sticks[--num_long_sticks]; + sampler->probs[i] = 1; + } + while (num_short_sticks) { + i = short_sticks[--num_short_sticks]; + sampler->probs[i] = 1; + } + + free(short_sticks); + free(long_sticks); + + return PLFIT_SUCCESS; +} + + +void plfit_walker_alias_sampler_destroy(plfit_walker_alias_sampler_t* sampler) { + if (sampler->indexes) { + free(sampler->indexes); + sampler->indexes = 0; + } + if (sampler->probs) { + free(sampler->probs); + sampler->probs = 0; + } +} + + +int plfit_walker_alias_sampler_sample(const plfit_walker_alias_sampler_t* sampler, + long int *xs, size_t n, plfit_mt_rng_t* rng) { + double u; + long int j; + long int *x; + + x = xs; + + if (rng == 0) { + /* Using built-in RNG */ + while (n > 0) { + u = RNG_UNIF01(); + j = RNG_INTEGER(0, sampler->num_bins - 1); + *x = (u < sampler->probs[j]) ? j : sampler->indexes[j]; + n--; x++; + } + } else { + /* Using Mersenne Twister */ + while (n > 0) { + u = plfit_mt_uniform_01(rng); + j = plfit_mt_random(rng) % sampler->num_bins; + *x = (u < sampler->probs[j]) ? j : sampler->indexes[j]; + n--; x++; + } + } + + return PLFIT_SUCCESS; +} diff --git a/src/rigraph/vendor/simpleraytracer/Color.cpp b/src/rigraph/vendor/simpleraytracer/Color.cpp new file mode 100644 index 0000000..3b31cf0 --- /dev/null +++ b/src/rigraph/vendor/simpleraytracer/Color.cpp @@ -0,0 +1,96 @@ +#include "Color.h" +#include "unit_limiter.h" + +namespace igraph { + +Color::Color() +{ +} + +Color::Color(double vRed, double vGreen, double vBlue, + double vTransparent) +{ + Red(vRed); + Green(vGreen); + Blue(vBlue); + Transparent(vTransparent); +} + +Color::~Color() +{ +} + +// returns multiplication of a scalar with this vector +Color Color::operator* (double vRhs) const +{ + return Color(mRed*vRhs, mGreen*vRhs, mBlue*vRhs, mTransparent); +} + +// returns the addition of this color with another color +Color Color::operator+ (const Color& vRhs) const +{ + double trans=Transparent() > vRhs.Transparent() ? Transparent() : + vRhs.Transparent(); + return Color(Red()+vRhs.Red(),Green()+vRhs.Green(),Blue()+vRhs.Blue(), + trans); +} + +void Color::Red(double vRed) +{ + mRed = unit_limiter(vRed); +} +double Color::Red() const +{ + return mRed; +} +void Color::Green(double vGreen) +{ + mGreen = unit_limiter(vGreen); + +} +double Color::Green() const +{ + return mGreen; +} +void Color::Blue(double vBlue) +{ + mBlue = unit_limiter(vBlue); +} +double Color::Blue() const +{ + return mBlue; +} + +void Color::Transparent(double vTransparent) +{ + mTransparent = unit_limiter(vTransparent); +} + +double Color::Transparent() const +{ + return mTransparent; +} + +unsigned char Color::RedByte() const +{ + return ByteValue(mRed); +} +unsigned char Color::GreenByte() const +{ + return ByteValue(mGreen); +} +unsigned char Color::BlueByte() const +{ + return ByteValue(mBlue); +} +unsigned char Color::TransparentByte() const +{ + return ByteValue(mTransparent); +} + +unsigned char Color::ByteValue(double vZeroToOne) const +{ + return (unsigned char)(vZeroToOne*255.0); +} + +} // namespace igraph diff --git a/src/rigraph/vendor/simpleraytracer/Color.h b/src/rigraph/vendor/simpleraytracer/Color.h new file mode 100644 index 0000000..8a10430 --- /dev/null +++ b/src/rigraph/vendor/simpleraytracer/Color.h @@ -0,0 +1,40 @@ +/** Color.h + */ + +#ifndef COLOR_H +#define COLOR_H + +namespace igraph { + +class Color +{ +public: + Color(); + Color(double vRed, double vGreen, double vBlue, + double vTransparent=1.0); + ~Color(); + + Color operator* (double vRhs) const; // returns multiplication of a scalar with a vector + Color operator+ (const Color& vRhs) const; // returns the addition of this color with another color + + void Red(double vRed); + double Red() const; + void Green(double vGreen); + double Green() const; + void Blue(double vBlue); + double Blue() const; + void Transparent(double vTransparent); + double Transparent() const; + + unsigned char RedByte() const; + unsigned char GreenByte() const; + unsigned char BlueByte() const; + unsigned char TransparentByte() const; +private: + unsigned char ByteValue(double vZeroToOne) const; + double mRed, mGreen, mBlue, mTransparent; +}; + +} // namespace igraph + +#endif diff --git a/src/rigraph/vendor/simpleraytracer/Light.cpp b/src/rigraph/vendor/simpleraytracer/Light.cpp new file mode 100644 index 0000000..d74cf0c --- /dev/null +++ b/src/rigraph/vendor/simpleraytracer/Light.cpp @@ -0,0 +1,46 @@ +#include "Light.h" +#include "unit_limiter.h" + +namespace igraph { + +Light::Light() : mLightPoint(0,0,0) +{ + mIntensity = 0.1; +} + +Light::Light(const Point& rLightPoint) : mLightPoint(rLightPoint) +{ + mIntensity = 0.1; +} + +Light::~Light() +{} + +const Point& Light::LightPoint() const +{ + return mLightPoint; +} +void Light::LightPoint(const Point& rLightPoint) +{ + mLightPoint = rLightPoint; +} +double Light::Intensity() const +{ + return mIntensity; +} +void Light::Intensity(double vIntensity) +{ + mIntensity = unit_limiter(vIntensity); +} + +const Color& Light::LightColor() const +{ + return mLightColor; +} + +void Light::LightColor(const Color& rLightColor) +{ + mLightColor = rLightColor; +} + +} // namespace igraph diff --git a/src/rigraph/vendor/simpleraytracer/Light.h b/src/rigraph/vendor/simpleraytracer/Light.h new file mode 100644 index 0000000..369b926 --- /dev/null +++ b/src/rigraph/vendor/simpleraytracer/Light.h @@ -0,0 +1,39 @@ +#ifndef LIGHT_H +#define LIGHT_H + +#include "Point.h" +#include "Color.h" + +#include +using namespace std; + +namespace igraph { + +class Light +{ +public: + Light(); // creates a light at the origin + Light(const Point& rLightPoint); + ~Light(); + + const Point& LightPoint() const; + void LightPoint(const Point& rLightPoint); + + double Intensity() const; + void Intensity(double vIntensity); + + const Color& LightColor() const; + void LightColor(const Color& rLightColor); + +private: + Point mLightPoint; + double mIntensity; // 0 to 1 + Color mLightColor; +}; + +typedef list LightList; +typedef list::iterator LightListIterator; + +} // namespace igraph + +#endif diff --git a/src/rigraph/vendor/simpleraytracer/Point.cpp b/src/rigraph/vendor/simpleraytracer/Point.cpp new file mode 100644 index 0000000..8ccbc27 --- /dev/null +++ b/src/rigraph/vendor/simpleraytracer/Point.cpp @@ -0,0 +1,106 @@ +#include "Point.h" +#include + +namespace igraph { + +Point::Point() +{ + X(0.0); + Y(0.0); + Z(0.0); + Name(0); +} + +Point::Point(double vX, double vY, double vZ, int vName) +{ + X(vX); + Y(vY); + Z(vZ); + Name(vName); +} + +Point::Point(double vX, double vY, double vZ) +{ + X(vX); + Y(vY); + Z(vZ); + Name(0); +} + +Point::~Point() +{} + +double Point::X() const +{ + return mX; +} + +void Point::X(double vX) +{ + mX = vX; +} + +double Point::Y() const +{ + return mY; +} + +void Point::Y(double vY) +{ + mY = vY; +} + +double Point::Z() const +{ + return mZ; +} + +void Point::Z(double vZ) +{ + mZ = vZ; +} + +int Point::Name() const +{ + return mName; +} + +void Point::Name(int vName) +{ + mName = vName; +} + +double Point::Distance(const Point& rPoint) const +{ + return sqrt( (rPoint.X() - mX)*(rPoint.X() - mX) + (rPoint.Y() - mY)*(rPoint.Y() - mY) + (rPoint.Z() - mZ)*(rPoint.Z() - mZ) ); +} + +bool Point::operator==(const Point& vRhs) const +{ + bool result = true; +/* + if ( mX + .001 <= vRhs.X() ) + result = false; + if ( mX - .001 >= vRhs.X() ) + result = false; + if ( mY + .001 <= vRhs.Y() ) + result = false; + if ( mY - .001 >= vRhs.Y() ) + result = false; + if ( mZ + .001 <= vRhs.Z() ) + result = false; + if ( mZ - .001 >= vRhs.Z() ) + result = false; +*/ + if ( mX != vRhs.X() ) + result = false; + if ( mY != vRhs.Y() ) + result = false; + if ( mZ != vRhs.Z() ) + result = false; + + + return result; +} + +} // namespace igraph diff --git a/src/rigraph/vendor/simpleraytracer/Point.h b/src/rigraph/vendor/simpleraytracer/Point.h new file mode 100644 index 0000000..647bc47 --- /dev/null +++ b/src/rigraph/vendor/simpleraytracer/Point.h @@ -0,0 +1,45 @@ +/** + this is a simple generic class representing a 3d point with a name. + it also defines the PointList type, which is a linked list of Points +*/ + +#ifndef POINT_H +#define POINT_H + +#include +using namespace std; + +namespace igraph { + +class Point +{ +public: + Point(); // creates a point at the origin with name 0 + Point(double vX, double vY, double vZ, int vName); + Point(double vX, double vY, double vZ); + ~Point(); + + double X() const; + void X(double vX); + double Y() const; + void Y(double vY); + double Z() const; + void Z(double vZ); + + int Name() const; + void Name(int vName); + double Distance(const Point& rPoint) const; + + bool operator==(const Point& vRhs) const; + +private: + double mX, mY, mZ; + int mName; +}; + +typedef list PointList; +typedef list::iterator PointListIterator; + +} // namespace igraph + +#endif diff --git a/src/rigraph/vendor/simpleraytracer/RIgraphRay.cpp b/src/rigraph/vendor/simpleraytracer/RIgraphRay.cpp new file mode 100644 index 0000000..8bc9ddb --- /dev/null +++ b/src/rigraph/vendor/simpleraytracer/RIgraphRay.cpp @@ -0,0 +1,93 @@ +/* -*- mode: C -*- */ +/* + IGraph library R interface. + Copyright (C) 2005-2012 Gabor Csardi + 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.h" +#include "igraph_error.h" + +#include "RayTracer.h" +#include "Sphere.h" + +#include "config.h" + +#include +#include +#include + +using namespace igraph; + +extern "C" { + +SEXP R_igraph_getsphere(SEXP pos, SEXP radius, SEXP color, SEXP bgcolor, + SEXP lightpos, SEXP lightcolor, SEXP width, + SEXP height) { + + /* All error checking is done at the R level */ + + int i; + double *spos=REAL(pos); + double *scolor=REAL(color); + int no_lights=GET_LENGTH(lightpos); + RayTracer* p_ray_tracer; + Sphere * sphere; + int swidth=INTEGER(width)[0]; + int sheight=INTEGER(height)[0]; + int nopixels=swidth * sheight; + SEXP result, dim; + Image image; + + p_ray_tracer = new RayTracer(); + p_ray_tracer->EyePoint(Point(0,0,0)); + + for (i=0; iIntensity(1); + light->LightColor(Color(lcol[0], lcol[1], lcol[2])); + p_ray_tracer->AddLight(light); + } + + sphere = new Sphere(Point(spos[0], spos[1], spos[2]), REAL(radius)[0]); + sphere->ShapeColor(Color(scolor[0], scolor[1], scolor[2])); + p_ray_tracer->AddShape(sphere); + + PROTECT(result=NEW_NUMERIC(nopixels * 4)); + PROTECT(dim=NEW_INTEGER(3)); + INTEGER(dim)[0]=swidth; INTEGER(dim)[1]=sheight; INTEGER(dim)[2]=4; + SET_DIM(result, dim); + + image.width=swidth; + image.height=sheight; + image.red=REAL(result); + image.green=image.red + nopixels; + image.blue=image.green + nopixels; + image.trans=image.blue + nopixels; + + p_ray_tracer->RayTrace(image); + delete p_ray_tracer; + + UNPROTECT(2); + return result; +} + +} // extern C diff --git a/src/rigraph/vendor/simpleraytracer/Ray.cpp b/src/rigraph/vendor/simpleraytracer/Ray.cpp new file mode 100644 index 0000000..7f6744a --- /dev/null +++ b/src/rigraph/vendor/simpleraytracer/Ray.cpp @@ -0,0 +1,44 @@ +#include "Ray.h" + +namespace igraph { + +Ray::Ray() +{} + +Ray::~Ray() +{} + +Ray::Ray(const Point& rOrigin, const Vector& rDirection) +{ + Direction(rDirection); + Origin(rOrigin); + +} + +Ray::Ray(const Point& rOrigin, const Point& rEndPoint) +{ + Direction(Vector(rOrigin,rEndPoint)); + Origin(rOrigin); +} + +const Point& Ray::Origin() const +{ + return mOrigin; +} + +void Ray::Origin(Point vOrigin) +{ + mOrigin = vOrigin; +} + +const Vector& Ray::Direction() const +{ + return mDirection; +} + +void Ray::Direction(Vector vDirection) +{ + mDirection = vDirection; +} + +} // namespace igraph diff --git a/src/rigraph/vendor/simpleraytracer/Ray.h b/src/rigraph/vendor/simpleraytracer/Ray.h new file mode 100644 index 0000000..cd14ca1 --- /dev/null +++ b/src/rigraph/vendor/simpleraytracer/Ray.h @@ -0,0 +1,33 @@ +/** Ray.h + */ + +#ifndef RAY_H +#define RAY_H + +#include "RayVector.h" +#include "Point.h" + +namespace igraph { + +class Ray +{ +public: + Ray(); + Ray(const Point& rOrigin, const Vector& rDirection); + Ray(const Point& rOrigin, const Point& rEndPoint); + ~Ray(); + + void Origin(Point vPoint); + const Point& Origin() const; + + const Vector& Direction() const; + void Direction(Vector vDirection); + +private: + Vector mDirection; + Point mOrigin; +}; + +} // namespace igraph + +#endif diff --git a/src/rigraph/vendor/simpleraytracer/RayTracer.cpp b/src/rigraph/vendor/simpleraytracer/RayTracer.cpp new file mode 100644 index 0000000..27e3cc5 --- /dev/null +++ b/src/rigraph/vendor/simpleraytracer/RayTracer.cpp @@ -0,0 +1,266 @@ +#include "RayTracer.h" +#include "unit_limiter.h" +#include +#include + +namespace igraph { + +RayTracer::RayTracer() : mBackgroundColor(0,0,0,0), mAmbientColor(0,0,0), mEyePoint(0,0,0), mSpecularColor(1,1,1) +{ + // begin settings + mAmbientIntensity = .7; + mRecursionLimit = 700; + mAntiAliasDetail = 1; + // end settings + + mRecursions = 0; + + mpShapes = new ShapeList; + mpLights = new LightList; +} + +RayTracer::~RayTracer() +{ + ShapeListIterator iter1 = mpShapes->begin(); + while ( iter1 != mpShapes->end() ) + { + delete *iter1; + iter1++; + } + delete mpShapes; + + LightListIterator iter2 = mpLights->begin(); + while ( iter2 != mpLights->end() ) + { + delete *iter2; + iter2++; + } + + delete mpLights; + +} + +void RayTracer::RayTrace(Image &result) +{ + int mWidth=result.width; + int mHeight=result.height; + Ray eye_ray(mEyePoint,Vector(0,0,1)); + Color draw_color; + double i_inc, j_inc, anti_alias_i_inc, anti_alias_j_inc; // amount to increment the ray in each direction + double i, j, anti_alias_i, anti_alias_j; // the i and j values of the ray + int pixel_x, pixel_y, anti_alias_pixel_x, anti_alias_pixel_y; // the pixels being drawn + + double average_red_byte, average_green_byte, average_blue_byte, average_trans_byte; + int anti_alias_count; // the number of anti aliases (used in averaging) + int idx=0; + + i_inc = 2.0/(double)mWidth; + j_inc = 2.0/(double)mHeight; + anti_alias_i_inc = 1.0/(double)mAntiAliasDetail; + anti_alias_j_inc = 1.0/(double)mAntiAliasDetail; + + pixel_y = 0; + j = 1.0; + for (; pixel_y < mHeight; j -= j_inc, pixel_y++) + { + pixel_x = 0; + i = -1.0; + for (; pixel_x < mWidth; i += i_inc, pixel_x++) + { + anti_alias_pixel_y = 0; + anti_alias_j = 0.0; + average_red_byte = 0; + average_green_byte = 0; + average_blue_byte = 0; + average_trans_byte = 0; + anti_alias_count = 0; + for (; anti_alias_pixel_y < mAntiAliasDetail; anti_alias_j += anti_alias_j_inc, anti_alias_pixel_y++) + { + anti_alias_pixel_x = 0; + anti_alias_i = 0.0; + + for (; anti_alias_pixel_x < mAntiAliasDetail; anti_alias_i += anti_alias_i_inc, anti_alias_pixel_x++) + { + anti_alias_count++; + eye_ray.Direction( Vector(i+(anti_alias_i*i_inc),j+(anti_alias_j*j_inc),1.0) ); + draw_color = Render(eye_ray); + + average_red_byte = average_red_byte + ((double)draw_color.RedByte() - average_red_byte)/(double)anti_alias_count; + average_green_byte = average_green_byte + ((double)draw_color.GreenByte() - average_green_byte)/(double)anti_alias_count; + average_blue_byte = average_blue_byte + ((double)draw_color.BlueByte() - average_blue_byte)/(double)anti_alias_count; + average_trans_byte = average_trans_byte + ((double)draw_color.TransparentByte() - average_trans_byte)/(double)anti_alias_count; + } + } + + result.red [idx] = average_red_byte/255; + result.green[idx] = average_green_byte/255; + result.blue [idx] = average_blue_byte/255; + result.trans[idx] = average_trans_byte/255; + idx++; + } + } +} + +Color RayTracer::Render(const Ray& rRay, bool vIsReflecting, const Shape* pReflectingFrom ) +{ + mRecursions++; + Shape* closest_shape; + Point intersect_point; + Color result; + if (vIsReflecting) + closest_shape = QueryScene(rRay, intersect_point, vIsReflecting, pReflectingFrom); + else + closest_shape = QueryScene(rRay, intersect_point); + + if (closest_shape == NULL && !vIsReflecting) + { + mRecursions = 0; + return mBackgroundColor; + } + if (closest_shape == NULL && vIsReflecting) + { + mRecursions = 0; + return mAmbientColor*mAmbientIntensity; + } + if ( mRecursions > mRecursionLimit ) + { + mRecursions = 0; + return Color(0,0,0); // mAmbientColor*mAmbientIntensity; + } + result = closest_shape->ShapeColor()*Shade(closest_shape, intersect_point); + + Ray backwards_ray(intersect_point,rRay.Direction()*-1); + if ( closest_shape->DiffuseReflectivity() > 0.0 ) + result = result + (Render( closest_shape->Reflect(intersect_point,backwards_ray), true, closest_shape )*closest_shape->DiffuseReflectivity()); + + return (result + mSpecularColor); +} + +double RayTracer::Shade(const Shape* pShapeToShade, const Point& rPointOnShapeToShade) +{ + double intensity = mAmbientIntensity * pShapeToShade->AmbientReflectivity(); // the ambient intensity of the scene + Ray light_ray; // the ray that goes from the intersection point to the light sources + double dot_product; + Shape* closest_shape; // the shape closest from the intersection point to the light source + Point light_intersect; // the intersection point of the ray that goes from the intersection point to the light source + light_ray.Origin(rPointOnShapeToShade); // lightRay. org= object. intersect; + Ray light_ray_from_light; + + LightListIterator iter = mpLights->begin(); + mSpecularColor.Red(0); + mSpecularColor.Green(0); + mSpecularColor.Blue(0); + + while ( iter != mpLights->end() ) // foreach light in LightList do + { + + light_ray.Direction(Vector(rPointOnShapeToShade,(*iter)->LightPoint())); // lightRay. dir= light. dir + light_ray_from_light.Origin((*iter)->LightPoint()); + light_ray_from_light.Direction(Vector((*iter)->LightPoint(),rPointOnShapeToShade)); + + closest_shape = QueryScene(light_ray_from_light, light_intersect); + if ( closest_shape == NULL || (closest_shape == pShapeToShade && light_ray.Direction().Dot(pShapeToShade->Normal(rPointOnShapeToShade, light_ray_from_light.Origin() )) >= 0.0 ) ) //if (QueryScene( lightRay)= NIL) + { + Vector normal_vector = pShapeToShade->Normal(rPointOnShapeToShade, Point() ); + dot_product = normal_vector.Dot(light_ray.Direction().Normalize()); + dot_product *= (*iter)->Intensity(); + + if (dot_product < 0.0) + { + if (pShapeToShade->Type() == "Triangle") + dot_product = dot_product*-1.0; + else + dot_product = 0.0; + } + intensity = unit_limiter( intensity + dot_product ); + + if ( light_ray.Direction().Dot(pShapeToShade->Normal(rPointOnShapeToShade, light_ray_from_light.Origin() )) >= 0.0 ) + { + double specular = Specular(pShapeToShade, rPointOnShapeToShade, *iter); + mSpecularColor = mSpecularColor + Color(specular,specular,specular); + } + } + + iter++; + } + + return intensity; +} + +double RayTracer::Specular(const Shape* pShapeToShade, const Point& rPointOnShapeToShade, const Light* pLight) +{ + Ray reflected = pShapeToShade->Reflect(rPointOnShapeToShade,Ray(rPointOnShapeToShade, pLight->LightPoint())); + + Vector eye_vector(rPointOnShapeToShade, mEyePoint); + Vector reflected_vector = reflected.Direction().Normalize(); + eye_vector.NormalizeThis(); + double dot_product = eye_vector.Dot(reflected_vector); + + int n = pShapeToShade->SpecularSize(); + double specular_intensity = dot_product/(n - n*dot_product+ dot_product); + return unit_limiter(specular_intensity*pLight->Intensity()); +} + +Shape* RayTracer::QueryScene(const Ray& rRay, Point& rIntersectionPoint, bool vIsReflecting, const Shape* pReflectingFrom) +{ + Shape* closest_shape = NULL; + Point intersect_point; + double closest_distance; + double intersect_distance; + bool found_intersection = false; + + ShapeListIterator iter = mpShapes->begin(); + while ( iter != mpShapes->end() ) + { + if ( (*iter)->Intersect( rRay, intersect_point ) ) + { + intersect_distance = intersect_point.Distance(rRay.Origin()); + if ( !found_intersection && (*iter) != pReflectingFrom) + { + found_intersection = true; + rIntersectionPoint = intersect_point; + closest_shape = *iter; + closest_distance = intersect_distance; + } + else if ( intersect_distance < closest_distance && (*iter) != pReflectingFrom ) + { + rIntersectionPoint = intersect_point; + closest_shape = *iter; + closest_distance = intersect_distance; + } + } + iter++; + } + + return closest_shape; +} + +void RayTracer::AddShape(Shape* pShape) +{ + // should check if a shape with the same name already exists + mpShapes->push_back(pShape); +} +void RayTracer::AddLight(Light* pLight) +{ + // should check if a shape with the same name already exists + mpLights->push_back(pLight); +} + +void RayTracer::BackgroundColor(const Color& rBackgroundColor) +{ + mBackgroundColor = rBackgroundColor; +} +void RayTracer::EyePoint(const Point& rEyePoint) +{ + mEyePoint = rEyePoint; +} +void RayTracer::AmbientColor(const Color& rAmbientColor) +{ + mAmbientColor = rAmbientColor; +} +void RayTracer::AmbientIntensity(double vAmbientIntensity) +{ + mAmbientIntensity = unit_limiter(vAmbientIntensity); +} + +} // namespace igraph diff --git a/src/rigraph/vendor/simpleraytracer/RayTracer.h b/src/rigraph/vendor/simpleraytracer/RayTracer.h new file mode 100644 index 0000000..cc4830d --- /dev/null +++ b/src/rigraph/vendor/simpleraytracer/RayTracer.h @@ -0,0 +1,63 @@ +/** RayTraceCanvas.h + */ + +#ifndef RAY_TRACER_H +#define RAY_TRACER_H + + +#include +#include "Point.h" +#include "Shape.h" +#include "Color.h" +#include "Light.h" + +namespace igraph { + +class Image +{ + public: + int width, height; + double *red, *green, *blue, *trans; +}; + +class RayTracer +{ + +public: + RayTracer(); + ~RayTracer(); + + void RayTrace(Image &result); + + void AddShape(Shape* pShape); + void AddLight(Light* pLight); + + void BackgroundColor(const Color& rBackgroundColor); + void EyePoint(const Point& rEyePoint); + void AmbientColor(const Color& rAmbient); + void AmbientIntensity(double vAmbientIntensity); + +private: + + Color Render(const Ray& rRay, bool vIsReflecting = false, const Shape* pReflectingFrom = 0 ); // vEyeRay should be true if the ray we are tracing is a ray from the eye, otherwise it should be false + Shape* QueryScene(const Ray& rRay, Point& rIntersectionPoint, bool vIsReflecting = false, const Shape* pReflectingFrom = 0); + double Shade(const Shape* pShapeToShade, const Point& rPointOnShapeToShade); + double Specular(const Shape* pShapeToShade, const Point& rPointOnShapeToShade, const Light* pLight); + + Color mBackgroundColor; + Color mAmbientColor; + Point mEyePoint; + Color mSpecularColor; + double mAmbientIntensity; + + ShapeList* mpShapes; + LightList* mpLights; + + int mRecursions; + int mRecursionLimit; + int mAntiAliasDetail; +}; + +} // namespace igraph + +#endif diff --git a/src/rigraph/vendor/simpleraytracer/RayVector.cpp b/src/rigraph/vendor/simpleraytracer/RayVector.cpp new file mode 100644 index 0000000..76f21aa --- /dev/null +++ b/src/rigraph/vendor/simpleraytracer/RayVector.cpp @@ -0,0 +1,128 @@ +#include "RayVector.h" +#include + +namespace igraph { + +Vector::Vector() +{ + mI = mJ = mK = 0.0; +} + +Vector::Vector(const Point& vStartPoint, const Point& vEndPoint) +{ + mI = vEndPoint.X() - vStartPoint.X(); + mJ = vEndPoint.Y() - vStartPoint.Y(); + mK = vEndPoint.Z() - vStartPoint.Z(); +} + +Vector::Vector(double vI, double vJ, double vK) +{ + mI = vI; + mJ = vJ; + mK = vK; +} + +Vector::~Vector() +{} + +// returns a unit vector of this vector +Vector Vector::Normalize() const +{ + double magnitude = Magnitude(); + return Vector(mI/magnitude, mJ/magnitude, mK/magnitude); +} + +void Vector::NormalizeThis() +{ + *this = Normalize(); +} + +void Vector::ReverseDirection() +{ + *this = *this * -1.0; +} + +bool Vector::IsSameDirection(const Vector& rVector) const +{ + return ( this->Normalize().Dot(rVector.Normalize()) > 0.0 ); +} + + +void Vector::I(double vI) +{ + mI = vI; +} + +double Vector::I() const +{ + return mI; +} + +void Vector::J(double vJ) +{ + mJ = vJ; +} + +double Vector::J() const +{ + return mJ; +} +void Vector::K(double vK) +{ + mK = vK; +} + +double Vector::K() const +{ + return mK; +} + +// returns the dot product of this and rVector +double Vector::Dot(const Vector& rVector) const +{ + return mI*rVector.I() + mJ*rVector.J() + mK*rVector.K(); +} + +// returns the cross product of this and vVector +Vector Vector::Cross(const Vector& rVector) const +{ + return Vector(mJ*rVector.K() - rVector.J()*mK, -1.0*(mI*rVector.K() - rVector.I()*mK), mI*rVector.J() - rVector.I()*mJ); +} + +// returns the sum of this vector with another vector +Vector Vector::operator+ (Vector vRhs) const +{ + return Vector(mI + vRhs.I(), mJ + vRhs.J(), mK + vRhs.K()); +} + +// returns the sume of a vector and a Point +Point Vector::operator+ (Point vRhs) const +{ + return Point(mI + vRhs.X(), mJ + vRhs.Y(), mK + vRhs.Z()); +} + +// returns the difference of two vectors +Vector Vector::operator- (Vector vRhs) const +{ + return Vector(mI-vRhs.I(), mJ-vRhs.J(), mK-vRhs.K()); +} + +// returns multiplication of a scalar with this vector +Vector Vector::operator* (double vRhs) const +{ + return Vector(mI*vRhs, mJ*vRhs, mK*vRhs); +} + +// converts this vector to a point +Point Vector::ToPoint() const +{ + return Point(mI,mJ,mK); +} + +// returns the magnitude +double Vector::Magnitude() const +{ + return sqrt(mI*mI + mJ*mJ + mK*mK); +} + +} // namespace igraph diff --git a/src/rigraph/vendor/simpleraytracer/RayVector.h b/src/rigraph/vendor/simpleraytracer/RayVector.h new file mode 100644 index 0000000..7157ba9 --- /dev/null +++ b/src/rigraph/vendor/simpleraytracer/RayVector.h @@ -0,0 +1,49 @@ +/** Vector.h + */ + +#ifndef VECTOR_H +#define VECTOR_H + +#include "Point.h" + +namespace igraph { + +class Vector +{ +public: + Vector(); + Vector(const Point& vStartPoint, const Point& vEndPoint); + Vector(double vI, double vJ, double vK); + ~Vector(); + + Vector Normalize() const; // returns a unit vector of this vector + void NormalizeThis(); + void ReverseDirection(); + bool IsSameDirection(const Vector& rVector) const; + + void I(double vI); + double I() const; + void J(double vJ); + double J() const; + void K(double vK); + double K() const; + + double Dot(const Vector& rVector) const; // returns the dot product of this and rVector + Vector Cross(const Vector& rVector) const; // returns the cross product of this and rVector + Vector operator+ (Vector vRhs) const; // returns the sum of two vectors + Vector operator- (Vector vRhs) const; // returns the difference of two vectors + Point operator+ (Point vRhs) const; // returns the sum of a vector and a Point + Vector operator* (double vRhs) const; // returns multiplication of a scalar with a vector + Point ToPoint() const; // converts a vector to a point + + double Magnitude() const; + +private: + + + double mI, mJ, mK; +}; + +} // namespace igraph + +#endif diff --git a/src/rigraph/vendor/simpleraytracer/Shape.cpp b/src/rigraph/vendor/simpleraytracer/Shape.cpp new file mode 100644 index 0000000..3ca2b14 --- /dev/null +++ b/src/rigraph/vendor/simpleraytracer/Shape.cpp @@ -0,0 +1,106 @@ +#include "Shape.h" +#include "unit_limiter.h" + +namespace igraph { + +Shape::Shape() +{ + mName = 0; + mAmbientReflectivity = .6; + mSpecularReflectivity = 0; + mDiffuseReflectivity = 0; + mSpecularSize = 64; +} + +Shape::~Shape() +{} + +int Shape::Name() const +{ + return mName; +} + +void Shape::Name(int vName) +{ + mName = vName; +} + +const Color& Shape::ShapeColor() const +{ + return mShapeColor; +} + +void Shape::ShapeColor(const Color& rColor) +{ + mShapeColor = rColor; +} + +double Shape::AmbientReflectivity() const +{ + return mAmbientReflectivity; +} +double Shape::SpecularReflectivity() const +{ + return mSpecularReflectivity; +} +double Shape::DiffuseReflectivity() const +{ + return mDiffuseReflectivity; +} + +void Shape::AmbientReflectivity(double rReflectivity) +{ + mAmbientReflectivity = unit_limiter(rReflectivity); +} +void Shape::SpecularReflectivity(double rReflectivity) +{ + mSpecularReflectivity = unit_limiter(rReflectivity); +} +void Shape::DiffuseReflectivity(double rReflectivity) +{ + mDiffuseReflectivity = unit_limiter(rReflectivity); +} + +Ray Shape::Reflect(const Point& rReflectFrom, const Ray& rIncidentRay) const +{ + Ray result; // the reflected ray + Vector result_direction; // the reflected direction vector + Vector incident_unit = rIncidentRay.Direction().Normalize(); + Vector normal = this->Normal(rReflectFrom, rIncidentRay.Origin() ); + if ( !normal.IsSameDirection(incident_unit) ) + normal.ReverseDirection(); // we want the normal in the same direction of the incident ray. + + result.Origin(rReflectFrom); + result.Direction( normal*2.0*normal.Dot(incident_unit) - incident_unit ); +/* + if ( normal.Dot(rIncidentRay.Direction().Normalize()) < 0.0 ) + normal.ReverseDirection(); + + result.Origin(rReflectFrom); + result.Direction((normal*2.0) - rIncidentRay.Direction().Normalize()); +*/ + + return result; +} + +const string& Shape::Type() const +{ + return mType; +} + +void Shape::Type(const string& rType) +{ + mType = rType; +} + +int Shape::SpecularSize() const +{ + return mSpecularSize; +} + +void Shape::SpecularSize(int vSpecularSize) +{ + mSpecularSize = vSpecularSize; +} + +} // namespace igraph diff --git a/src/rigraph/vendor/simpleraytracer/Shape.h b/src/rigraph/vendor/simpleraytracer/Shape.h new file mode 100644 index 0000000..fc9c7c1 --- /dev/null +++ b/src/rigraph/vendor/simpleraytracer/Shape.h @@ -0,0 +1,65 @@ +/** Shape.h + */ + +#ifndef SHAPE_H +#define SHAPE_H + +#include +#include "Color.h" +#include "Ray.h" +#include "Point.h" + +#include +using namespace std; + +namespace igraph { + +class Shape +{ +public: + Shape(); + virtual ~Shape(); + + virtual bool Intersect(const Ray& rRay, Point& rIntersectPoint) const = 0; + virtual Vector Normal(const Point& rSurfacePoint, const Point& rOffSurface) const = 0; + // returns a normalized vector that is the normal of this shape from the surface point + // it also takes the rOffSurface point into account, for example: + // if rSurfacePoint is on top of a triangle, then the normal returned will be going up. + + Ray Reflect(const Point& rReflectFrom, const Ray& rRay) const; + + void Name(int vName); + int Name() const; + + const Color& ShapeColor() const; + void ShapeColor(const Color& rColor); + + double SpecularReflectivity() const; + void SpecularReflectivity(double rReflectivity); + double DiffuseReflectivity() const; + void DiffuseReflectivity(double rReflectivity); + double AmbientReflectivity() const; + void AmbientReflectivity(double rReflectivity); + + int SpecularSize() const; + void SpecularSize(int vSpecularSize); + + const string& Type() const; + void Type(const string& rType); + +private: + int mName; + string mType; + Color mShapeColor; + double mSpecularReflectivity; // from 0 to 1 + int mSpecularSize; // 1 to 64 + double mDiffuseReflectivity; // from 0 to 1 + double mAmbientReflectivity; // from 0 to 1 +}; + +typedef list ShapeList; +typedef list::iterator ShapeListIterator; + +} // namespace igraph + +#endif diff --git a/src/rigraph/vendor/simpleraytracer/Sphere.cpp b/src/rigraph/vendor/simpleraytracer/Sphere.cpp new file mode 100644 index 0000000..c449b80 --- /dev/null +++ b/src/rigraph/vendor/simpleraytracer/Sphere.cpp @@ -0,0 +1,70 @@ +#include "Sphere.h" +#include + +namespace igraph { + +Sphere::Sphere() +{} + +Sphere::Sphere(Point vCenter, double vRadius) +{ + Type("Sphere"); + mCenter = vCenter; + mRadius = vRadius; +} + +Sphere::~Sphere() +{ +} + +bool Sphere::Intersect(const Ray& vRay, Point& vIntersectPoint) const +{ + Vector V; + Vector EO(vRay.Origin(), mCenter); + double v; + double disc; + double d; + Vector E(Point(0,0,0), vRay.Origin()); // E = vector from origin to ray origin + Vector P; + mCenter.Distance(vRay.Origin()); //c = distance from eye to center of sphere + V = vRay.Direction(); + V.NormalizeThis(); + v = EO.Dot(V); + double v2 = V.Dot(EO.Normalize()); + if (v2 >= 0.0) + { + disc = mRadius*mRadius - (EO.Dot(EO) - v*v); + if (disc <= 0) + return false; + else + { + d = sqrt(disc); + P = E + V*(v-d); + vIntersectPoint = P.ToPoint(); + return true; + } + } + else + return false; +} + +Vector Sphere::Normal(const Point& rSurfacePoint, const Point& rOffSurface) const +{ + // currently does not take rOffSurface point into account, + // it should check if this point is inside the sphere, if it is + // return a normal facing the center. + + Vector radius_vector (mCenter, rSurfacePoint); + return (radius_vector.Normalize()); +} + +double Sphere::Radius() const +{ + return mRadius; +} +const Point& Sphere::Center() const +{ + return mCenter; +} + +} // namespace igraph diff --git a/src/rigraph/vendor/simpleraytracer/Sphere.h b/src/rigraph/vendor/simpleraytracer/Sphere.h new file mode 100644 index 0000000..faff6da --- /dev/null +++ b/src/rigraph/vendor/simpleraytracer/Sphere.h @@ -0,0 +1,31 @@ +/** Sphere.h +*/ + +#ifndef SPHERE_H +#define SPHERE_H + +#include "Shape.h" + +namespace igraph { + +class Sphere : public Shape +{ +public: + Sphere(); + Sphere(Point vCenter, double vRadius); + ~Sphere(); + + virtual bool Intersect(const Ray& vRay, Point& vIntersectPoint) const; + virtual Vector Normal(const Point& rSurfacePoint, const Point& rOffSurface) const; + + double Radius() const; + const Point& Center() const; + +private: + Point mCenter; + double mRadius; +}; + +} // namespace igraph + +#endif diff --git a/src/rigraph/vendor/simpleraytracer/Triangle.cpp b/src/rigraph/vendor/simpleraytracer/Triangle.cpp new file mode 100644 index 0000000..7946c26 --- /dev/null +++ b/src/rigraph/vendor/simpleraytracer/Triangle.cpp @@ -0,0 +1,90 @@ +#include "Triangle.h" +#include + +namespace igraph { + +Triangle::Triangle() +{} + +Triangle::Triangle(const Point& rPoint1, const Point& rPoint2, const Point& rPoint3) +{ + Type("Triangle"); + mPoint1 = rPoint1; + mPoint2 = rPoint2; + mPoint3 = rPoint3; +} + +Triangle::~Triangle() +{ +} + +bool Triangle::Intersect(const Ray& vRay, Point& rIntersectPoint) const +{ + + Vector pointb_minus_pointa (mPoint1, mPoint2); + Vector pointb_minus_pointc (mPoint1, mPoint3); +/* + Vector plane_normal = pointb_minus_pointa.Cross(pointb_minus_pointc); + + // get the plane normal facing the right way: + Vector plane_normal_normalized = plane_normal.Normalize(); + Vector triangle_to_ray_origin = Vector(mPoint1, vRay.Origin() ); + triangle_to_ray_origin.NormalizeThis(); + if ( plane_normal_normalized.Dot(triangle_to_ray_origin) < 0.0 ) + { + plane_normal = plane_normal * -1.0; + plane_normal_normalized = plane_normal_normalized * -1.0; + } + + // check that the ray is actually facing the triangle + Vector ray_direction_normalized = vRay.Direction().Normalize(); + if ( plane_normal_normalized.Dot(ray_direction_normalized) > 0.0 ) + return false; +*/ + Vector plane_normal = this->Normal(mPoint1, vRay.Origin()); + Vector ray_direction_normalized = vRay.Direction().Normalize(); + if ( plane_normal.IsSameDirection(ray_direction_normalized) ) + return false; + + Vector b_minus_u (vRay.Origin(), mPoint2); + + double t = plane_normal.Dot(b_minus_u) / plane_normal.Dot(vRay.Direction()); + Point p = (vRay.Direction() * t) + vRay.Origin(); + + Vector p_minus_a (mPoint1, p); + Vector p_minus_b (mPoint2, p); + Vector p_minus_c (mPoint3, p); + Vector pointc_minus_pointb (mPoint2, mPoint3); + Vector pointa_minus_pointc (mPoint3, mPoint1); + + double test1 = (pointb_minus_pointa.Cross(p_minus_a)).Dot(plane_normal); + double test2 = (pointc_minus_pointb.Cross(p_minus_b)).Dot(plane_normal); + double test3 = (pointa_minus_pointc.Cross(p_minus_c)).Dot(plane_normal); + + if ((test1 > 0 && test2 > 0 && test3 > 0) || (test1 < 0 && test2 < 0 && test3 < 0)) + { + rIntersectPoint = p; + return true; + } + else + return false; +} + +Vector Triangle::Normal(const Point& rSurfacePoint, const Point& rOffSurface) const +{ + Vector pointb_minus_pointa (mPoint1, mPoint2); + Vector pointb_minus_pointc (mPoint1, mPoint3); + Vector plane_normal = pointb_minus_pointa.Cross(pointb_minus_pointc).Normalize(); + + // get the plane normal facing the right way: + Vector triangle_to_off_surface_point = Vector(mPoint1, rOffSurface ); + triangle_to_off_surface_point.NormalizeThis(); + if ( !plane_normal.IsSameDirection(triangle_to_off_surface_point) ) + { + plane_normal.ReverseDirection(); + } + + return plane_normal; +} + +} // namespace igraph diff --git a/src/rigraph/vendor/simpleraytracer/Triangle.h b/src/rigraph/vendor/simpleraytracer/Triangle.h new file mode 100644 index 0000000..198d241 --- /dev/null +++ b/src/rigraph/vendor/simpleraytracer/Triangle.h @@ -0,0 +1,27 @@ +/** Triangle.h +*/ + +#ifndef TRIANGLE_H +#define TRIANGLE_H + +#include "Shape.h" + +namespace igraph { + +class Triangle : public Shape +{ +public: + Triangle(); + Triangle(const Point& rPoint1, const Point& rPoint2, const Point& rPoint3); + ~Triangle(); + + virtual bool Intersect(const Ray& vRay, Point& vIntersectPoint) const; + virtual Vector Normal(const Point& rSurfacePoint, const Point& rOffSurface) const; + +private: + Point mPoint1, mPoint2, mPoint3; +}; + +} // namespace igraph + +#endif diff --git a/src/rigraph/vendor/simpleraytracer/unit_limiter.cpp b/src/rigraph/vendor/simpleraytracer/unit_limiter.cpp new file mode 100644 index 0000000..75a59da --- /dev/null +++ b/src/rigraph/vendor/simpleraytracer/unit_limiter.cpp @@ -0,0 +1,15 @@ +#include "unit_limiter.h" + +namespace igraph { + +double unit_limiter(double vUnitDouble) +{ + double result = vUnitDouble; + if (result < 0.0) + result = 0.0; + else if (result > 1.0) + result = 1.0; + return result; +} + +} // namespace igraph diff --git a/src/rigraph/vendor/simpleraytracer/unit_limiter.h b/src/rigraph/vendor/simpleraytracer/unit_limiter.h new file mode 100644 index 0000000..9c6f61b --- /dev/null +++ b/src/rigraph/vendor/simpleraytracer/unit_limiter.h @@ -0,0 +1,10 @@ +#ifndef ZERO_TO_ONE_H +#define ZERO_TO_ONE_H + +namespace igraph { + +double unit_limiter(double vUnitDouble); + +} // namespace igraph + +#endif diff --git a/src/rigraph/vendor/uuid/COPYING b/src/rigraph/vendor/uuid/COPYING new file mode 100644 index 0000000..e7a8054 --- /dev/null +++ b/src/rigraph/vendor/uuid/COPYING @@ -0,0 +1,28 @@ +This library is free software; you can redistribute it and/or +modify it under the terms of the Modified BSD License: + +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, and the entire permission notice in its entirety, + including the disclaimer of warranties. +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. The name of the author may not be used to endorse or promote + products derived from this software without specific prior + written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF +WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR 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 NOT ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. diff --git a/src/rigraph/vendor/uuid/Makevars.in b/src/rigraph/vendor/uuid/Makevars.in new file mode 100644 index 0000000..0ab302b --- /dev/null +++ b/src/rigraph/vendor/uuid/Makevars.in @@ -0,0 +1,2 @@ +PKG_CPPFLAGS=@CPPFLAGS@ +PKG_LIBS=@LIBS@ diff --git a/src/rigraph/vendor/uuid/Makevars.win b/src/rigraph/vendor/uuid/Makevars.win new file mode 100644 index 0000000..248e1ce --- /dev/null +++ b/src/rigraph/vendor/uuid/Makevars.win @@ -0,0 +1 @@ +PKG_CPPFLAGS=-Iwin32 diff --git a/src/rigraph/vendor/uuid/R.c b/src/rigraph/vendor/uuid/R.c new file mode 100644 index 0000000..b956b8d --- /dev/null +++ b/src/rigraph/vendor/uuid/R.c @@ -0,0 +1,25 @@ +#include "uuid.h" + +#include +#include "igraph_random.h" + +SEXP UUID_gen(SEXP sTime) { + + RNG_BEGIN(); + + uuid_t u; + char c[40]; + int use_time = asInteger(sTime); + if (use_time == TRUE) + uuid_generate_time(u); + else if (use_time == FALSE) + uuid_generate_random(u); + else + uuid_generate(u); + uuid_unparse_lower(u, c); + + RNG_END(); + + return mkString(c); +} + diff --git a/src/rigraph/vendor/uuid/clear.c b/src/rigraph/vendor/uuid/clear.c new file mode 100644 index 0000000..ad1f066 --- /dev/null +++ b/src/rigraph/vendor/uuid/clear.c @@ -0,0 +1,43 @@ +/* + * clear.c -- Clear a UUID + * + * Copyright (C) 1996, 1997 Theodore Ts'o. + * + * %Begin-Header% + * 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, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 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. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF + * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR 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 NOT ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * %End-Header% + */ + +#include + +#include "uuidP.h" + +void uuid_clear(uuid_t uu) +{ + memset(uu, 0, 16); +} + diff --git a/src/rigraph/vendor/uuid/compare.c b/src/rigraph/vendor/uuid/compare.c new file mode 100644 index 0000000..8f3437a --- /dev/null +++ b/src/rigraph/vendor/uuid/compare.c @@ -0,0 +1,55 @@ +/* + * compare.c --- compare whether or not two UUIDs are the same + * + * Returns 0 if the two UUIDs are different, and 1 if they are the same. + * + * Copyright (C) 1996, 1997 Theodore Ts'o. + * + * %Begin-Header% + * 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, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 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. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF + * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR 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 NOT ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * %End-Header% + */ + +#include "uuidP.h" +#include + +#define UUCMP(u1,u2) if (u1 != u2) return((u1 < u2) ? -1 : 1); + +int uuid_compare(const uuid_t uu1, const uuid_t uu2) +{ + struct uuid uuid1, uuid2; + + uuid_unpack(uu1, &uuid1); + uuid_unpack(uu2, &uuid2); + + UUCMP(uuid1.time_low, uuid2.time_low); + UUCMP(uuid1.time_mid, uuid2.time_mid); + UUCMP(uuid1.time_hi_and_version, uuid2.time_hi_and_version); + UUCMP(uuid1.clock_seq, uuid2.clock_seq); + return memcmp(uuid1.node, uuid2.node, 6); +} + diff --git a/src/rigraph/vendor/uuid/config.h.in b/src/rigraph/vendor/uuid/config.h.in new file mode 100644 index 0000000..b6f72b3 --- /dev/null +++ b/src/rigraph/vendor/uuid/config.h.in @@ -0,0 +1,82 @@ +/* src/config.h.in. Generated from configure.ac by autoheader. */ + +/* Define to 1 if you have the header file. */ +#undef HAVE_INTTYPES_H + +/* Define to 1 if you have the `jrand48' function. */ +#undef HAVE_JRAND48 + +/* Define to 1 if you have the header file. */ +#undef HAVE_MEMORY_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_NETINET_IN_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_NET_IF_DL_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_NET_IF_H + +/* Define if struct sockaddr contains sa_len */ +#undef HAVE_SA_LEN + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDINT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDLIB_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRINGS_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRING_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_FILE_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_IOCTL_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_SOCKET_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_SOCKIO_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_STAT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_SYSCALL_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_TIME_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_TYPES_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_UN_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_UNISTD_H + +/* Define to the address where bug reports for this package should be sent. */ +#undef PACKAGE_BUGREPORT + +/* Define to the full name of this package. */ +#undef PACKAGE_NAME + +/* Define to the full name and version of this package. */ +#undef PACKAGE_STRING + +/* Define to the one symbol short name of this package. */ +#undef PACKAGE_TARNAME + +/* Define to the version of this package. */ +#undef PACKAGE_VERSION + +/* Define to 1 if you have the ANSI C header files. */ +#undef STDC_HEADERS diff --git a/src/rigraph/vendor/uuid/copy.c b/src/rigraph/vendor/uuid/copy.c new file mode 100644 index 0000000..ead33aa --- /dev/null +++ b/src/rigraph/vendor/uuid/copy.c @@ -0,0 +1,45 @@ +/* + * copy.c --- copy UUIDs + * + * Copyright (C) 1996, 1997 Theodore Ts'o. + * + * %Begin-Header% + * 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, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 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. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF + * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR 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 NOT ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * %End-Header% + */ + +#include "uuidP.h" + +void uuid_copy(uuid_t dst, const uuid_t src) +{ + unsigned char *cp1; + const unsigned char *cp2; + int i; + + for (i=0, cp1 = dst, cp2 = src; i < 16; i++) + *cp1++ = *cp2++; +} diff --git a/src/rigraph/vendor/uuid/gen_uuid.c b/src/rigraph/vendor/uuid/gen_uuid.c new file mode 100644 index 0000000..4bc6143 --- /dev/null +++ b/src/rigraph/vendor/uuid/gen_uuid.c @@ -0,0 +1,536 @@ +/* + * gen_uuid.c --- generate a DCE-compatible uuid + * + * Copyright (C) 1996, 1997, 1998, 1999 Theodore Ts'o. + * + * %Begin-Header% + * 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, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 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. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF + * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR 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 NOT ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * %End-Header% + */ + +/* + * Force inclusion of SVID stuff since we need it if we're compiling in + * gcc-wall wall mode + */ +#define _DEFAULT_SOURCE + +#include "config.h" + +#ifdef _WIN32 +#define _WIN32_WINNT 0x0500 +#include +#define UUID MYUUID +#endif +#include +#ifdef HAVE_UNISTD_H +#include +#endif +#ifdef HAVE_STDLIB_H +#include +#endif +#include +#include +#include +#include +#include +#ifdef HAVE_SYS_TIME_H +#include +#endif +#include +#ifdef HAVE_SYS_FILE_H +#include +#endif +#ifdef HAVE_SYS_IOCTL_H +#include +#endif +#ifdef HAVE_SYS_SOCKET_H +#include +#endif +#ifdef HAVE_SYS_UN_H +#include +#endif +#ifdef HAVE_SYS_SOCKIO_H +#include +#endif +#ifdef HAVE_NET_IF_H +#include +#endif +#ifdef HAVE_NETINET_IN_H +#include +#endif +#ifdef HAVE_NET_IF_DL_H +#include +#endif +#if defined(__linux__) && defined(HAVE_SYS_SYSCALL_H) +#include +#endif + +#include "uuidP.h" +#include "uuidd.h" + +#ifdef USING_R +#include "igraph_random.h" +#define srand(x) ; +#define rand() RNG_INTEGER(0, RAND_MAX) +#endif + +#ifdef HAVE_TLS +#define THREAD_LOCAL static __thread +#else +#define THREAD_LOCAL static +#endif + +#ifdef _WIN32 +#if 0 /* MinGW has gettimeofday so we don't need this */ +static int gettimeofday (struct timeval *tv, void *dummy) +{ + FILETIME ftime; + uint64_t n; + + GetSystemTimeAsFileTime (&ftime); + n = (((uint64_t) ftime.dwHighDateTime << 32) + + (uint64_t) ftime.dwLowDateTime); + if (n) { + n /= 10; + n -= ((369 * 365 + 89) * (uint64_t) 86400) * 1000000; + } + + tv->tv_sec = n / 1000000; + tv->tv_usec = n % 1000000; +} +#endif + +#ifdef __MINGW32__ +int gettimeofday (struct timeval *tv, void *dummy); +#endif +#ifdef __MINGW64__ +int gettimeofday (struct timeval *tv, void *dummy); +#endif + +static int getuid (void) +{ + return 1; +} +#endif + +/* + * Get the ethernet hardware address, if we can find it... + * + * XXX for a windows version, probably should use GetAdaptersInfo: + * http://www.codeguru.com/cpp/i-n/network/networkinformation/article.php/c5451 + * commenting out get_node_id just to get gen_uuid to compile under windows + * is not the right way to go! + */ +static int get_node_id(unsigned char *node_id) +{ +#ifdef HAVE_NET_IF_H + int sd; + struct ifreq ifr, *ifrp; + struct ifconf ifc; + char buf[1024]; + int n, i; + unsigned char *a; +#ifdef HAVE_NET_IF_DL_H + struct sockaddr_dl *sdlp; +#endif + +/* + * BSD 4.4 defines the size of an ifreq to be + * max(sizeof(ifreq), sizeof(ifreq.ifr_name)+ifreq.ifr_addr.sa_len + * However, under earlier systems, sa_len isn't present, so the size is + * just sizeof(struct ifreq) + */ +#ifdef HAVE_SA_LEN +#define max(x, y) (((x) > (y)) ? (x) : (y)) +#define ifreq_size(i) max(sizeof(struct ifreq),\ + sizeof((i).ifr_name)+(i).ifr_addr.sa_len) +#else +#define ifreq_size(i) sizeof(struct ifreq) +#endif /* HAVE_SA_LEN */ + + sd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); + if (sd < 0) { + return -1; + } + memset(buf, 0, sizeof(buf)); + ifc.ifc_len = sizeof(buf); + ifc.ifc_buf = buf; + if (ioctl (sd, SIOCGIFCONF, (char *)&ifc) < 0) { + close(sd); + return -1; + } + n = ifc.ifc_len; + for (i = 0; i < n; i+= ifreq_size(*ifrp) ) { + ifrp = (struct ifreq *)((char *) ifc.ifc_buf+i); + strncpy(ifr.ifr_name, ifrp->ifr_name, IFNAMSIZ); +#if defined(SIOCGIFHWADDR) && (!defined(__sun__)) + if (ioctl(sd, SIOCGIFHWADDR, &ifr) < 0) + continue; + a = (unsigned char *) &ifr.ifr_hwaddr.sa_data; +#else +#ifdef SIOCGENADDR + if (ioctl(sd, SIOCGENADDR, &ifr) < 0) + continue; + a = (unsigned char *) ifr.ifr_enaddr; +#else +#ifdef HAVE_NET_IF_DL_H + sdlp = (struct sockaddr_dl *) &ifrp->ifr_addr; + if ((sdlp->sdl_family != AF_LINK) || (sdlp->sdl_alen != 6)) + continue; + a = (unsigned char *) &sdlp->sdl_data[sdlp->sdl_nlen]; +#else + /* + * XXX we don't have a way of getting the hardware + * address + */ + close(sd); + return 0; +#endif /* HAVE_NET_IF_DL_H */ +#endif /* SIOCGENADDR */ +#endif /* SIOCGIFHWADDR */ + if (!a[0] && !a[1] && !a[2] && !a[3] && !a[4] && !a[5]) + continue; + if (node_id) { + memcpy(node_id, a, 6); + close(sd); + return 1; + } + } + close(sd); +#endif + return 0; +} + +#if defined(__linux__) && defined(__NR_gettid) && defined(HAVE_JRAND48) +#define DO_JRAND_MIX +static unsigned short ul_jrand_seed[3]; +#endif + +static int random_get_fd(void) +{ + int i, fd = -1; + struct timeval tv; + + gettimeofday(&tv, NULL); +#ifndef _WIN32 + fd = open("/dev/urandom", O_RDONLY); + if (fd == -1) + fd = open("/dev/random", O_RDONLY | O_NONBLOCK); + if (fd >= 0) { + i = fcntl(fd, F_GETFD); + if (i >= 0) + fcntl(fd, F_SETFD, i | FD_CLOEXEC); + } +#endif + srand((getpid() << 16) ^ getuid() ^ tv.tv_sec ^ tv.tv_usec); + +#ifdef DO_JRAND_MIX + ul_jrand_seed[0] = getpid() ^ (tv.tv_sec & 0xFFFF); + ul_jrand_seed[1] = getppid() ^ (tv.tv_usec & 0xFFFF); + ul_jrand_seed[2] = (tv.tv_sec ^ tv.tv_usec) >> 16; +#endif + /* Crank the random number generator a few times */ + gettimeofday(&tv, NULL); + for (i = (tv.tv_sec ^ tv.tv_usec) & 0x1F; i > 0; i--) + rand(); + return fd; +} + +/* + * Generate a stream of random nbytes into buf. + * Use /dev/urandom if possible, and if not, + * use glibc pseudo-random functions. + */ +static void random_get_bytes(void *buf, size_t nbytes) +{ + size_t i, n = nbytes; + int fd = random_get_fd(); + int lose_counter = 0; + unsigned char *cp = (unsigned char *) buf; + + if (fd >= 0) { + while (n > 0) { + ssize_t x = read(fd, cp, n); + if (x <= 0) { + if (lose_counter++ > 16) + break; + continue; + } + n -= x; + cp += x; + lose_counter = 0; + } + + close(fd); + } + + /* + * We do this all the time, but this is the only source of + * randomness if /dev/random/urandom is out to lunch. + */ + for (cp = buf, i = 0; i < nbytes; i++) + *cp++ ^= (rand() >> 7) & 0xFF; + +#ifdef DO_JRAND_MIX + { + unsigned short tmp_seed[3]; + + memcpy(tmp_seed, ul_jrand_seed, sizeof(tmp_seed)); + ul_jrand_seed[2] = ul_jrand_seed[2] ^ syscall(__NR_gettid); + for (cp = buf, i = 0; i < nbytes; i++) + *cp++ ^= (jrand48(tmp_seed) >> 7) & 0xFF; + memcpy(ul_jrand_seed, tmp_seed, + sizeof(ul_jrand_seed)-sizeof(unsigned short)); + } +#endif + + return; +} + +/* Assume that the gettimeofday() has microsecond granularity */ +#define MAX_ADJUSTMENT 10 + +/* + * Get clock from global sequence clock counter. + * + * Return -1 if the clock counter could not be opened/locked (in this case + * pseudorandom value is returned in @ret_clock_seq), otherwise return 0. + */ +static int get_clock(uint32_t *clock_high, uint32_t *clock_low, + uint16_t *ret_clock_seq, int *num) +{ + THREAD_LOCAL int adjustment = 0; + THREAD_LOCAL struct timeval last = {0, 0}; + THREAD_LOCAL int state_fd = -2; + THREAD_LOCAL FILE *state_f; + THREAD_LOCAL uint16_t clock_seq; + struct timeval tv; + uint64_t clock_reg; + mode_t save_umask; + int len; + int ret = 0; + + if (state_fd == -2) { + save_umask = umask(0); + state_fd = open(LIBUUID_CLOCK_FILE, O_RDWR|O_CREAT, 0660); + (void) umask(save_umask); + if (state_fd != -1) { + state_f = fdopen(state_fd, "r+"); + if (!state_f) { + close(state_fd); + state_fd = -1; + ret = -1; + } + } + else + ret = -1; + } + if (state_fd >= 0) { + rewind(state_f); + } + if (state_fd >= 0) { + unsigned int cl; + unsigned long tv1, tv2; + int a; + + if (fscanf(state_f, "clock: %04x tv: %lu %lu adj: %d\n", + &cl, &tv1, &tv2, &a) == 4) { + clock_seq = cl & 0x3FFF; + last.tv_sec = tv1; + last.tv_usec = tv2; + adjustment = a; + } + } + + if ((last.tv_sec == 0) && (last.tv_usec == 0)) { + random_get_bytes(&clock_seq, sizeof(clock_seq)); + clock_seq &= 0x3FFF; + gettimeofday(&last, NULL); + last.tv_sec--; + } + +try_again: + gettimeofday(&tv, NULL); + if ((tv.tv_sec < last.tv_sec) || + ((tv.tv_sec == last.tv_sec) && + (tv.tv_usec < last.tv_usec))) { + clock_seq = (clock_seq+1) & 0x3FFF; + adjustment = 0; + last = tv; + } else if ((tv.tv_sec == last.tv_sec) && + (tv.tv_usec == last.tv_usec)) { + if (adjustment >= MAX_ADJUSTMENT) + goto try_again; + adjustment++; + } else { + adjustment = 0; + last = tv; + } + + clock_reg = tv.tv_usec*10 + adjustment; + clock_reg += ((uint64_t) tv.tv_sec)*10000000; + clock_reg += (((uint64_t) 0x01B21DD2) << 32) + 0x13814000; + + if (num && (*num > 1)) { + adjustment += *num - 1; + last.tv_usec += adjustment / 10; + adjustment = adjustment % 10; + last.tv_sec += last.tv_usec / 1000000; + last.tv_usec = last.tv_usec % 1000000; + } + + if (state_fd >= 0) { + rewind(state_f); + len = fprintf(state_f, + "clock: %04x tv: %016lu %08lu adj: %08d\n", + clock_seq, (unsigned long) last.tv_sec, (unsigned long) last.tv_usec, adjustment); + fflush(state_f); + if (ftruncate(state_fd, len) < 0) { + fprintf(state_f, " \n"); + fflush(state_f); + } + rewind(state_f); + } + + *clock_high = clock_reg >> 32; + *clock_low = clock_reg; + *ret_clock_seq = clock_seq; + return ret; +} + + +int __uuid_generate_time(uuid_t out, int *num) +{ + static unsigned char node_id[6]; + static int has_init = 0; + struct uuid uu; + uint32_t clock_mid; + int ret; + + if (!has_init) { + if (get_node_id(node_id) <= 0) { + random_get_bytes(node_id, 6); + /* + * Set multicast bit, to prevent conflicts + * with IEEE 802 addresses obtained from + * network cards + */ + node_id[0] |= 0x01; + } + has_init = 1; + } + ret = get_clock(&clock_mid, &uu.time_low, &uu.clock_seq, num); + uu.clock_seq |= 0x8000; + uu.time_mid = (uint16_t) clock_mid; + uu.time_hi_and_version = ((clock_mid >> 16) & 0x0FFF) | 0x1000; + memcpy(uu.node, node_id, 6); + uuid_pack(&uu, out); + return ret; +} + +/* + * Generate time-based UUID and store it to @out + * + * Since there is no daemon here, use fall-back right away + */ +static int uuid_generate_time_generic(uuid_t out) { + return __uuid_generate_time(out, 0); +} + +/* + * Generate time-based UUID and store it to @out. + * + * Discards return value from uuid_generate_time_generic() + */ +void uuid_generate_time(uuid_t out) +{ + (void)uuid_generate_time_generic(out); +} + + +int uuid_generate_time_safe(uuid_t out) +{ + return uuid_generate_time_generic(out); +} + + +void __uuid_generate_random(uuid_t out, int *num) +{ + uuid_t buf; + struct uuid uu; + int i, n; + + if (!num || !*num) + n = 1; + else + n = *num; + + for (i = 0; i < n; i++) { + random_get_bytes(buf, sizeof(buf)); + uuid_unpack(buf, &uu); + + uu.clock_seq = (uu.clock_seq & 0x3FFF) | 0x8000; + uu.time_hi_and_version = (uu.time_hi_and_version & 0x0FFF) + | 0x4000; + uuid_pack(&uu, out); + out += sizeof(uuid_t); + } +} + +void uuid_generate_random(uuid_t out) +{ + int num = 1; + /* No real reason to use the daemon for random uuid's -- yet */ + + __uuid_generate_random(out, &num); +} + +/* + * Check whether good random source (/dev/random or /dev/urandom) + * is available. + */ +static int have_random_source(void) +{ + struct stat s; + + return (!stat("/dev/random", &s) || !stat("/dev/urandom", &s)); +} + + +/* + * This is the generic front-end to uuid_generate_random and + * uuid_generate_time. It uses uuid_generate_random only if + * /dev/urandom is available, since otherwise we won't have + * high-quality randomness. + */ +void uuid_generate(uuid_t out) +{ + if (have_random_source()) + uuid_generate_random(out); + else + uuid_generate_time(out); +} diff --git a/src/rigraph/vendor/uuid/isnull.c b/src/rigraph/vendor/uuid/isnull.c new file mode 100644 index 0000000..931e7e7 --- /dev/null +++ b/src/rigraph/vendor/uuid/isnull.c @@ -0,0 +1,48 @@ +/* + * isnull.c --- Check whether or not the UUID is null + * + * Copyright (C) 1996, 1997 Theodore Ts'o. + * + * %Begin-Header% + * 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, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 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. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF + * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR 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 NOT ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * %End-Header% + */ + +#include "uuidP.h" + +/* Returns 1 if the uuid is the NULL uuid */ +int uuid_is_null(const uuid_t uu) +{ + const unsigned char *cp; + int i; + + for (i=0, cp = uu; i < 16; i++) + if (*cp++) + return 0; + return 1; +} + diff --git a/src/rigraph/vendor/uuid/pack.c b/src/rigraph/vendor/uuid/pack.c new file mode 100644 index 0000000..6e12476 --- /dev/null +++ b/src/rigraph/vendor/uuid/pack.c @@ -0,0 +1,69 @@ +/* + * Internal routine for packing UUIDs + * + * Copyright (C) 1996, 1997 Theodore Ts'o. + * + * %Begin-Header% + * 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, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 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. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF + * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR 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 NOT ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * %End-Header% + */ + +#include +#include "uuidP.h" + +void uuid_pack(const struct uuid *uu, uuid_t ptr) +{ + uint32_t tmp; + unsigned char *out = ptr; + + tmp = uu->time_low; + out[3] = (unsigned char) tmp; + tmp >>= 8; + out[2] = (unsigned char) tmp; + tmp >>= 8; + out[1] = (unsigned char) tmp; + tmp >>= 8; + out[0] = (unsigned char) tmp; + + tmp = uu->time_mid; + out[5] = (unsigned char) tmp; + tmp >>= 8; + out[4] = (unsigned char) tmp; + + tmp = uu->time_hi_and_version; + out[7] = (unsigned char) tmp; + tmp >>= 8; + out[6] = (unsigned char) tmp; + + tmp = uu->clock_seq; + out[9] = (unsigned char) tmp; + tmp >>= 8; + out[8] = (unsigned char) tmp; + + memcpy(out+10, uu->node, 6); +} + diff --git a/src/rigraph/vendor/uuid/parse.c b/src/rigraph/vendor/uuid/parse.c new file mode 100644 index 0000000..074383e --- /dev/null +++ b/src/rigraph/vendor/uuid/parse.c @@ -0,0 +1,79 @@ +/* + * parse.c --- UUID parsing + * + * Copyright (C) 1996, 1997 Theodore Ts'o. + * + * %Begin-Header% + * 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, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 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. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF + * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR 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 NOT ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * %End-Header% + */ + +#include +#include +#include +#include + +#include "uuidP.h" + +int uuid_parse(const char *in, uuid_t uu) +{ + struct uuid uuid; + int i; + const char *cp; + char buf[3]; + + if (strlen(in) != 36) + return -1; + for (i=0, cp = in; i <= 36; i++,cp++) { + if ((i == 8) || (i == 13) || (i == 18) || + (i == 23)) { + if (*cp == '-') + continue; + else + return -1; + } + if (i== 36) + if (*cp == 0) + continue; + if (!isxdigit(*cp)) + return -1; + } + uuid.time_low = strtoul(in, NULL, 16); + uuid.time_mid = strtoul(in+9, NULL, 16); + uuid.time_hi_and_version = strtoul(in+14, NULL, 16); + uuid.clock_seq = strtoul(in+19, NULL, 16); + cp = in+24; + buf[2] = 0; + for (i=0; i < 6; i++) { + buf[0] = *cp++; + buf[1] = *cp++; + uuid.node[i] = strtoul(buf, NULL, 16); + } + + uuid_pack(&uuid, uu); + return 0; +} diff --git a/src/rigraph/vendor/uuid/unpack.c b/src/rigraph/vendor/uuid/unpack.c new file mode 100644 index 0000000..beaaff3 --- /dev/null +++ b/src/rigraph/vendor/uuid/unpack.c @@ -0,0 +1,63 @@ +/* + * Internal routine for unpacking UUID + * + * Copyright (C) 1996, 1997 Theodore Ts'o. + * + * %Begin-Header% + * 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, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 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. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF + * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR 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 NOT ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * %End-Header% + */ + +#include +#include "uuidP.h" + +void uuid_unpack(const uuid_t in, struct uuid *uu) +{ + const uint8_t *ptr = in; + uint32_t tmp; + + tmp = *ptr++; + tmp = (tmp << 8) | *ptr++; + tmp = (tmp << 8) | *ptr++; + tmp = (tmp << 8) | *ptr++; + uu->time_low = tmp; + + tmp = *ptr++; + tmp = (tmp << 8) | *ptr++; + uu->time_mid = tmp; + + tmp = *ptr++; + tmp = (tmp << 8) | *ptr++; + uu->time_hi_and_version = tmp; + + tmp = *ptr++; + tmp = (tmp << 8) | *ptr++; + uu->clock_seq = tmp; + + memcpy(uu->node, ptr, 6); +} + diff --git a/src/rigraph/vendor/uuid/unparse.c b/src/rigraph/vendor/uuid/unparse.c new file mode 100644 index 0000000..a95bbb0 --- /dev/null +++ b/src/rigraph/vendor/uuid/unparse.c @@ -0,0 +1,76 @@ +/* + * unparse.c -- convert a UUID to string + * + * Copyright (C) 1996, 1997 Theodore Ts'o. + * + * %Begin-Header% + * 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, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 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. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF + * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR 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 NOT ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * %End-Header% + */ + +#include + +#include "uuidP.h" + +static const char *fmt_lower = + "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x"; + +static const char *fmt_upper = + "%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X"; + +#ifdef UUID_UNPARSE_DEFAULT_UPPER +#define FMT_DEFAULT fmt_upper +#else +#define FMT_DEFAULT fmt_lower +#endif + +static void uuid_unparse_x(const uuid_t uu, char *out, const char *fmt) +{ + struct uuid uuid; + + uuid_unpack(uu, &uuid); + sprintf(out, fmt, + uuid.time_low, uuid.time_mid, uuid.time_hi_and_version, + uuid.clock_seq >> 8, uuid.clock_seq & 0xFF, + uuid.node[0], uuid.node[1], uuid.node[2], + uuid.node[3], uuid.node[4], uuid.node[5]); +} + +void uuid_unparse_lower(const uuid_t uu, char *out) +{ + uuid_unparse_x(uu, out, fmt_lower); +} + +void uuid_unparse_upper(const uuid_t uu, char *out) +{ + uuid_unparse_x(uu, out, fmt_upper); +} + +void uuid_unparse(const uuid_t uu, char *out) +{ + uuid_unparse_x(uu, out, FMT_DEFAULT); +} diff --git a/src/rigraph/vendor/uuid/uuid.h b/src/rigraph/vendor/uuid/uuid.h new file mode 100644 index 0000000..874d65a --- /dev/null +++ b/src/rigraph/vendor/uuid/uuid.h @@ -0,0 +1,104 @@ +/* + * Public include file for the UUID library + * + * Copyright (C) 1996, 1997, 1998 Theodore Ts'o. + * + * %Begin-Header% + * 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, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 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. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF + * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR 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 NOT ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * %End-Header% + */ + +#ifndef _UUID_UUID_H +#define _UUID_UUID_H + +#include +#ifndef _WIN32 +#include +#endif +#include + +typedef unsigned char uuid_t[16]; + +/* UUID Variant definitions */ +#define UUID_VARIANT_NCS 0 +#define UUID_VARIANT_DCE 1 +#define UUID_VARIANT_MICROSOFT 2 +#define UUID_VARIANT_OTHER 3 + +/* UUID Type definitions */ +#define UUID_TYPE_DCE_TIME 1 +#define UUID_TYPE_DCE_RANDOM 4 + +/* Allow UUID constants to be defined */ +#ifdef __GNUC__ +#define UUID_DEFINE(name,u0,u1,u2,u3,u4,u5,u6,u7,u8,u9,u10,u11,u12,u13,u14,u15) \ + static const uuid_t name __attribute__ ((unused)) = {u0,u1,u2,u3,u4,u5,u6,u7,u8,u9,u10,u11,u12,u13,u14,u15} +#else +#define UUID_DEFINE(name,u0,u1,u2,u3,u4,u5,u6,u7,u8,u9,u10,u11,u12,u13,u14,u15) \ + static const uuid_t name = {u0,u1,u2,u3,u4,u5,u6,u7,u8,u9,u10,u11,u12,u13,u14,u15} +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* clear.c */ +void uuid_clear(uuid_t uu); + +/* compare.c */ +int uuid_compare(const uuid_t uu1, const uuid_t uu2); + +/* copy.c */ +void uuid_copy(uuid_t dst, const uuid_t src); + +/* gen_uuid.c */ +void uuid_generate(uuid_t out); +void uuid_generate_random(uuid_t out); +void uuid_generate_time(uuid_t out); +int uuid_generate_time_safe(uuid_t out); + +/* isnull.c */ +int uuid_is_null(const uuid_t uu); + +/* parse.c */ +int uuid_parse(const char *in, uuid_t uu); + +/* unparse.c */ +void uuid_unparse(const uuid_t uu, char *out); +void uuid_unparse_lower(const uuid_t uu, char *out); +void uuid_unparse_upper(const uuid_t uu, char *out); + +/* uuid_time.c */ +time_t uuid_time(const uuid_t uu, struct timeval *ret_tv); +int uuid_type(const uuid_t uu); +int uuid_variant(const uuid_t uu); + +#ifdef __cplusplus +} +#endif + +#endif /* _UUID_UUID_H */ diff --git a/src/rigraph/vendor/uuid/uuidP.h b/src/rigraph/vendor/uuid/uuidP.h new file mode 100644 index 0000000..604d8bf --- /dev/null +++ b/src/rigraph/vendor/uuid/uuidP.h @@ -0,0 +1,63 @@ +/* + * uuid.h -- private header file for uuids + * + * Copyright (C) 1996, 1997 Theodore Ts'o. + * + * %Begin-Header% + * 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, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 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. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF + * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR 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 NOT ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * %End-Header% + */ + +#include +#include + +#include "config.h" + +#include "uuid.h" + +#define LIBUUID_CLOCK_FILE "/var/lib/libuuid/clock.txt" + +/* + * Offset between 15-Oct-1582 and 1-Jan-70 + */ +#define TIME_OFFSET_HIGH 0x01B21DD2 +#define TIME_OFFSET_LOW 0x13814000 + +struct uuid { + uint32_t time_low; + uint16_t time_mid; + uint16_t time_hi_and_version; + uint16_t clock_seq; + uint8_t node[6]; +}; + + +/* + * prototypes + */ +void uuid_pack(const struct uuid *uu, uuid_t ptr); +void uuid_unpack(const uuid_t in, struct uuid *uu); diff --git a/src/rigraph/vendor/uuid/uuidd.h b/src/rigraph/vendor/uuid/uuidd.h new file mode 100644 index 0000000..2f70968 --- /dev/null +++ b/src/rigraph/vendor/uuid/uuidd.h @@ -0,0 +1,54 @@ +/* + * Definitions used by the uuidd daemon + * + * Copyright (C) 2007 Theodore Ts'o. + * + * %Begin-Header% + * 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, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 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. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF + * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR 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 NOT ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * %End-Header% + */ + +#ifndef _UUID_UUIDD_H +#define _UUID_UUIDD_H + +#define UUIDD_DIR _PATH_LOCALSTATEDIR "/uuidd" +#define UUIDD_SOCKET_PATH UUIDD_DIR "/request" +#define UUIDD_PIDFILE_PATH UUIDD_DIR "/uuidd.pid" +#define UUIDD_PATH "/usr/sbin/uuidd" + +#define UUIDD_OP_GETPID 0 +#define UUIDD_OP_GET_MAXOP 1 +#define UUIDD_OP_TIME_UUID 2 +#define UUIDD_OP_RANDOM_UUID 3 +#define UUIDD_OP_BULK_TIME_UUID 4 +#define UUIDD_OP_BULK_RANDOM_UUID 5 +#define UUIDD_MAX_OP UUIDD_OP_BULK_RANDOM_UUID + +extern int __uuid_generate_time(uuid_t out, int *num); +extern void __uuid_generate_random(uuid_t out, int *num); + +#endif /* _UUID_UUID_H */ diff --git a/src/rigraph/vendor/uuid/win32/config.h b/src/rigraph/vendor/uuid/win32/config.h new file mode 100644 index 0000000..ffb2aba --- /dev/null +++ b/src/rigraph/vendor/uuid/win32/config.h @@ -0,0 +1,84 @@ +/* src/config.h. Generated from config.h.in by configure. */ +/* src/config.h.in. Generated from configure.ac by autoheader. */ +/* -- reflects MinGW + Win32 -- */ + +/* Define to 1 if you have the header file. */ +#define HAVE_INTTYPES_H 1 + +/* Define to 1 if you have the `jrand48' function. */ +/* #undef HAVE_JRAND48 */ + +/* Define to 1 if you have the header file. */ +#define HAVE_MEMORY_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_NETINET_IN_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_NET_IF_DL_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_NET_IF_H */ + +/* Define if struct sockaddr contains sa_len */ +/* #undef HAVE_SA_LEN */ + +/* Define to 1 if you have the header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRINGS_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_FILE_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_IOCTL_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_SOCKET_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_SOCKIO_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_SYSCALL_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TIME_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_UN_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_UNISTD_H 1 + +/* Define to the address where bug reports for this package should be sent. */ +#define PACKAGE_BUGREPORT "Simon.Urbanek@r-project.org" + +/* Define to the full name of this package. */ +#define PACKAGE_NAME "uuid" + +/* Define to the full name and version of this package. */ +#define PACKAGE_STRING "uuid 0.1" + +/* Define to the one symbol short name of this package. */ +#define PACKAGE_TARNAME "uuid" + +/* Define to the version of this package. */ +#define PACKAGE_VERSION "0.1" + +/* Define to 1 if you have the ANSI C header files. */ +#define STDC_HEADERS 1 From 71bbf163d216bcd0321e4a585a4d864baa8fac41 Mon Sep 17 00:00:00 2001 From: evanbiederstedt Date: Thu, 7 Apr 2022 23:58:15 -0400 Subject: [PATCH 13/20] works! --- R/RcppExports.R | 23 +++++++++++++ src/Makevars | 2 +- src/RcppExports.cpp | 38 ++++++++++++++++++++++ src/leiden.cpp | 79 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 141 insertions(+), 1 deletion(-) create mode 100644 R/RcppExports.R create mode 100644 src/RcppExports.cpp create mode 100644 src/leiden.cpp diff --git a/R/RcppExports.R b/R/RcppExports.R new file mode 100644 index 0000000..0826ef9 --- /dev/null +++ b/R/RcppExports.R @@ -0,0 +1,23 @@ +# Generated by using Rcpp::compileAttributes() -> do not edit by hand +# Generator token: 10BE3573-1514-4C36-9D1C-5A225CD40393 + +#' Finds the optimal partition using the Leiden algorithm +#' +#' @param graph The igraph graph to define the partition on +#' @param edge_weights Vector of edge weights. In weighted graphs, a real number is assigned to each (directed or undirected) edge. Refer to igraph, weighted graphs. +#' @param resolution Integer resoluiton parameter controlling communities detected (default=1.0) Higher resolutions lead to more communities, while lower resolutions lead to fewer communities. +#' @param niter Number of iterations that the algorithm should be run for (default=2) +#' @return A vector of membership values +#' @examples +#' library(igraph) +#' library(leidenAlg) +#' +#' g <- make_star(10) +#' E(g)$weight <- seq(ecount(g)) +#' find_partition(g, E(g)$weight) +#' +#' @export +find_partition <- function(graph, edge_weights, resolution = 1.0, niter = 2L) { + .Call('_leidenAlg_find_partition', PACKAGE = 'leidenAlg', graph, edge_weights, resolution, niter) +} + diff --git a/src/Makevars b/src/Makevars index e70087d..bdeaefa 100644 --- a/src/Makevars +++ b/src/Makevars @@ -10,7 +10,7 @@ LDFLAGS += -lpthread -PKG_LIBS=-L/usr/lib/ -L"." -lpthread -lstdc++ -lleidenalg -lrigraph -lm `$(R_HOME)/bin/Rscript -e "Rcpp:::LdFlags()"` $(LAPACK_LIBS) $(BLAS_LIBS) $(FLIBS) $(SHLIB_OPENMP_CXXFLAGS) $(PKGB_PATH)/igraph$(SHLIB_EXT) +PKG_LIBS=-L/usr/lib/ -L"." -lpthread -lstdc++ -lleidenalg -lrigraph -lm `$(R_HOME)/bin/Rscript -e "Rcpp:::LdFlags()"` $(LAPACK_LIBS) $(BLAS_LIBS) $(FLIBS) $(SHLIB_OPENMP_CXXFLAGS) CXX_STD = CXX11 MkInclude = $(R_HOME)/etc${R_ARCH}/Makeconf diff --git a/src/RcppExports.cpp b/src/RcppExports.cpp new file mode 100644 index 0000000..fd6dc48 --- /dev/null +++ b/src/RcppExports.cpp @@ -0,0 +1,38 @@ +// Generated by using Rcpp::compileAttributes() -> do not edit by hand +// Generator token: 10BE3573-1514-4C36-9D1C-5A225CD40393 + +#include +#include +#include + +using namespace Rcpp; + +#ifdef RCPP_USE_GLOBAL_ROSTREAM +Rcpp::Rostream& Rcpp::Rcout = Rcpp::Rcpp_cout_get(); +Rcpp::Rostream& Rcpp::Rcerr = Rcpp::Rcpp_cerr_get(); +#endif + +// find_partition +std::vector find_partition(SEXP graph, std::vector& edge_weights, double resolution, int niter); +RcppExport SEXP _leidenAlg_find_partition(SEXP graphSEXP, SEXP edge_weightsSEXP, SEXP resolutionSEXP, SEXP niterSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< SEXP >::type graph(graphSEXP); + Rcpp::traits::input_parameter< std::vector& >::type edge_weights(edge_weightsSEXP); + Rcpp::traits::input_parameter< double >::type resolution(resolutionSEXP); + Rcpp::traits::input_parameter< int >::type niter(niterSEXP); + rcpp_result_gen = Rcpp::wrap(find_partition(graph, edge_weights, resolution, niter)); + return rcpp_result_gen; +END_RCPP +} + +static const R_CallMethodDef CallEntries[] = { + {"_leidenAlg_find_partition", (DL_FUNC) &_leidenAlg_find_partition, 4}, + {NULL, NULL, 0} +}; + +RcppExport void R_init_leidenAlg(DllInfo *dll) { + R_registerRoutines(dll, NULL, CallEntries, NULL, NULL); + R_useDynamicSymbols(dll, FALSE); +} diff --git a/src/leiden.cpp b/src/leiden.cpp new file mode 100644 index 0000000..2164975 --- /dev/null +++ b/src/leiden.cpp @@ -0,0 +1,79 @@ +#include +#include +#include +#include +#include "igraph.h" +#include "leidenalg/include/GraphHelper.h" +#include "leidenalg/include/Optimiser.h" +#include "leidenalg/include/RBERVertexPartition.h" +#include "leidenalg/include/RBConfigurationVertexPartition.h" + +using namespace std; +using namespace Rcpp; + + +// a wrapper for the Leidgen algorithm implementation (https://github.com/vtraag/leidenalg) + +int R_SEXP_to_vector(SEXP sv, igraph_vector_t *v) { + v->stor_begin=REAL(sv); + v->stor_end=v->stor_begin+GET_LENGTH(sv); + v->end=v->stor_end; + return 0; +} + +int R_SEXP_to_igraph(SEXP graph, igraph_t *res) { + + res->n=(igraph_integer_t) REAL(VECTOR_ELT(graph, 0))[0]; + res->directed=LOGICAL(VECTOR_ELT(graph, 1))[0]; + R_SEXP_to_vector(VECTOR_ELT(graph, 2), &res->from); + R_SEXP_to_vector(VECTOR_ELT(graph, 3), &res->to); + R_SEXP_to_vector(VECTOR_ELT(graph, 4), &res->oi); + R_SEXP_to_vector(VECTOR_ELT(graph, 5), &res->ii); + R_SEXP_to_vector(VECTOR_ELT(graph, 6), &res->os); + R_SEXP_to_vector(VECTOR_ELT(graph, 7), &res->is); + + /* attributes */ + REAL(VECTOR_ELT(VECTOR_ELT(graph, 8), 0))[0] = 1; /* R objects refcount */ + REAL(VECTOR_ELT(VECTOR_ELT(graph, 8), 0))[1] = 0; /* igraph_t objects */ + res->attr=VECTOR_ELT(graph, 8); + + return 0; +} + +//' Finds the optimal partition using the Leiden algorithm +//' +//' @param graph The igraph graph to define the partition on +//' @param edge_weights Vector of edge weights. In weighted graphs, a real number is assigned to each (directed or undirected) edge. Refer to igraph, weighted graphs. +//' @param resolution Integer resoluiton parameter controlling communities detected (default=1.0) Higher resolutions lead to more communities, while lower resolutions lead to fewer communities. +//' @param niter Number of iterations that the algorithm should be run for (default=2) +//' @return A vector of membership values +//' @examples +//' library(igraph) +//' library(leidenAlg) +//' +//' g <- make_star(10) +//' E(g)$weight <- seq(ecount(g)) +//' find_partition(g, E(g)$weight) +//' +//' @export +// [[Rcpp::export]] +std::vector find_partition(SEXP graph, std::vector& edge_weights, double resolution=1.0, int niter=2) { + igraph_t g; + + R_SEXP_to_igraph(graph, &g); + Graph og(&g, edge_weights); + Optimiser o( (int) (R::runif(0,1)*(double)RAND_MAX) ); + RBConfigurationVertexPartition p(&og,resolution); + //RBERVertexPartition p(&og,resolution); + //o.find_partition(og,resolution); + double val=1; + int iter=0; + while(val>0 && (iter Date: Thu, 7 Apr 2022 23:58:56 -0400 Subject: [PATCH 14/20] #define NDEBUG 1 --- src/RcppExports.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/RcppExports.cpp b/src/RcppExports.cpp index fd6dc48..737c1f4 100644 --- a/src/RcppExports.cpp +++ b/src/RcppExports.cpp @@ -2,6 +2,7 @@ // Generator token: 10BE3573-1514-4C36-9D1C-5A225CD40393 #include +#define NDEBUG 1 #include #include From a30deed7af539338ca1f66f6b6a09dd296a213bd Mon Sep 17 00:00:00 2001 From: evanbiederstedt Date: Fri, 8 Apr 2022 00:00:12 -0400 Subject: [PATCH 15/20] basic Makefile.win --- src/rigraph/Makefile.win | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 src/rigraph/Makefile.win diff --git a/src/rigraph/Makefile.win b/src/rigraph/Makefile.win new file mode 100644 index 0000000..b3398a3 --- /dev/null +++ b/src/rigraph/Makefile.win @@ -0,0 +1,27 @@ +C ?= gcc +CFLAGS = -O3 -std=c11 -std=gnu11 +PKG_CFLAGS = -O3 -std=c11 -std=gnu11 -pthread -fPIC $(SHLIB_OPENMP_CXXFLAGS) +PKG_CFLAGS += -I"./include" -I"../rigraph/include" +LDFLAGS += -lpthread + + +CXX ?= g++ +PKG_CXXFLAGS = -O3 -std=c++11 -pthread -fPIC $(SHLIB_OPENMP_CXXFLAGS) +PKG_CXXFLAGS += -I"./include" -I"../rigraph/include" +LDFLAGS += -lpthread + +OBJECTS=core/centrality/betweenness.o core/centrality/centrality_other.o core/centrality/centralization.o core/centrality/closeness.o core/centrality/coreness.o core/centrality/prpack/prpack_base_graph.o core/centrality/prpack/prpack_igraph_graph.o core/centrality/prpack/prpack_preprocessed_ge_graph.o core/centrality/prpack/prpack_preprocessed_gs_graph.o core/centrality/prpack/prpack_preprocessed_scc_graph.o core/centrality/prpack/prpack_preprocessed_schur_graph.o core/centrality/prpack/prpack_result.o core/centrality/prpack/prpack_solver.o core/centrality/prpack/prpack_utils.o core/cliques/cliquer/cliquer.o core/cliques/cliquer/cliquer_graph.o core/cliques/cliquer/reorder.o core/cliques/cliquer_wrapper.o core/cliques/cliques.o core/cliques/glet.o core/cliques/maximal_cliques.o core/community/community_misc.o core/community/edge_betweenness.o core/community/fast_modularity.o core/community/fluid.o core/community/infomap/infomap.o core/community/infomap/infomap_FlowGraph.o core/community/infomap/infomap_Greedy.o core/community/infomap/infomap_Node.o core/community/label_propagation.o core/community/leading_eigenvector.o core/community/leiden.o core/community/louvain.o core/community/modularity.o core/community/optimal_modularity.o core/community/spinglass/NetDataTypes.o core/community/spinglass/NetRoutines.o core/community/spinglass/clustertool.o core/community/spinglass/pottsmodel_2.o core/community/walktrap/walktrap.o core/community/walktrap/walktrap_communities.o core/community/walktrap/walktrap_graph.o core/community/walktrap/walktrap_heap.o core/connectivity/cohesive_blocks.o core/connectivity/components.o core/connectivity/separators.o core/constructors/adjacency.o core/constructors/atlas.o core/constructors/basic_constructors.o core/constructors/de_bruijn.o core/constructors/famous.o core/constructors/full.o core/constructors/kautz.o core/constructors/lcf.o core/constructors/linegraph.o core/constructors/prufer.o core/constructors/regular.o core/core/array.o core/core/buckets.o core/core/cutheap.o core/core/dqueue.o core/core/error.o core/core/estack.o core/core/fixed_vectorlist.o core/core/grid.o core/core/heap.o core/core/indheap.o core/core/interruption.o core/core/marked_queue.o core/core/matrix.o core/core/memory.o core/core/printing.o core/core/progress.o core/core/psumtree.o core/core/set.o core/core/sparsemat.o core/core/spmatrix.o core/core/stack.o core/core/statusbar.o core/core/strvector.o core/core/trie.o core/core/vector.o core/core/vector_ptr.o core/flow/flow.o core/flow/st-cuts.o core/games/barabasi.o core/games/callaway_traits.o core/games/citations.o core/games/correlated.o core/games/degree_sequence.o core/games/degree_sequence_vl/gengraph_box_list.o core/games/degree_sequence_vl/gengraph_degree_sequence.o core/games/degree_sequence_vl/gengraph_graph_molloy_hash.o core/games/degree_sequence_vl/gengraph_graph_molloy_optimized.o core/games/degree_sequence_vl/gengraph_mr-connected.o core/games/degree_sequence_vl/gengraph_powerlaw.o core/games/degree_sequence_vl/gengraph_random.o core/games/dotproduct.o core/games/erdos_renyi.o core/games/establishment.o core/games/forestfire.o core/games/grg.o core/games/growing_random.o core/games/islands.o core/games/k_regular.o core/games/preference.o core/games/recent_degree.o core/games/sbm.o core/games/static_fitness.o core/games/tree.o core/games/watts_strogatz.o core/graph/adjlist.o core/graph/attributes.o core/graph/basic_query.o core/graph/cattributes.o core/graph/iterators.o core/graph/type_indexededgelist.o core/graph/visitors.o core/hrg/hrg.o core/hrg/hrg_types.o core/internal/glpk_support.o core/internal/hacks.o core/internal/lsap.o core/internal/qsort.o core/internal/qsort_r.o core/internal/zeroin.o core/io/dimacs.o core/io/dl-lexer.o core/io/dl-parser.o core/io/dl.o core/io/dot.o core/io/edgelist.o core/io/gml-lexer.o core/io/gml-parser.o core/io/gml-tree.o core/io/gml.o core/io/graphdb.o core/io/graphml.o core/io/leda.o core/io/lgl-lexer.o core/io/lgl-parser.o core/io/lgl.o core/io/ncol-lexer.o core/io/ncol-parser.o core/io/ncol.o core/io/pajek-lexer.o core/io/pajek-parser.o core/io/pajek.o core/isomorphism/bliss.o core/isomorphism/bliss/defs.o core/isomorphism/bliss/graph.o core/isomorphism/bliss/heap.o core/isomorphism/bliss/orbit.o core/isomorphism/bliss/partition.o core/isomorphism/bliss/uintseqhash.o core/isomorphism/bliss/utils.o core/isomorphism/isoclasses.o core/isomorphism/isomorphism_misc.o core/isomorphism/lad.o core/isomorphism/queries.o core/isomorphism/vf2.o core/layout/circular.o core/layout/davidson_harel.o core/layout/drl/DensityGrid.o core/layout/drl/DensityGrid_3d.o core/layout/drl/drl_graph.o core/layout/drl/drl_graph_3d.o core/layout/drl/drl_layout.o core/layout/drl/drl_layout_3d.o core/layout/drl/drl_parse.o core/layout/fruchterman_reingold.o core/layout/gem.o core/layout/graphopt.o core/layout/kamada_kawai.o core/layout/large_graph.o core/layout/layout_bipartite.o core/layout/layout_grid.o core/layout/layout_random.o core/layout/mds.o core/layout/merge_dla.o core/layout/merge_grid.o core/layout/reingold_tilford.o core/layout/sugiyama.o core/linalg/arpack.o core/linalg/blas.o core/linalg/eigen.o core/linalg/lapack.o core/math/bfgs.o core/math/complex.o core/math/utils.o core/misc/bipartite.o core/misc/chordality.o core/misc/cocitation.o core/misc/coloring.o core/misc/conversion.o core/misc/degree_sequence.o core/misc/embedding.o core/misc/feedback_arc_set.o core/misc/graphicality.o core/misc/matching.o core/misc/microscopic_update.o core/misc/mixing.o core/misc/motifs.o core/misc/other.o core/misc/scan.o core/misc/sir.o core/misc/spanning_trees.o core/operators/add_edge.o core/operators/complementer.o core/operators/compose.o core/operators/connect_neighborhood.o core/operators/contract.o core/operators/difference.o core/operators/disjoint_union.o core/operators/intersection.o core/operators/misc_internal.o core/operators/permute.o core/operators/rewire.o core/operators/rewire_edges.o core/operators/simplify.o core/operators/subgraph.o core/operators/union.o core/paths/all_shortest_paths.o core/paths/bellman_ford.o core/paths/dijkstra.o core/paths/distances.o core/paths/eulerian.o core/paths/histogram.o core/paths/johnson.o core/paths/random_walk.o core/paths/shortest_paths.o core/paths/simple_paths.o core/paths/unweighted.o core/properties/basic_properties.o core/properties/constraint.o core/properties/convergence_degree.o core/properties/dag.o core/properties/degrees.o core/properties/girth.o core/properties/loops.o core/properties/multiplicity.o core/properties/neighborhood.o core/properties/spectral.o core/properties/trees.o core/properties/triangles.o core/random/random.o core/scg/scg.o core/scg/scg_approximate_methods.o core/scg/scg_exact_scg.o core/scg/scg_kmeans.o core/scg/scg_optimal_method.o core/scg/scg_utils.o core/version.o vendor/cs/cs_add.o vendor/cs/cs_amd.o vendor/cs/cs_chol.o vendor/cs/cs_cholsol.o vendor/cs/cs_compress.o vendor/cs/cs_counts.o vendor/cs/cs_cumsum.o vendor/cs/cs_dfs.o vendor/cs/cs_dmperm.o vendor/cs/cs_droptol.o vendor/cs/cs_dropzeros.o vendor/cs/cs_dupl.o vendor/cs/cs_entry.o vendor/cs/cs_ereach.o vendor/cs/cs_etree.o vendor/cs/cs_fkeep.o vendor/cs/cs_gaxpy.o vendor/cs/cs_happly.o vendor/cs/cs_house.o vendor/cs/cs_ipvec.o vendor/cs/cs_leaf.o vendor/cs/cs_load.o vendor/cs/cs_lsolve.o vendor/cs/cs_ltsolve.o vendor/cs/cs_lu.o vendor/cs/cs_lusol.o vendor/cs/cs_malloc.o vendor/cs/cs_maxtrans.o vendor/cs/cs_multiply.o vendor/cs/cs_norm.o vendor/cs/cs_permute.o vendor/cs/cs_pinv.o vendor/cs/cs_post.o vendor/cs/cs_print.o vendor/cs/cs_pvec.o vendor/cs/cs_qr.o vendor/cs/cs_qrsol.o vendor/cs/cs_randperm.o vendor/cs/cs_reach.o vendor/cs/cs_scatter.o vendor/cs/cs_scc.o vendor/cs/cs_schol.o vendor/cs/cs_spsolve.o vendor/cs/cs_sqr.o vendor/cs/cs_symperm.o vendor/cs/cs_tdfs.o vendor/cs/cs_transpose.o vendor/cs/cs_updown.o vendor/cs/cs_usolve.o vendor/cs/cs_util.o vendor/cs/cs_utsolve.o vendor/mini-gmp/mini-gmp.o vendor/plfit/gss.o vendor/plfit/hzeta.o vendor/plfit/kolmogorov.o vendor/plfit/lbfgs.o vendor/plfit/mt.o vendor/plfit/options.o vendor/plfit/platform.o vendor/plfit/plfit.o vendor/plfit/plfit_error.o vendor/plfit/rbinom.o vendor/plfit/sampling.o vendor/arpack/dgetv0.o vendor/arpack/dlaqrb.o vendor/arpack/dmout.o vendor/arpack/dnaitr.o vendor/arpack/dnapps.o vendor/arpack/dnaup2.o vendor/arpack/dnaupd.o vendor/arpack/dnconv.o vendor/arpack/dneigh.o vendor/arpack/dneupd.o vendor/arpack/dngets.o vendor/arpack/dsaitr.o vendor/arpack/dsapps.o vendor/arpack/dsaup2.o vendor/arpack/dsaupd.o vendor/arpack/dsconv.o vendor/arpack/dseigt.o vendor/arpack/dsesrt.o vendor/arpack/dseupd.o vendor/arpack/dsgets.o vendor/arpack/dsortc.o vendor/arpack/dsortr.o vendor/arpack/dstatn.o vendor/arpack/dstats.o vendor/arpack/dstqrb.o vendor/arpack/dvout.o vendor/arpack/ivout.o vendor/arpack/second.o vendor/arpack/wrap.o vendor/simpleraytracer/Color.o vendor/simpleraytracer/Light.o vendor/simpleraytracer/Point.o vendor/simpleraytracer/RIgraphRay.o vendor/simpleraytracer/Ray.o vendor/simpleraytracer/RayTracer.o vendor/simpleraytracer/RayVector.o vendor/simpleraytracer/Shape.o vendor/simpleraytracer/Sphere.o vendor/simpleraytracer/Triangle.o vendor/simpleraytracer/unit_limiter.o vendor/uuid/R.o vendor/uuid/clear.o vendor/uuid/compare.o vendor/uuid/copy.o vendor/uuid/gen_uuid.o vendor/uuid/isnull.o vendor/uuid/pack.o vendor/uuid/parse.o vendor/uuid/unpack.o vendor/uuid/unparse.o rinterface.o rinterface_extra.o rrandom.o lazyeval.o + +LIB=../librigraph.a +lib: $(LIB) + +## ignoring: core/centrality/prpack.o + +$(LIB): core/centrality/betweenness.o core/centrality/centrality_other.o core/centrality/centralization.o core/centrality/closeness.o core/centrality/coreness.o core/centrality/prpack/prpack_base_graph.o core/centrality/prpack/prpack_igraph_graph.o core/centrality/prpack/prpack_preprocessed_ge_graph.o core/centrality/prpack/prpack_preprocessed_gs_graph.o core/centrality/prpack/prpack_preprocessed_scc_graph.o core/centrality/prpack/prpack_preprocessed_schur_graph.o core/centrality/prpack/prpack_result.o core/centrality/prpack/prpack_solver.o core/centrality/prpack/prpack_utils.o core/cliques/cliquer/cliquer.o core/cliques/cliquer/cliquer_graph.o core/cliques/cliquer/reorder.o core/cliques/cliquer_wrapper.o core/cliques/cliques.o core/cliques/glet.o core/cliques/maximal_cliques.o core/community/community_misc.o core/community/edge_betweenness.o core/community/fast_modularity.o core/community/fluid.o core/community/infomap/infomap.o core/community/infomap/infomap_FlowGraph.o core/community/infomap/infomap_Greedy.o core/community/infomap/infomap_Node.o core/community/label_propagation.o core/community/leading_eigenvector.o core/community/leiden.o core/community/louvain.o core/community/modularity.o core/community/optimal_modularity.o core/community/spinglass/NetDataTypes.o core/community/spinglass/NetRoutines.o core/community/spinglass/clustertool.o core/community/spinglass/pottsmodel_2.o core/community/walktrap/walktrap.o core/community/walktrap/walktrap_communities.o core/community/walktrap/walktrap_graph.o core/community/walktrap/walktrap_heap.o core/connectivity/cohesive_blocks.o core/connectivity/components.o core/connectivity/separators.o core/constructors/adjacency.o core/constructors/atlas.o core/constructors/basic_constructors.o core/constructors/de_bruijn.o core/constructors/famous.o core/constructors/full.o core/constructors/kautz.o core/constructors/lcf.o core/constructors/linegraph.o core/constructors/prufer.o core/constructors/regular.o core/core/array.o core/core/buckets.o core/core/cutheap.o core/core/dqueue.o core/core/error.o core/core/estack.o core/core/fixed_vectorlist.o core/core/grid.o core/core/heap.o core/core/indheap.o core/core/interruption.o core/core/marked_queue.o core/core/matrix.o core/core/memory.o core/core/printing.o core/core/progress.o core/core/psumtree.o core/core/set.o core/core/sparsemat.o core/core/spmatrix.o core/core/stack.o core/core/statusbar.o core/core/strvector.o core/core/trie.o core/core/vector.o core/core/vector_ptr.o core/flow/flow.o core/flow/st-cuts.o core/games/barabasi.o core/games/callaway_traits.o core/games/citations.o core/games/correlated.o core/games/degree_sequence.o core/games/degree_sequence_vl/gengraph_box_list.o core/games/degree_sequence_vl/gengraph_degree_sequence.o core/games/degree_sequence_vl/gengraph_graph_molloy_hash.o core/games/degree_sequence_vl/gengraph_graph_molloy_optimized.o core/games/degree_sequence_vl/gengraph_mr-connected.o core/games/degree_sequence_vl/gengraph_powerlaw.o core/games/degree_sequence_vl/gengraph_random.o core/games/dotproduct.o core/games/erdos_renyi.o core/games/establishment.o core/games/forestfire.o core/games/grg.o core/games/growing_random.o core/games/islands.o core/games/k_regular.o core/games/preference.o core/games/recent_degree.o core/games/sbm.o core/games/static_fitness.o core/games/tree.o core/games/watts_strogatz.o core/graph/adjlist.o core/graph/attributes.o core/graph/basic_query.o core/graph/cattributes.o core/graph/iterators.o core/graph/type_indexededgelist.o core/graph/visitors.o core/hrg/hrg.o core/hrg/hrg_types.o core/internal/glpk_support.o core/internal/hacks.o core/internal/lsap.o core/internal/qsort.o core/internal/qsort_r.o core/internal/zeroin.o core/io/dimacs.o core/io/dl-lexer.o core/io/dl-parser.o core/io/dl.o core/io/dot.o core/io/edgelist.o core/io/gml-lexer.o core/io/gml-parser.o core/io/gml-tree.o core/io/gml.o core/io/graphdb.o core/io/graphml.o core/io/leda.o core/io/lgl-lexer.o core/io/lgl-parser.o core/io/lgl.o core/io/ncol-lexer.o core/io/ncol-parser.o core/io/ncol.o core/io/pajek-lexer.o core/io/pajek-parser.o core/io/pajek.o core/isomorphism/bliss.o core/isomorphism/bliss/defs.o core/isomorphism/bliss/graph.o core/isomorphism/bliss/heap.o core/isomorphism/bliss/orbit.o core/isomorphism/bliss/partition.o core/isomorphism/bliss/uintseqhash.o core/isomorphism/bliss/utils.o core/isomorphism/isoclasses.o core/isomorphism/isomorphism_misc.o core/isomorphism/lad.o core/isomorphism/queries.o core/isomorphism/vf2.o core/layout/circular.o core/layout/davidson_harel.o core/layout/drl/DensityGrid.o core/layout/drl/DensityGrid_3d.o core/layout/drl/drl_graph.o core/layout/drl/drl_graph_3d.o core/layout/drl/drl_layout.o core/layout/drl/drl_layout_3d.o core/layout/drl/drl_parse.o core/layout/fruchterman_reingold.o core/layout/gem.o core/layout/graphopt.o core/layout/kamada_kawai.o core/layout/large_graph.o core/layout/layout_bipartite.o core/layout/layout_grid.o core/layout/layout_random.o core/layout/mds.o core/layout/merge_dla.o core/layout/merge_grid.o core/layout/reingold_tilford.o core/layout/sugiyama.o core/linalg/arpack.o core/linalg/blas.o core/linalg/eigen.o core/linalg/lapack.o core/math/bfgs.o core/math/complex.o core/math/utils.o core/misc/bipartite.o core/misc/chordality.o core/misc/cocitation.o core/misc/coloring.o core/misc/conversion.o core/misc/degree_sequence.o core/misc/embedding.o core/misc/feedback_arc_set.o core/misc/graphicality.o core/misc/matching.o core/misc/microscopic_update.o core/misc/mixing.o core/misc/motifs.o core/misc/other.o core/misc/scan.o core/misc/sir.o core/misc/spanning_trees.o core/operators/add_edge.o core/operators/complementer.o core/operators/compose.o core/operators/connect_neighborhood.o core/operators/contract.o core/operators/difference.o core/operators/disjoint_union.o core/operators/intersection.o core/operators/misc_internal.o core/operators/permute.o core/operators/rewire.o core/operators/rewire_edges.o core/operators/simplify.o core/operators/subgraph.o core/operators/union.o core/paths/all_shortest_paths.o core/paths/bellman_ford.o core/paths/dijkstra.o core/paths/distances.o core/paths/eulerian.o core/paths/histogram.o core/paths/johnson.o core/paths/random_walk.o core/paths/shortest_paths.o core/paths/simple_paths.o core/paths/unweighted.o core/properties/basic_properties.o core/properties/constraint.o core/properties/convergence_degree.o core/properties/dag.o core/properties/degrees.o core/properties/girth.o core/properties/loops.o core/properties/multiplicity.o core/properties/neighborhood.o core/properties/spectral.o core/properties/trees.o core/properties/triangles.o core/random/random.o core/scg/scg.o core/scg/scg_approximate_methods.o core/scg/scg_exact_scg.o core/scg/scg_kmeans.o core/scg/scg_optimal_method.o core/scg/scg_utils.o core/version.o vendor/cs/cs_add.o vendor/cs/cs_amd.o vendor/cs/cs_chol.o vendor/cs/cs_cholsol.o vendor/cs/cs_compress.o vendor/cs/cs_counts.o vendor/cs/cs_cumsum.o vendor/cs/cs_dfs.o vendor/cs/cs_dmperm.o vendor/cs/cs_droptol.o vendor/cs/cs_dropzeros.o vendor/cs/cs_dupl.o vendor/cs/cs_entry.o vendor/cs/cs_ereach.o vendor/cs/cs_etree.o vendor/cs/cs_fkeep.o vendor/cs/cs_gaxpy.o vendor/cs/cs_happly.o vendor/cs/cs_house.o vendor/cs/cs_ipvec.o vendor/cs/cs_leaf.o vendor/cs/cs_load.o vendor/cs/cs_lsolve.o vendor/cs/cs_ltsolve.o vendor/cs/cs_lu.o vendor/cs/cs_lusol.o vendor/cs/cs_malloc.o vendor/cs/cs_maxtrans.o vendor/cs/cs_multiply.o vendor/cs/cs_norm.o vendor/cs/cs_permute.o vendor/cs/cs_pinv.o vendor/cs/cs_post.o vendor/cs/cs_print.o vendor/cs/cs_pvec.o vendor/cs/cs_qr.o vendor/cs/cs_qrsol.o vendor/cs/cs_randperm.o vendor/cs/cs_reach.o vendor/cs/cs_scatter.o vendor/cs/cs_scc.o vendor/cs/cs_schol.o vendor/cs/cs_spsolve.o vendor/cs/cs_sqr.o vendor/cs/cs_symperm.o vendor/cs/cs_tdfs.o vendor/cs/cs_transpose.o vendor/cs/cs_updown.o vendor/cs/cs_usolve.o vendor/cs/cs_util.o vendor/cs/cs_utsolve.o vendor/mini-gmp/mini-gmp.o vendor/plfit/gss.o vendor/plfit/hzeta.o vendor/plfit/kolmogorov.o vendor/plfit/lbfgs.o vendor/plfit/mt.o vendor/plfit/options.o vendor/plfit/platform.o vendor/plfit/plfit.o vendor/plfit/plfit_error.o vendor/plfit/rbinom.o vendor/plfit/sampling.o vendor/arpack/dgetv0.o vendor/arpack/dlaqrb.o vendor/arpack/dmout.o vendor/arpack/dnaitr.o vendor/arpack/dnapps.o vendor/arpack/dnaup2.o vendor/arpack/dnaupd.o vendor/arpack/dnconv.o vendor/arpack/dneigh.o vendor/arpack/dneupd.o vendor/arpack/dngets.o vendor/arpack/dsaitr.o vendor/arpack/dsapps.o vendor/arpack/dsaup2.o vendor/arpack/dsaupd.o vendor/arpack/dsconv.o vendor/arpack/dseigt.o vendor/arpack/dsesrt.o vendor/arpack/dseupd.o vendor/arpack/dsgets.o vendor/arpack/dsortc.o vendor/arpack/dsortr.o vendor/arpack/dstatn.o vendor/arpack/dstats.o vendor/arpack/dstqrb.o vendor/arpack/dvout.o vendor/arpack/ivout.o vendor/arpack/second.o vendor/arpack/wrap.o vendor/simpleraytracer/Color.o vendor/simpleraytracer/Light.o vendor/simpleraytracer/Point.o vendor/simpleraytracer/RIgraphRay.o vendor/simpleraytracer/Ray.o vendor/simpleraytracer/RayTracer.o vendor/simpleraytracer/RayVector.o vendor/simpleraytracer/Shape.o vendor/simpleraytracer/Sphere.o vendor/simpleraytracer/Triangle.o vendor/simpleraytracer/unit_limiter.o vendor/uuid/R.o vendor/uuid/clear.o vendor/uuid/compare.o vendor/uuid/copy.o vendor/uuid/gen_uuid.o vendor/uuid/isnull.o vendor/uuid/pack.o vendor/uuid/parse.o vendor/uuid/unpack.o vendor/uuid/unparse.o rinterface.o rinterface_extra.o rrandom.o lazyeval.o + ar rvs $@ $? + + +clean: + @-rm -f *.o $(LIB) + + From 259da92b460189c46a935e12ed2fa219dd9af053 Mon Sep 17 00:00:00 2001 From: evanbiederstedt Date: Fri, 8 Apr 2022 01:07:06 -0400 Subject: [PATCH 16/20] compiles, clean up for cran --- CHANGELOG.md | 7 +++++++ DESCRIPTION | 2 +- README.md | 2 +- src/Makevars | 2 +- src/Makevars.win | 28 +++++++++++----------------- src/rigraph/Makefile | 1 - src/rigraph/Makefile.win | 3 --- src/rigraph/vendor/uuid/gen_uuid.c | 13 +++++++++---- 8 files changed, 30 insertions(+), 28 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a510020..5ba8c9d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## [1.0.3] - 2022 April 08 +* Install rigraph directly + + +## [1.0.2] - 2022 March 03 +* Fixed Makevars to use `install_name_tool -change`. + ## [1.0.1] - 2021 Dec 03 * Modified the Makevars to use SHLIB_EXT to account for both shared library extensions on Mac OS (either *.so or *dylib) diff --git a/DESCRIPTION b/DESCRIPTION index 9d88fdd..28d4526 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,7 +1,7 @@ Package: leidenAlg Type: Package Title: Implements the Leiden Algorithm via an R Interface -Version: 1.0.1 +Version: 1.0.3 Authors@R: c(person("Peter", "Kharchenko", email = "Peter_Kharchenko@hms.harvard.edu", role = c("aut")), person("Viktor", "Petukhov", email = "viktor.s.petukhov@ya.ru", role = c("aut")), person("V.A.", "Traag", email = "v.a.traag@cwts.leidenuniv.nl", role = c("ctb")), person("Gábor", "Csárdi", email = "sardi.gabor@gmail.com", role = c("ctb")), person("Tamás", "Nepusz", email = "ntamas@gmail.com", role = c("ctb")), person("Minh Van", "Nguyen", email = "nguyenminh2@gmail.com", role = c("ctb")), person("Evan", "Biederstedt", email = "evan.biederstedt@gmail.com", role=c("cre", "aut"))) Description: An R interface to the Leiden algorithm, an iterative community detection algorithm on networks. The algorithm is designed to converge to a partition in which all subsets of all communities are locally optimally assigned, yielding communities guaranteed to be connected. The implementation proves to be fast, scales well, and can be run on graphs of millions of nodes (as long as they can fit in memory). The original implementation was constructed as a python interface "leidenalg" found here: . The algorithm was originally described in Traag, V.A., Waltman, L. & van Eck, N.J. "From Louvain to Leiden: guaranteeing well-connected communities". Sci Rep 9, 5233 (2019) . License: GPL-3 diff --git a/README.md b/README.md index ed68237..417ba35 100644 --- a/README.md +++ b/README.md @@ -77,5 +77,5 @@ If you find `leidenAlg` useful for your publication, please cite: ``` Peter Kharchenko, Viktor Petukhov and Evan Biederstedt (2021). leidenAlg: Implements the Leiden Algorithm via an R Interface. R -package version 1.0.1. https://github.com/kharchenkolab/leidenAlg +package version 1.0.3. https://github.com/kharchenkolab/leidenAlg ``` diff --git a/src/Makevars b/src/Makevars index bdeaefa..3509bba 100644 --- a/src/Makevars +++ b/src/Makevars @@ -4,7 +4,7 @@ PKG_CXXFLAGS= -I"../inst/include" -I"./rigraph/include" -I"./leidenalg/include" $(SHLIB_OPENMP_CXXFLAGS) -PKG_CFLAGS = -std=c11 -std=gnu11 -O3 -pthread -fPIC $(SHLIB_OPENMP_CXXFLAGS) +PKG_CFLAGS = -pthread $(SHLIB_OPENMP_CFLAGS) PKG_CFLAGS += -I"./include" -I"../rigraph/include" LDFLAGS += -lpthread diff --git a/src/Makevars.win b/src/Makevars.win index 435a73c..9814132 100644 --- a/src/Makevars.win +++ b/src/Makevars.win @@ -1,39 +1,33 @@ ## include OpenMP -# detect R_arch -rarch := $(shell echo 'Sys.getenv("R_ARCH")' | ${R_HOME}/bin/R --vanilla --slave) +PKG_CXXFLAGS= -I"../inst/include" -I"./rigraph/include" -I"./leidenalg/include" $(SHLIB_OPENMP_CXXFLAGS) -PKGB_PATH := $(shell echo 'library(igraph); cat(system.file("libs", package="igraph"))' | ${R_HOME}/bin/R --vanilla --slave) +PKG_CFLAGS = -pthread $(SHLIB_OPENMP_CFLAGS) +PKG_CFLAGS += -I"./include" -I"../rigraph/include" +LDFLAGS += -lpthread -## note: the space in '[1] "/i386" ' is important... -## else, '[1] "/x64" ' -ifeq ($(rarch),[1] "/i386" ) - IGRAPH_LIB := $(PKGB_PATH)/i386/igraph.dll -else - IGRAPH_LIB := $(PKGB_PATH)/x64/igraph.dll -endif -PKG_CXXFLAGS=-I"../inst/include" -I"./leidenalg/igraph-R" -I"./leidenalg/include" $(SHLIB_OPENMP_CXXFLAGS) - -PKG_LIBS=-L/usr/lib/ -L"." -lpthread -lstdc++ -lleidenalg -lm $(shell "${R_HOME}/bin${R_ARCH_BIN}/Rscript.exe" -e "Rcpp:::LdFlags()") $(LAPACK_LIBS) $(BLAS_LIBS) $(FLIBS) $(SHLIB_OPENMP_CXXFLAGS) $(IGRAPH_LIB) +PKG_LIBS=-L/usr/lib/ -L"." -lpthread -lstdc++ -lleidenalg -lrigraph -lm `$(R_HOME)/bin/Rscript -e "Rcpp:::LdFlags()"` $(LAPACK_LIBS) $(BLAS_LIBS) $(FLIBS) $(SHLIB_OPENMP_CXXFLAGS) CXX_STD = CXX11 MkInclude = $(R_HOME)/etc${R_ARCH}/Makeconf #.PHONY: all sublibs #OBJECTS = $(.cpp=.o) -SUBDIRS = leidenalg -SUBLIBS = leidenalg.a +SUBDIRS = rigraph leidenalg +SUBLIBS = librigraph.a leidenalg.a + all: $(SHLIB) -$(SHLIB): $(OBJECTS) sublibs + +$(SHLIB): sublibs sublibs: sublibraries sublibraries: @for d in $(SUBDIRS); do \ - (cd $${d} && CFLAGS="$(CFLAGS)" CXXFLAGS="$(CXXFLAGS)" MAKE="$(MAKE) -f \"$(MkInclude)\" -f Makefile.win" $(MAKE) -f "$(MkInclude)" -f Makefile.win lib) || exit 1; \ + (cd $${d} && CFLAGS="$(CFLAGS)" CXXFLAGS="$(CXXFLAGS)" MAKE="$(MAKE) -f \"$(MkInclude)\" -f Makefile" $(MAKE) -f "$(MkInclude)" -f Makefile lib) || exit 1; \ done clean: subclean diff --git a/src/rigraph/Makefile b/src/rigraph/Makefile index b3398a3..3b32566 100644 --- a/src/rigraph/Makefile +++ b/src/rigraph/Makefile @@ -10,7 +10,6 @@ PKG_CXXFLAGS = -O3 -std=c++11 -pthread -fPIC $(SHLIB_OPENMP_CXXFLAGS) PKG_CXXFLAGS += -I"./include" -I"../rigraph/include" LDFLAGS += -lpthread -OBJECTS=core/centrality/betweenness.o core/centrality/centrality_other.o core/centrality/centralization.o core/centrality/closeness.o core/centrality/coreness.o core/centrality/prpack/prpack_base_graph.o core/centrality/prpack/prpack_igraph_graph.o core/centrality/prpack/prpack_preprocessed_ge_graph.o core/centrality/prpack/prpack_preprocessed_gs_graph.o core/centrality/prpack/prpack_preprocessed_scc_graph.o core/centrality/prpack/prpack_preprocessed_schur_graph.o core/centrality/prpack/prpack_result.o core/centrality/prpack/prpack_solver.o core/centrality/prpack/prpack_utils.o core/cliques/cliquer/cliquer.o core/cliques/cliquer/cliquer_graph.o core/cliques/cliquer/reorder.o core/cliques/cliquer_wrapper.o core/cliques/cliques.o core/cliques/glet.o core/cliques/maximal_cliques.o core/community/community_misc.o core/community/edge_betweenness.o core/community/fast_modularity.o core/community/fluid.o core/community/infomap/infomap.o core/community/infomap/infomap_FlowGraph.o core/community/infomap/infomap_Greedy.o core/community/infomap/infomap_Node.o core/community/label_propagation.o core/community/leading_eigenvector.o core/community/leiden.o core/community/louvain.o core/community/modularity.o core/community/optimal_modularity.o core/community/spinglass/NetDataTypes.o core/community/spinglass/NetRoutines.o core/community/spinglass/clustertool.o core/community/spinglass/pottsmodel_2.o core/community/walktrap/walktrap.o core/community/walktrap/walktrap_communities.o core/community/walktrap/walktrap_graph.o core/community/walktrap/walktrap_heap.o core/connectivity/cohesive_blocks.o core/connectivity/components.o core/connectivity/separators.o core/constructors/adjacency.o core/constructors/atlas.o core/constructors/basic_constructors.o core/constructors/de_bruijn.o core/constructors/famous.o core/constructors/full.o core/constructors/kautz.o core/constructors/lcf.o core/constructors/linegraph.o core/constructors/prufer.o core/constructors/regular.o core/core/array.o core/core/buckets.o core/core/cutheap.o core/core/dqueue.o core/core/error.o core/core/estack.o core/core/fixed_vectorlist.o core/core/grid.o core/core/heap.o core/core/indheap.o core/core/interruption.o core/core/marked_queue.o core/core/matrix.o core/core/memory.o core/core/printing.o core/core/progress.o core/core/psumtree.o core/core/set.o core/core/sparsemat.o core/core/spmatrix.o core/core/stack.o core/core/statusbar.o core/core/strvector.o core/core/trie.o core/core/vector.o core/core/vector_ptr.o core/flow/flow.o core/flow/st-cuts.o core/games/barabasi.o core/games/callaway_traits.o core/games/citations.o core/games/correlated.o core/games/degree_sequence.o core/games/degree_sequence_vl/gengraph_box_list.o core/games/degree_sequence_vl/gengraph_degree_sequence.o core/games/degree_sequence_vl/gengraph_graph_molloy_hash.o core/games/degree_sequence_vl/gengraph_graph_molloy_optimized.o core/games/degree_sequence_vl/gengraph_mr-connected.o core/games/degree_sequence_vl/gengraph_powerlaw.o core/games/degree_sequence_vl/gengraph_random.o core/games/dotproduct.o core/games/erdos_renyi.o core/games/establishment.o core/games/forestfire.o core/games/grg.o core/games/growing_random.o core/games/islands.o core/games/k_regular.o core/games/preference.o core/games/recent_degree.o core/games/sbm.o core/games/static_fitness.o core/games/tree.o core/games/watts_strogatz.o core/graph/adjlist.o core/graph/attributes.o core/graph/basic_query.o core/graph/cattributes.o core/graph/iterators.o core/graph/type_indexededgelist.o core/graph/visitors.o core/hrg/hrg.o core/hrg/hrg_types.o core/internal/glpk_support.o core/internal/hacks.o core/internal/lsap.o core/internal/qsort.o core/internal/qsort_r.o core/internal/zeroin.o core/io/dimacs.o core/io/dl-lexer.o core/io/dl-parser.o core/io/dl.o core/io/dot.o core/io/edgelist.o core/io/gml-lexer.o core/io/gml-parser.o core/io/gml-tree.o core/io/gml.o core/io/graphdb.o core/io/graphml.o core/io/leda.o core/io/lgl-lexer.o core/io/lgl-parser.o core/io/lgl.o core/io/ncol-lexer.o core/io/ncol-parser.o core/io/ncol.o core/io/pajek-lexer.o core/io/pajek-parser.o core/io/pajek.o core/isomorphism/bliss.o core/isomorphism/bliss/defs.o core/isomorphism/bliss/graph.o core/isomorphism/bliss/heap.o core/isomorphism/bliss/orbit.o core/isomorphism/bliss/partition.o core/isomorphism/bliss/uintseqhash.o core/isomorphism/bliss/utils.o core/isomorphism/isoclasses.o core/isomorphism/isomorphism_misc.o core/isomorphism/lad.o core/isomorphism/queries.o core/isomorphism/vf2.o core/layout/circular.o core/layout/davidson_harel.o core/layout/drl/DensityGrid.o core/layout/drl/DensityGrid_3d.o core/layout/drl/drl_graph.o core/layout/drl/drl_graph_3d.o core/layout/drl/drl_layout.o core/layout/drl/drl_layout_3d.o core/layout/drl/drl_parse.o core/layout/fruchterman_reingold.o core/layout/gem.o core/layout/graphopt.o core/layout/kamada_kawai.o core/layout/large_graph.o core/layout/layout_bipartite.o core/layout/layout_grid.o core/layout/layout_random.o core/layout/mds.o core/layout/merge_dla.o core/layout/merge_grid.o core/layout/reingold_tilford.o core/layout/sugiyama.o core/linalg/arpack.o core/linalg/blas.o core/linalg/eigen.o core/linalg/lapack.o core/math/bfgs.o core/math/complex.o core/math/utils.o core/misc/bipartite.o core/misc/chordality.o core/misc/cocitation.o core/misc/coloring.o core/misc/conversion.o core/misc/degree_sequence.o core/misc/embedding.o core/misc/feedback_arc_set.o core/misc/graphicality.o core/misc/matching.o core/misc/microscopic_update.o core/misc/mixing.o core/misc/motifs.o core/misc/other.o core/misc/scan.o core/misc/sir.o core/misc/spanning_trees.o core/operators/add_edge.o core/operators/complementer.o core/operators/compose.o core/operators/connect_neighborhood.o core/operators/contract.o core/operators/difference.o core/operators/disjoint_union.o core/operators/intersection.o core/operators/misc_internal.o core/operators/permute.o core/operators/rewire.o core/operators/rewire_edges.o core/operators/simplify.o core/operators/subgraph.o core/operators/union.o core/paths/all_shortest_paths.o core/paths/bellman_ford.o core/paths/dijkstra.o core/paths/distances.o core/paths/eulerian.o core/paths/histogram.o core/paths/johnson.o core/paths/random_walk.o core/paths/shortest_paths.o core/paths/simple_paths.o core/paths/unweighted.o core/properties/basic_properties.o core/properties/constraint.o core/properties/convergence_degree.o core/properties/dag.o core/properties/degrees.o core/properties/girth.o core/properties/loops.o core/properties/multiplicity.o core/properties/neighborhood.o core/properties/spectral.o core/properties/trees.o core/properties/triangles.o core/random/random.o core/scg/scg.o core/scg/scg_approximate_methods.o core/scg/scg_exact_scg.o core/scg/scg_kmeans.o core/scg/scg_optimal_method.o core/scg/scg_utils.o core/version.o vendor/cs/cs_add.o vendor/cs/cs_amd.o vendor/cs/cs_chol.o vendor/cs/cs_cholsol.o vendor/cs/cs_compress.o vendor/cs/cs_counts.o vendor/cs/cs_cumsum.o vendor/cs/cs_dfs.o vendor/cs/cs_dmperm.o vendor/cs/cs_droptol.o vendor/cs/cs_dropzeros.o vendor/cs/cs_dupl.o vendor/cs/cs_entry.o vendor/cs/cs_ereach.o vendor/cs/cs_etree.o vendor/cs/cs_fkeep.o vendor/cs/cs_gaxpy.o vendor/cs/cs_happly.o vendor/cs/cs_house.o vendor/cs/cs_ipvec.o vendor/cs/cs_leaf.o vendor/cs/cs_load.o vendor/cs/cs_lsolve.o vendor/cs/cs_ltsolve.o vendor/cs/cs_lu.o vendor/cs/cs_lusol.o vendor/cs/cs_malloc.o vendor/cs/cs_maxtrans.o vendor/cs/cs_multiply.o vendor/cs/cs_norm.o vendor/cs/cs_permute.o vendor/cs/cs_pinv.o vendor/cs/cs_post.o vendor/cs/cs_print.o vendor/cs/cs_pvec.o vendor/cs/cs_qr.o vendor/cs/cs_qrsol.o vendor/cs/cs_randperm.o vendor/cs/cs_reach.o vendor/cs/cs_scatter.o vendor/cs/cs_scc.o vendor/cs/cs_schol.o vendor/cs/cs_spsolve.o vendor/cs/cs_sqr.o vendor/cs/cs_symperm.o vendor/cs/cs_tdfs.o vendor/cs/cs_transpose.o vendor/cs/cs_updown.o vendor/cs/cs_usolve.o vendor/cs/cs_util.o vendor/cs/cs_utsolve.o vendor/mini-gmp/mini-gmp.o vendor/plfit/gss.o vendor/plfit/hzeta.o vendor/plfit/kolmogorov.o vendor/plfit/lbfgs.o vendor/plfit/mt.o vendor/plfit/options.o vendor/plfit/platform.o vendor/plfit/plfit.o vendor/plfit/plfit_error.o vendor/plfit/rbinom.o vendor/plfit/sampling.o vendor/arpack/dgetv0.o vendor/arpack/dlaqrb.o vendor/arpack/dmout.o vendor/arpack/dnaitr.o vendor/arpack/dnapps.o vendor/arpack/dnaup2.o vendor/arpack/dnaupd.o vendor/arpack/dnconv.o vendor/arpack/dneigh.o vendor/arpack/dneupd.o vendor/arpack/dngets.o vendor/arpack/dsaitr.o vendor/arpack/dsapps.o vendor/arpack/dsaup2.o vendor/arpack/dsaupd.o vendor/arpack/dsconv.o vendor/arpack/dseigt.o vendor/arpack/dsesrt.o vendor/arpack/dseupd.o vendor/arpack/dsgets.o vendor/arpack/dsortc.o vendor/arpack/dsortr.o vendor/arpack/dstatn.o vendor/arpack/dstats.o vendor/arpack/dstqrb.o vendor/arpack/dvout.o vendor/arpack/ivout.o vendor/arpack/second.o vendor/arpack/wrap.o vendor/simpleraytracer/Color.o vendor/simpleraytracer/Light.o vendor/simpleraytracer/Point.o vendor/simpleraytracer/RIgraphRay.o vendor/simpleraytracer/Ray.o vendor/simpleraytracer/RayTracer.o vendor/simpleraytracer/RayVector.o vendor/simpleraytracer/Shape.o vendor/simpleraytracer/Sphere.o vendor/simpleraytracer/Triangle.o vendor/simpleraytracer/unit_limiter.o vendor/uuid/R.o vendor/uuid/clear.o vendor/uuid/compare.o vendor/uuid/copy.o vendor/uuid/gen_uuid.o vendor/uuid/isnull.o vendor/uuid/pack.o vendor/uuid/parse.o vendor/uuid/unpack.o vendor/uuid/unparse.o rinterface.o rinterface_extra.o rrandom.o lazyeval.o LIB=../librigraph.a lib: $(LIB) diff --git a/src/rigraph/Makefile.win b/src/rigraph/Makefile.win index b3398a3..c23f59f 100644 --- a/src/rigraph/Makefile.win +++ b/src/rigraph/Makefile.win @@ -4,14 +4,11 @@ PKG_CFLAGS = -O3 -std=c11 -std=gnu11 -pthread -fPIC $(SHLIB_OPENMP_CXXFLAGS) PKG_CFLAGS += -I"./include" -I"../rigraph/include" LDFLAGS += -lpthread - CXX ?= g++ PKG_CXXFLAGS = -O3 -std=c++11 -pthread -fPIC $(SHLIB_OPENMP_CXXFLAGS) PKG_CXXFLAGS += -I"./include" -I"../rigraph/include" LDFLAGS += -lpthread -OBJECTS=core/centrality/betweenness.o core/centrality/centrality_other.o core/centrality/centralization.o core/centrality/closeness.o core/centrality/coreness.o core/centrality/prpack/prpack_base_graph.o core/centrality/prpack/prpack_igraph_graph.o core/centrality/prpack/prpack_preprocessed_ge_graph.o core/centrality/prpack/prpack_preprocessed_gs_graph.o core/centrality/prpack/prpack_preprocessed_scc_graph.o core/centrality/prpack/prpack_preprocessed_schur_graph.o core/centrality/prpack/prpack_result.o core/centrality/prpack/prpack_solver.o core/centrality/prpack/prpack_utils.o core/cliques/cliquer/cliquer.o core/cliques/cliquer/cliquer_graph.o core/cliques/cliquer/reorder.o core/cliques/cliquer_wrapper.o core/cliques/cliques.o core/cliques/glet.o core/cliques/maximal_cliques.o core/community/community_misc.o core/community/edge_betweenness.o core/community/fast_modularity.o core/community/fluid.o core/community/infomap/infomap.o core/community/infomap/infomap_FlowGraph.o core/community/infomap/infomap_Greedy.o core/community/infomap/infomap_Node.o core/community/label_propagation.o core/community/leading_eigenvector.o core/community/leiden.o core/community/louvain.o core/community/modularity.o core/community/optimal_modularity.o core/community/spinglass/NetDataTypes.o core/community/spinglass/NetRoutines.o core/community/spinglass/clustertool.o core/community/spinglass/pottsmodel_2.o core/community/walktrap/walktrap.o core/community/walktrap/walktrap_communities.o core/community/walktrap/walktrap_graph.o core/community/walktrap/walktrap_heap.o core/connectivity/cohesive_blocks.o core/connectivity/components.o core/connectivity/separators.o core/constructors/adjacency.o core/constructors/atlas.o core/constructors/basic_constructors.o core/constructors/de_bruijn.o core/constructors/famous.o core/constructors/full.o core/constructors/kautz.o core/constructors/lcf.o core/constructors/linegraph.o core/constructors/prufer.o core/constructors/regular.o core/core/array.o core/core/buckets.o core/core/cutheap.o core/core/dqueue.o core/core/error.o core/core/estack.o core/core/fixed_vectorlist.o core/core/grid.o core/core/heap.o core/core/indheap.o core/core/interruption.o core/core/marked_queue.o core/core/matrix.o core/core/memory.o core/core/printing.o core/core/progress.o core/core/psumtree.o core/core/set.o core/core/sparsemat.o core/core/spmatrix.o core/core/stack.o core/core/statusbar.o core/core/strvector.o core/core/trie.o core/core/vector.o core/core/vector_ptr.o core/flow/flow.o core/flow/st-cuts.o core/games/barabasi.o core/games/callaway_traits.o core/games/citations.o core/games/correlated.o core/games/degree_sequence.o core/games/degree_sequence_vl/gengraph_box_list.o core/games/degree_sequence_vl/gengraph_degree_sequence.o core/games/degree_sequence_vl/gengraph_graph_molloy_hash.o core/games/degree_sequence_vl/gengraph_graph_molloy_optimized.o core/games/degree_sequence_vl/gengraph_mr-connected.o core/games/degree_sequence_vl/gengraph_powerlaw.o core/games/degree_sequence_vl/gengraph_random.o core/games/dotproduct.o core/games/erdos_renyi.o core/games/establishment.o core/games/forestfire.o core/games/grg.o core/games/growing_random.o core/games/islands.o core/games/k_regular.o core/games/preference.o core/games/recent_degree.o core/games/sbm.o core/games/static_fitness.o core/games/tree.o core/games/watts_strogatz.o core/graph/adjlist.o core/graph/attributes.o core/graph/basic_query.o core/graph/cattributes.o core/graph/iterators.o core/graph/type_indexededgelist.o core/graph/visitors.o core/hrg/hrg.o core/hrg/hrg_types.o core/internal/glpk_support.o core/internal/hacks.o core/internal/lsap.o core/internal/qsort.o core/internal/qsort_r.o core/internal/zeroin.o core/io/dimacs.o core/io/dl-lexer.o core/io/dl-parser.o core/io/dl.o core/io/dot.o core/io/edgelist.o core/io/gml-lexer.o core/io/gml-parser.o core/io/gml-tree.o core/io/gml.o core/io/graphdb.o core/io/graphml.o core/io/leda.o core/io/lgl-lexer.o core/io/lgl-parser.o core/io/lgl.o core/io/ncol-lexer.o core/io/ncol-parser.o core/io/ncol.o core/io/pajek-lexer.o core/io/pajek-parser.o core/io/pajek.o core/isomorphism/bliss.o core/isomorphism/bliss/defs.o core/isomorphism/bliss/graph.o core/isomorphism/bliss/heap.o core/isomorphism/bliss/orbit.o core/isomorphism/bliss/partition.o core/isomorphism/bliss/uintseqhash.o core/isomorphism/bliss/utils.o core/isomorphism/isoclasses.o core/isomorphism/isomorphism_misc.o core/isomorphism/lad.o core/isomorphism/queries.o core/isomorphism/vf2.o core/layout/circular.o core/layout/davidson_harel.o core/layout/drl/DensityGrid.o core/layout/drl/DensityGrid_3d.o core/layout/drl/drl_graph.o core/layout/drl/drl_graph_3d.o core/layout/drl/drl_layout.o core/layout/drl/drl_layout_3d.o core/layout/drl/drl_parse.o core/layout/fruchterman_reingold.o core/layout/gem.o core/layout/graphopt.o core/layout/kamada_kawai.o core/layout/large_graph.o core/layout/layout_bipartite.o core/layout/layout_grid.o core/layout/layout_random.o core/layout/mds.o core/layout/merge_dla.o core/layout/merge_grid.o core/layout/reingold_tilford.o core/layout/sugiyama.o core/linalg/arpack.o core/linalg/blas.o core/linalg/eigen.o core/linalg/lapack.o core/math/bfgs.o core/math/complex.o core/math/utils.o core/misc/bipartite.o core/misc/chordality.o core/misc/cocitation.o core/misc/coloring.o core/misc/conversion.o core/misc/degree_sequence.o core/misc/embedding.o core/misc/feedback_arc_set.o core/misc/graphicality.o core/misc/matching.o core/misc/microscopic_update.o core/misc/mixing.o core/misc/motifs.o core/misc/other.o core/misc/scan.o core/misc/sir.o core/misc/spanning_trees.o core/operators/add_edge.o core/operators/complementer.o core/operators/compose.o core/operators/connect_neighborhood.o core/operators/contract.o core/operators/difference.o core/operators/disjoint_union.o core/operators/intersection.o core/operators/misc_internal.o core/operators/permute.o core/operators/rewire.o core/operators/rewire_edges.o core/operators/simplify.o core/operators/subgraph.o core/operators/union.o core/paths/all_shortest_paths.o core/paths/bellman_ford.o core/paths/dijkstra.o core/paths/distances.o core/paths/eulerian.o core/paths/histogram.o core/paths/johnson.o core/paths/random_walk.o core/paths/shortest_paths.o core/paths/simple_paths.o core/paths/unweighted.o core/properties/basic_properties.o core/properties/constraint.o core/properties/convergence_degree.o core/properties/dag.o core/properties/degrees.o core/properties/girth.o core/properties/loops.o core/properties/multiplicity.o core/properties/neighborhood.o core/properties/spectral.o core/properties/trees.o core/properties/triangles.o core/random/random.o core/scg/scg.o core/scg/scg_approximate_methods.o core/scg/scg_exact_scg.o core/scg/scg_kmeans.o core/scg/scg_optimal_method.o core/scg/scg_utils.o core/version.o vendor/cs/cs_add.o vendor/cs/cs_amd.o vendor/cs/cs_chol.o vendor/cs/cs_cholsol.o vendor/cs/cs_compress.o vendor/cs/cs_counts.o vendor/cs/cs_cumsum.o vendor/cs/cs_dfs.o vendor/cs/cs_dmperm.o vendor/cs/cs_droptol.o vendor/cs/cs_dropzeros.o vendor/cs/cs_dupl.o vendor/cs/cs_entry.o vendor/cs/cs_ereach.o vendor/cs/cs_etree.o vendor/cs/cs_fkeep.o vendor/cs/cs_gaxpy.o vendor/cs/cs_happly.o vendor/cs/cs_house.o vendor/cs/cs_ipvec.o vendor/cs/cs_leaf.o vendor/cs/cs_load.o vendor/cs/cs_lsolve.o vendor/cs/cs_ltsolve.o vendor/cs/cs_lu.o vendor/cs/cs_lusol.o vendor/cs/cs_malloc.o vendor/cs/cs_maxtrans.o vendor/cs/cs_multiply.o vendor/cs/cs_norm.o vendor/cs/cs_permute.o vendor/cs/cs_pinv.o vendor/cs/cs_post.o vendor/cs/cs_print.o vendor/cs/cs_pvec.o vendor/cs/cs_qr.o vendor/cs/cs_qrsol.o vendor/cs/cs_randperm.o vendor/cs/cs_reach.o vendor/cs/cs_scatter.o vendor/cs/cs_scc.o vendor/cs/cs_schol.o vendor/cs/cs_spsolve.o vendor/cs/cs_sqr.o vendor/cs/cs_symperm.o vendor/cs/cs_tdfs.o vendor/cs/cs_transpose.o vendor/cs/cs_updown.o vendor/cs/cs_usolve.o vendor/cs/cs_util.o vendor/cs/cs_utsolve.o vendor/mini-gmp/mini-gmp.o vendor/plfit/gss.o vendor/plfit/hzeta.o vendor/plfit/kolmogorov.o vendor/plfit/lbfgs.o vendor/plfit/mt.o vendor/plfit/options.o vendor/plfit/platform.o vendor/plfit/plfit.o vendor/plfit/plfit_error.o vendor/plfit/rbinom.o vendor/plfit/sampling.o vendor/arpack/dgetv0.o vendor/arpack/dlaqrb.o vendor/arpack/dmout.o vendor/arpack/dnaitr.o vendor/arpack/dnapps.o vendor/arpack/dnaup2.o vendor/arpack/dnaupd.o vendor/arpack/dnconv.o vendor/arpack/dneigh.o vendor/arpack/dneupd.o vendor/arpack/dngets.o vendor/arpack/dsaitr.o vendor/arpack/dsapps.o vendor/arpack/dsaup2.o vendor/arpack/dsaupd.o vendor/arpack/dsconv.o vendor/arpack/dseigt.o vendor/arpack/dsesrt.o vendor/arpack/dseupd.o vendor/arpack/dsgets.o vendor/arpack/dsortc.o vendor/arpack/dsortr.o vendor/arpack/dstatn.o vendor/arpack/dstats.o vendor/arpack/dstqrb.o vendor/arpack/dvout.o vendor/arpack/ivout.o vendor/arpack/second.o vendor/arpack/wrap.o vendor/simpleraytracer/Color.o vendor/simpleraytracer/Light.o vendor/simpleraytracer/Point.o vendor/simpleraytracer/RIgraphRay.o vendor/simpleraytracer/Ray.o vendor/simpleraytracer/RayTracer.o vendor/simpleraytracer/RayVector.o vendor/simpleraytracer/Shape.o vendor/simpleraytracer/Sphere.o vendor/simpleraytracer/Triangle.o vendor/simpleraytracer/unit_limiter.o vendor/uuid/R.o vendor/uuid/clear.o vendor/uuid/compare.o vendor/uuid/copy.o vendor/uuid/gen_uuid.o vendor/uuid/isnull.o vendor/uuid/pack.o vendor/uuid/parse.o vendor/uuid/unpack.o vendor/uuid/unparse.o rinterface.o rinterface_extra.o rrandom.o lazyeval.o - LIB=../librigraph.a lib: $(LIB) diff --git a/src/rigraph/vendor/uuid/gen_uuid.c b/src/rigraph/vendor/uuid/gen_uuid.c index 4bc6143..d549706 100644 --- a/src/rigraph/vendor/uuid/gen_uuid.c +++ b/src/rigraph/vendor/uuid/gen_uuid.c @@ -46,9 +46,9 @@ #define UUID MYUUID #endif #include -#ifdef HAVE_UNISTD_H + #include -#endif + #ifdef HAVE_STDLIB_H #include #endif @@ -92,11 +92,14 @@ #include "uuidP.h" #include "uuidd.h" -#ifdef USING_R +#include + +#include + #include "igraph_random.h" #define srand(x) ; #define rand() RNG_INTEGER(0, RAND_MAX) -#endif + #ifdef HAVE_TLS #define THREAD_LOCAL static __thread @@ -314,6 +317,8 @@ static void random_get_bytes(void *buf, size_t nbytes) /* Assume that the gettimeofday() has microsecond granularity */ #define MAX_ADJUSTMENT 10 + +#include /* * Get clock from global sequence clock counter. * From 26288423be07e2175878de7276450ce5ffafd8d9 Mon Sep 17 00:00:00 2001 From: evanbiederstedt Date: Fri, 8 Apr 2022 01:21:58 -0400 Subject: [PATCH 17/20] Update CHANGELOG.md --- CHANGELOG.md | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5ba8c9d..e9ace83 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,8 @@ # Changelog -## [1.0.3] - 2022 April 08 -* Install rigraph directly +## [1.0.3] - 2022 April 08 +* Install rigraph directly in `/src`. Remove previous linking to `igraph` R package in Makevars. ## [1.0.2] - 2022 March 03 * Fixed Makevars to use `install_name_tool -change`. @@ -13,13 +13,9 @@ ## [1.0.0] - 2021-11-19 * `leidenAlg` has gone through no major revisions in over a year. In order to avoid any confusion, this should be released with a major version. - ## [0.1.1] - 2021-03-02 - * Fixed issue with unweighted graph. In this case, we set all edge weights to 1. - ## [0.1.0] - 2020-11-11 - * Version published on CRAN: https://cran.r-project.org/web/packages/leidenAlg/index.html -* Tagged version on github released on 2 Jan 2021: https://github.com/kharchenkolab/leidenAlg/releases/tag/0.1.0 \ No newline at end of file +* Tagged version on github released on 2 Jan 2021: https://github.com/kharchenkolab/leidenAlg/releases/tag/0.1.0 From 065a4e640b73a59976b16eb974fd020b9e606edc Mon Sep 17 00:00:00 2001 From: evanbiederstedt Date: Fri, 8 Apr 2022 01:25:30 -0400 Subject: [PATCH 18/20] remove $(SHLIB_OPENMP_CFLAGS) --- src/Makevars | 2 +- src/Makevars.win | 2 +- src/rigraph/Makefile | 2 +- src/rigraph/Makefile.win | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Makevars b/src/Makevars index 3509bba..3213dba 100644 --- a/src/Makevars +++ b/src/Makevars @@ -4,7 +4,7 @@ PKG_CXXFLAGS= -I"../inst/include" -I"./rigraph/include" -I"./leidenalg/include" $(SHLIB_OPENMP_CXXFLAGS) -PKG_CFLAGS = -pthread $(SHLIB_OPENMP_CFLAGS) +PKG_CFLAGS = -pthread PKG_CFLAGS += -I"./include" -I"../rigraph/include" LDFLAGS += -lpthread diff --git a/src/Makevars.win b/src/Makevars.win index 9814132..056e1e4 100644 --- a/src/Makevars.win +++ b/src/Makevars.win @@ -4,7 +4,7 @@ PKG_CXXFLAGS= -I"../inst/include" -I"./rigraph/include" -I"./leidenalg/include" $(SHLIB_OPENMP_CXXFLAGS) -PKG_CFLAGS = -pthread $(SHLIB_OPENMP_CFLAGS) +PKG_CFLAGS = -pthread PKG_CFLAGS += -I"./include" -I"../rigraph/include" LDFLAGS += -lpthread diff --git a/src/rigraph/Makefile b/src/rigraph/Makefile index 3b32566..62664fd 100644 --- a/src/rigraph/Makefile +++ b/src/rigraph/Makefile @@ -1,6 +1,6 @@ C ?= gcc CFLAGS = -O3 -std=c11 -std=gnu11 -PKG_CFLAGS = -O3 -std=c11 -std=gnu11 -pthread -fPIC $(SHLIB_OPENMP_CXXFLAGS) +PKG_CFLAGS = -O3 -std=c11 -std=gnu11 -pthread -fPIC PKG_CFLAGS += -I"./include" -I"../rigraph/include" LDFLAGS += -lpthread diff --git a/src/rigraph/Makefile.win b/src/rigraph/Makefile.win index c23f59f..a282b37 100644 --- a/src/rigraph/Makefile.win +++ b/src/rigraph/Makefile.win @@ -1,6 +1,6 @@ C ?= gcc CFLAGS = -O3 -std=c11 -std=gnu11 -PKG_CFLAGS = -O3 -std=c11 -std=gnu11 -pthread -fPIC $(SHLIB_OPENMP_CXXFLAGS) +PKG_CFLAGS = -O3 -std=c11 -std=gnu11 -pthread -fPIC PKG_CFLAGS += -I"./include" -I"../rigraph/include" LDFLAGS += -lpthread From d02be8420fd281277a5c09ca2abedb8040031d45 Mon Sep 17 00:00:00 2001 From: evanbiederstedt Date: Sun, 10 Apr 2022 20:32:06 -0400 Subject: [PATCH 19/20] readme update --- README.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/README.md b/README.md index 417ba35..de92ffe 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,13 @@ Implements the Leiden algorithm via an R interface + +#### Note: cluster_leiden() now in igraph + +Since [October 2020](https://github.com/igraph/rigraph/pull/399), the R package [igraph](https://cran.r-project.org/web/packages/igraph/) contains the function `cluster_leiden()` implemented by Vincent Traag ([@vtraag](https://github.com/vtraag)). The usage of this function is detailed in the igraph documentation [here](https://igraph.org/r/html/1.2.7/cluster_leiden.html). We recommend users use this function. + +There is still no R package which entirely encompasses all of the functionality of the original Python/C++ implementation here from https://github.com/vtraag/leidenalg. We hope interested developers could use this package as a starting point for creating an R package which mirrors the full available functionality of the Python package. See [here](https://github.com/vtraag/leidenalg#usage) for details. + ## Summary The Leiden algorithm is an iterative community detection algorithm on networks---the algorithm is designed to converge to a partition in which all subsets of all communities are locally optimally assigned, yielding communities guaranteed to be connected. From 2aa305105cd43e156035473d044986898c275cfd Mon Sep 17 00:00:00 2001 From: evanbiederstedt Date: Sun, 10 Apr 2022 23:59:48 -0400 Subject: [PATCH 20/20] update README, CRAN URL --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index de92ffe..62d6b08 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ Implements the Leiden algorithm via an R interface #### Note: cluster_leiden() now in igraph -Since [October 2020](https://github.com/igraph/rigraph/pull/399), the R package [igraph](https://cran.r-project.org/web/packages/igraph/) contains the function `cluster_leiden()` implemented by Vincent Traag ([@vtraag](https://github.com/vtraag)). The usage of this function is detailed in the igraph documentation [here](https://igraph.org/r/html/1.2.7/cluster_leiden.html). We recommend users use this function. +Since [October 2020](https://github.com/igraph/rigraph/pull/399), the R package [igraph](https://cran.r-project.org/package=igraph) contains the function `cluster_leiden()` implemented by Vincent Traag ([@vtraag](https://github.com/vtraag)). The usage of this function is detailed in the igraph documentation [here](https://igraph.org/r/html/1.2.7/cluster_leiden.html). We recommend users use this function. There is still no R package which entirely encompasses all of the functionality of the original Python/C++ implementation here from https://github.com/vtraag/leidenalg. We hope interested developers could use this package as a starting point for creating an R package which mirrors the full available functionality of the Python package. See [here](https://github.com/vtraag/leidenalg#usage) for details.